From a2a885ed6083aaa39da31f80da2dadecc60f0a73 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Fri, 24 Jul 2015 18:27:42 -0500 Subject: add experimental kernel for solidrun devices, to check kodi audio --- .../patches/3.14.36/0001-solidrun-openelec.patch | 429704 ++++++++++++++++++ 1 file changed, 429704 insertions(+) create mode 100644 target/arm/solidrun-imx6/patches/3.14.36/0001-solidrun-openelec.patch (limited to 'target/arm/solidrun-imx6') diff --git a/target/arm/solidrun-imx6/patches/3.14.36/0001-solidrun-openelec.patch b/target/arm/solidrun-imx6/patches/3.14.36/0001-solidrun-openelec.patch new file mode 100644 index 000000000..ef2c8ed0c --- /dev/null +++ b/target/arm/solidrun-imx6/patches/3.14.36/0001-solidrun-openelec.patch @@ -0,0 +1,429704 @@ +diff -Nur linux-3.14.36/arch/arm/boot/dts/clcd-panels.dtsi linux-openelec/arch/arm/boot/dts/clcd-panels.dtsi +--- linux-3.14.36/arch/arm/boot/dts/clcd-panels.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/clcd-panels.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,52 @@ ++/* ++ * ARM Ltd. Versatile Express ++ * ++ */ ++ ++/ { ++ panels { ++ panel@0 { ++ compatible = "panel"; ++ mode = "VGA"; ++ refresh = <60>; ++ xres = <640>; ++ yres = <480>; ++ pixclock = <39721>; ++ left_margin = <40>; ++ right_margin = <24>; ++ upper_margin = <32>; ++ lower_margin = <11>; ++ hsync_len = <96>; ++ vsync_len = <2>; ++ sync = <0>; ++ vmode = "FB_VMODE_NONINTERLACED"; ++ ++ tim2 = "TIM2_BCD", "TIM2_IPC"; ++ cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)"; ++ caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888"; ++ bpp = <16>; ++ }; ++ ++ panel@1 { ++ compatible = "panel"; ++ mode = "XVGA"; ++ refresh = <60>; ++ xres = <1024>; ++ yres = <768>; ++ pixclock = <15748>; ++ left_margin = <152>; ++ right_margin = <48>; ++ upper_margin = <23>; ++ lower_margin = <3>; ++ hsync_len = <104>; ++ vsync_len = <4>; ++ sync = <0>; ++ vmode = "FB_VMODE_NONINTERLACED"; ++ ++ tim2 = "TIM2_BCD", "TIM2_IPC"; ++ cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)"; ++ caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888"; ++ bpp = <16>; ++ }; ++ }; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/efm32gg-dk3750.dts linux-openelec/arch/arm/boot/dts/efm32gg-dk3750.dts +--- linux-3.14.36/arch/arm/boot/dts/efm32gg-dk3750.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/efm32gg-dk3750.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -26,7 +26,7 @@ + }; + + i2c@4000a000 { +- location = <3>; ++ efm32,location = <3>; + status = "ok"; + + temp@48 { +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx23.dtsi linux-openelec/arch/arm/boot/dts/imx23.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx23.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx23.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -363,7 +363,8 @@ + compatible = "fsl,imx23-lcdif"; + reg = <0x80030000 2000>; + interrupts = <46 45>; +- clocks = <&clks 38>; ++ clocks = <&clks 38>, <&clks 38>; ++ clock-names = "pix", "axi"; + status = "disabled"; + }; + +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx25.dtsi linux-openelec/arch/arm/boot/dts/imx25.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx25.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx25.dtsi 2015-07-24 18:03:29.476842002 -0500 +@@ -13,6 +13,7 @@ + + / { + aliases { ++ ethernet0 = &fec; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; +@@ -56,6 +57,7 @@ + + osc { + compatible = "fsl,imx-osc", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx25.dtsi.orig linux-openelec/arch/arm/boot/dts/imx25.dtsi.orig +--- linux-3.14.36/arch/arm/boot/dts/imx25.dtsi.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx25.dtsi.orig 2015-07-24 18:03:29.376842002 -0500 +@@ -0,0 +1,543 @@ ++/* ++ * Copyright 2012 Sascha Hauer, Pengutronix ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include "skeleton.dtsi" ++ ++/ { ++ aliases { ++ ethernet0 = &fec; ++ gpio0 = &gpio1; ++ gpio1 = &gpio2; ++ gpio2 = &gpio3; ++ gpio3 = &gpio4; ++ i2c0 = &i2c1; ++ i2c1 = &i2c2; ++ i2c2 = &i2c3; ++ serial0 = &uart1; ++ serial1 = &uart2; ++ serial2 = &uart3; ++ serial3 = &uart4; ++ serial4 = &uart5; ++ spi0 = &spi1; ++ spi1 = &spi2; ++ spi2 = &spi3; ++ usb0 = &usbotg; ++ usb1 = &usbhost1; ++ ethernet0 = &fec; ++ }; ++ ++ cpus { ++ #address-cells = <0>; ++ #size-cells = <0>; ++ ++ cpu { ++ compatible = "arm,arm926ej-s"; ++ device_type = "cpu"; ++ }; ++ }; ++ ++ asic: asic-interrupt-controller@68000000 { ++ compatible = "fsl,imx25-asic", "fsl,avic"; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ reg = <0x68000000 0x8000000>; ++ }; ++ ++ clocks { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ osc { ++ compatible = "fsl,imx-osc", "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&asic>; ++ ranges; ++ ++ aips@43f00000 { /* AIPS1 */ ++ compatible = "fsl,aips-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x43f00000 0x100000>; ++ ranges; ++ ++ i2c1: i2c@43f80000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; ++ reg = <0x43f80000 0x4000>; ++ clocks = <&clks 48>; ++ clock-names = ""; ++ interrupts = <3>; ++ status = "disabled"; ++ }; ++ ++ i2c3: i2c@43f84000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; ++ reg = <0x43f84000 0x4000>; ++ clocks = <&clks 48>; ++ clock-names = ""; ++ interrupts = <10>; ++ status = "disabled"; ++ }; ++ ++ can1: can@43f88000 { ++ compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan"; ++ reg = <0x43f88000 0x4000>; ++ interrupts = <43>; ++ clocks = <&clks 75>, <&clks 75>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ can2: can@43f8c000 { ++ compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan"; ++ reg = <0x43f8c000 0x4000>; ++ interrupts = <44>; ++ clocks = <&clks 76>, <&clks 76>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ uart1: serial@43f90000 { ++ compatible = "fsl,imx25-uart", "fsl,imx21-uart"; ++ reg = <0x43f90000 0x4000>; ++ interrupts = <45>; ++ clocks = <&clks 120>, <&clks 57>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ uart2: serial@43f94000 { ++ compatible = "fsl,imx25-uart", "fsl,imx21-uart"; ++ reg = <0x43f94000 0x4000>; ++ interrupts = <32>; ++ clocks = <&clks 121>, <&clks 57>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ i2c2: i2c@43f98000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; ++ reg = <0x43f98000 0x4000>; ++ clocks = <&clks 48>; ++ clock-names = ""; ++ interrupts = <4>; ++ status = "disabled"; ++ }; ++ ++ owire@43f9c000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x43f9c000 0x4000>; ++ clocks = <&clks 51>; ++ clock-names = ""; ++ interrupts = <2>; ++ status = "disabled"; ++ }; ++ ++ spi1: cspi@43fa4000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; ++ reg = <0x43fa4000 0x4000>; ++ clocks = <&clks 78>, <&clks 78>; ++ clock-names = "ipg", "per"; ++ interrupts = <14>; ++ status = "disabled"; ++ }; ++ ++ kpp@43fa8000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x43fa8000 0x4000>; ++ clocks = <&clks 102>; ++ clock-names = ""; ++ interrupts = <24>; ++ status = "disabled"; ++ }; ++ ++ iomuxc@43fac000{ ++ compatible = "fsl,imx25-iomuxc"; ++ reg = <0x43fac000 0x4000>; ++ }; ++ ++ audmux@43fb0000 { ++ compatible = "fsl,imx25-audmux", "fsl,imx31-audmux"; ++ reg = <0x43fb0000 0x4000>; ++ status = "disabled"; ++ }; ++ }; ++ ++ spba@50000000 { ++ compatible = "fsl,spba-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x50000000 0x40000>; ++ ranges; ++ ++ spi3: cspi@50004000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; ++ reg = <0x50004000 0x4000>; ++ interrupts = <0>; ++ clocks = <&clks 80>, <&clks 80>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ uart4: serial@50008000 { ++ compatible = "fsl,imx25-uart", "fsl,imx21-uart"; ++ reg = <0x50008000 0x4000>; ++ interrupts = <5>; ++ clocks = <&clks 123>, <&clks 57>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ uart3: serial@5000c000 { ++ compatible = "fsl,imx25-uart", "fsl,imx21-uart"; ++ reg = <0x5000c000 0x4000>; ++ interrupts = <18>; ++ clocks = <&clks 122>, <&clks 57>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ spi2: cspi@50010000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; ++ reg = <0x50010000 0x4000>; ++ clocks = <&clks 79>, <&clks 79>; ++ clock-names = "ipg", "per"; ++ interrupts = <13>; ++ status = "disabled"; ++ }; ++ ++ ssi2: ssi@50014000 { ++ compatible = "fsl,imx25-ssi", "fsl,imx21-ssi"; ++ reg = <0x50014000 0x4000>; ++ interrupts = <11>; ++ status = "disabled"; ++ }; ++ ++ esai@50018000 { ++ reg = <0x50018000 0x4000>; ++ interrupts = <7>; ++ }; ++ ++ uart5: serial@5002c000 { ++ compatible = "fsl,imx25-uart", "fsl,imx21-uart"; ++ reg = <0x5002c000 0x4000>; ++ interrupts = <40>; ++ clocks = <&clks 124>, <&clks 57>; ++ clock-names = "ipg", "per"; ++ status = "disabled"; ++ }; ++ ++ tsc: tsc@50030000 { ++ compatible = "fsl,imx25-adc", "fsl,imx21-tsc"; ++ reg = <0x50030000 0x4000>; ++ interrupts = <46>; ++ clocks = <&clks 119>; ++ clock-names = "ipg"; ++ status = "disabled"; ++ }; ++ ++ ssi1: ssi@50034000 { ++ compatible = "fsl,imx25-ssi", "fsl,imx21-ssi"; ++ reg = <0x50034000 0x4000>; ++ interrupts = <12>; ++ status = "disabled"; ++ }; ++ ++ fec: ethernet@50038000 { ++ compatible = "fsl,imx25-fec"; ++ reg = <0x50038000 0x4000>; ++ interrupts = <57>; ++ clocks = <&clks 88>, <&clks 65>; ++ clock-names = "ipg", "ahb"; ++ status = "disabled"; ++ }; ++ }; ++ ++ aips@53f00000 { /* AIPS2 */ ++ compatible = "fsl,aips-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x53f00000 0x100000>; ++ ranges; ++ ++ clks: ccm@53f80000 { ++ compatible = "fsl,imx25-ccm"; ++ reg = <0x53f80000 0x4000>; ++ interrupts = <31>; ++ #clock-cells = <1>; ++ }; ++ ++ gpt4: timer@53f84000 { ++ compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; ++ reg = <0x53f84000 0x4000>; ++ clocks = <&clks 9>, <&clks 45>; ++ clock-names = "ipg", "per"; ++ interrupts = <1>; ++ }; ++ ++ gpt3: timer@53f88000 { ++ compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; ++ reg = <0x53f88000 0x4000>; ++ clocks = <&clks 9>, <&clks 47>; ++ clock-names = "ipg", "per"; ++ interrupts = <29>; ++ }; ++ ++ gpt2: timer@53f8c000 { ++ compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; ++ reg = <0x53f8c000 0x4000>; ++ clocks = <&clks 9>, <&clks 47>; ++ clock-names = "ipg", "per"; ++ interrupts = <53>; ++ }; ++ ++ gpt1: timer@53f90000 { ++ compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; ++ reg = <0x53f90000 0x4000>; ++ clocks = <&clks 9>, <&clks 47>; ++ clock-names = "ipg", "per"; ++ interrupts = <54>; ++ }; ++ ++ epit1: timer@53f94000 { ++ compatible = "fsl,imx25-epit"; ++ reg = <0x53f94000 0x4000>; ++ interrupts = <28>; ++ }; ++ ++ epit2: timer@53f98000 { ++ compatible = "fsl,imx25-epit"; ++ reg = <0x53f98000 0x4000>; ++ interrupts = <27>; ++ }; ++ ++ gpio4: gpio@53f9c000 { ++ compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; ++ reg = <0x53f9c000 0x4000>; ++ interrupts = <23>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ pwm2: pwm@53fa0000 { ++ compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; ++ #pwm-cells = <2>; ++ reg = <0x53fa0000 0x4000>; ++ clocks = <&clks 106>, <&clks 36>; ++ clock-names = "ipg", "per"; ++ interrupts = <36>; ++ }; ++ ++ gpio3: gpio@53fa4000 { ++ compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; ++ reg = <0x53fa4000 0x4000>; ++ interrupts = <16>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ pwm3: pwm@53fa8000 { ++ compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; ++ #pwm-cells = <2>; ++ reg = <0x53fa8000 0x4000>; ++ clocks = <&clks 107>, <&clks 36>; ++ clock-names = "ipg", "per"; ++ interrupts = <41>; ++ }; ++ ++ esdhc1: esdhc@53fb4000 { ++ compatible = "fsl,imx25-esdhc"; ++ reg = <0x53fb4000 0x4000>; ++ interrupts = <9>; ++ clocks = <&clks 86>, <&clks 63>, <&clks 45>; ++ clock-names = "ipg", "ahb", "per"; ++ status = "disabled"; ++ }; ++ ++ esdhc2: esdhc@53fb8000 { ++ compatible = "fsl,imx25-esdhc"; ++ reg = <0x53fb8000 0x4000>; ++ interrupts = <8>; ++ clocks = <&clks 87>, <&clks 64>, <&clks 46>; ++ clock-names = "ipg", "ahb", "per"; ++ status = "disabled"; ++ }; ++ ++ lcdc: lcdc@53fbc000 { ++ compatible = "fsl,imx25-fb", "fsl,imx21-fb"; ++ reg = <0x53fbc000 0x4000>; ++ interrupts = <39>; ++ clocks = <&clks 103>, <&clks 66>, <&clks 49>; ++ clock-names = "ipg", "ahb", "per"; ++ status = "disabled"; ++ }; ++ ++ slcdc@53fc0000 { ++ reg = <0x53fc0000 0x4000>; ++ interrupts = <38>; ++ status = "disabled"; ++ }; ++ ++ pwm4: pwm@53fc8000 { ++ compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; ++ reg = <0x53fc8000 0x4000>; ++ clocks = <&clks 108>, <&clks 36>; ++ clock-names = "ipg", "per"; ++ interrupts = <42>; ++ }; ++ ++ gpio1: gpio@53fcc000 { ++ compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; ++ reg = <0x53fcc000 0x4000>; ++ interrupts = <52>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gpio2: gpio@53fd0000 { ++ compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; ++ reg = <0x53fd0000 0x4000>; ++ interrupts = <51>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ sdma@53fd4000 { ++ compatible = "fsl,imx25-sdma", "fsl,imx35-sdma"; ++ reg = <0x53fd4000 0x4000>; ++ clocks = <&clks 112>, <&clks 68>; ++ clock-names = "ipg", "ahb"; ++ #dma-cells = <3>; ++ interrupts = <34>; ++ }; ++ ++ wdog@53fdc000 { ++ compatible = "fsl,imx25-wdt", "fsl,imx21-wdt"; ++ reg = <0x53fdc000 0x4000>; ++ clocks = <&clks 126>; ++ clock-names = ""; ++ interrupts = <55>; ++ }; ++ ++ pwm1: pwm@53fe0000 { ++ compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; ++ #pwm-cells = <2>; ++ reg = <0x53fe0000 0x4000>; ++ clocks = <&clks 105>, <&clks 36>; ++ clock-names = "ipg", "per"; ++ interrupts = <26>; ++ }; ++ ++ iim: iim@53ff0000 { ++ compatible = "fsl,imx25-iim", "fsl,imx27-iim"; ++ reg = <0x53ff0000 0x4000>; ++ interrupts = <19>; ++ clocks = <&clks 99>; ++ }; ++ ++ usbphy1: usbphy@1 { ++ compatible = "nop-usbphy"; ++ status = "disabled"; ++ }; ++ ++ usbphy2: usbphy@2 { ++ compatible = "nop-usbphy"; ++ status = "disabled"; ++ }; ++ ++ usbotg: usb@53ff4000 { ++ compatible = "fsl,imx25-usb", "fsl,imx27-usb"; ++ reg = <0x53ff4000 0x0200>; ++ interrupts = <37>; ++ clocks = <&clks 9>, <&clks 70>, <&clks 8>; ++ clock-names = "ipg", "ahb", "per"; ++ fsl,usbmisc = <&usbmisc 0>; ++ status = "disabled"; ++ }; ++ ++ usbhost1: usb@53ff4400 { ++ compatible = "fsl,imx25-usb", "fsl,imx27-usb"; ++ reg = <0x53ff4400 0x0200>; ++ interrupts = <35>; ++ clocks = <&clks 9>, <&clks 70>, <&clks 8>; ++ clock-names = "ipg", "ahb", "per"; ++ fsl,usbmisc = <&usbmisc 1>; ++ status = "disabled"; ++ }; ++ ++ usbmisc: usbmisc@53ff4600 { ++ #index-cells = <1>; ++ compatible = "fsl,imx25-usbmisc"; ++ clocks = <&clks 9>, <&clks 70>, <&clks 8>; ++ clock-names = "ipg", "ahb", "per"; ++ reg = <0x53ff4600 0x00f>; ++ status = "disabled"; ++ }; ++ ++ dryice@53ffc000 { ++ compatible = "fsl,imx25-dryice", "fsl,imx25-rtc"; ++ reg = <0x53ffc000 0x4000>; ++ clocks = <&clks 81>; ++ clock-names = "ipg"; ++ interrupts = <25>; ++ }; ++ }; ++ ++ emi@80000000 { ++ compatible = "fsl,emi-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x80000000 0x3b002000>; ++ ranges; ++ ++ nfc: nand@bb000000 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ compatible = "fsl,imx25-nand"; ++ reg = <0xbb000000 0x2000>; ++ clocks = <&clks 50>; ++ clock-names = ""; ++ interrupts = <33>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx25-karo-tx25.dts linux-openelec/arch/arm/boot/dts/imx25-karo-tx25.dts +--- linux-3.14.36/arch/arm/boot/dts/imx25-karo-tx25.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx25-karo-tx25.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -16,6 +16,10 @@ + model = "Ka-Ro TX25"; + compatible = "karo,imx25-tx25", "fsl,imx25"; + ++ chosen { ++ stdout-path = &uart1; ++ }; ++ + memory { + reg = <0x80000000 0x02000000 0x90000000 0x02000000>; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx27-apf27.dts linux-openelec/arch/arm/boot/dts/imx27-apf27.dts +--- linux-3.14.36/arch/arm/boot/dts/imx27-apf27.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx27-apf27.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -29,6 +29,7 @@ + + osc26m { + compatible = "fsl,imx-osc26m", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <0>; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx27.dtsi linux-openelec/arch/arm/boot/dts/imx27.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx27.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx27.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -13,6 +13,7 @@ + + / { + aliases { ++ ethernet0 = &fec; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; +@@ -46,6 +47,7 @@ + + osc26m { + compatible = "fsl,imx-osc26m", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <26000000>; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts linux-openelec/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts +--- linux-3.14.36/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -15,6 +15,10 @@ + model = "Phytec pca100 rapid development kit"; + compatible = "phytec,imx27-pca100-rdk", "phytec,imx27-pca100", "fsl,imx27"; + ++ chosen { ++ stdout-path = &uart1; ++ }; ++ + display: display { + model = "Primeview-PD050VL1"; + native-mode = <&timing0>; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx28.dtsi linux-openelec/arch/arm/boot/dts/imx28.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx28.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx28.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -840,7 +840,8 @@ + compatible = "fsl,imx28-lcdif"; + reg = <0x80030000 0x2000>; + interrupts = <38>; +- clocks = <&clks 55>; ++ clocks = <&clks 55>, <&clks 55>; ++ clock-names = "pix", "axi"; + dmas = <&dma_apbh 13>; + dma-names = "rx"; + status = "disabled"; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx51-babbage.dts linux-openelec/arch/arm/boot/dts/imx51-babbage.dts +--- linux-3.14.36/arch/arm/boot/dts/imx51-babbage.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx51-babbage.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -17,6 +17,10 @@ + model = "Freescale i.MX51 Babbage Board"; + compatible = "fsl,imx51-babbage", "fsl,imx51"; + ++ chosen { ++ stdout-path = &uart1; ++ }; ++ + memory { + reg = <0x90000000 0x20000000>; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx51.dtsi linux-openelec/arch/arm/boot/dts/imx51.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx51.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx51.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -15,6 +15,7 @@ + + / { + aliases { ++ ethernet0 = &fec; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; +@@ -43,21 +44,25 @@ + + ckil { + compatible = "fsl,imx-ckil", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <32768>; + }; + + ckih1 { + compatible = "fsl,imx-ckih1", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <0>; + }; + + ckih2 { + compatible = "fsl,imx-ckih2", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <0>; + }; + + osc { + compatible = "fsl,imx-osc", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx53.dtsi linux-openelec/arch/arm/boot/dts/imx53.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx53.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx53.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -15,6 +15,7 @@ + + / { + aliases { ++ ethernet0 = &fec; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; +@@ -59,21 +60,25 @@ + + ckil { + compatible = "fsl,imx-ckil", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <32768>; + }; + + ckih1 { + compatible = "fsl,imx-ckih1", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <22579200>; + }; + + ckih2 { + compatible = "fsl,imx-ckih2", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <0>; + }; + + osc { + compatible = "fsl,imx-osc", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx53-mba53.dts linux-openelec/arch/arm/boot/dts/imx53-mba53.dts +--- linux-3.14.36/arch/arm/boot/dts/imx53-mba53.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx53-mba53.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -25,6 +25,10 @@ + enable-active-low; + }; + ++ chosen { ++ stdout-path = &uart2; ++ }; ++ + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm2 0 50000>; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts linux-openelec/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,23 @@ ++/* ++ * Copyright 2013 Sascha Hauer ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef __DTS_V1__ ++#define __DTS_V1__ ++/dts-v1/; ++#endif ++ ++#include "imx6dl.dtsi" ++#include "imx6qdl-dfi-fs700-m60.dtsi" ++ ++/ { ++ model = "DFI FS700-M60-6DL i.MX6dl Q7 Board"; ++ compatible = "dfi,fs700-m60-6dl", "dfi,fs700e-m60", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl.dtsi linux-openelec/arch/arm/boot/dts/imx6dl.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6dl.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6dl.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -8,6 +8,7 @@ + * + */ + ++#include + #include "imx6dl-pinfunc.h" + #include "imx6qdl.dtsi" + +@@ -21,6 +22,26 @@ + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2>; ++ operating-points = < ++ /* kHz uV */ ++ 996000 1275000 ++ 792000 1175000 ++ 396000 1075000 ++ >; ++ fsl,soc-operating-points = < ++ /* ARM kHz SOC-PU uV */ ++ 996000 1175000 ++ 792000 1175000 ++ 396000 1175000 ++ >; ++ clock-latency = <61036>; /* two CLK32 periods */ ++ clocks = <&clks 104>, <&clks 6>, <&clks 16>, ++ <&clks 17>, <&clks 170>; ++ clock-names = "arm", "pll2_pfd2_396m", "step", ++ "pll1_sw", "pll1_sys"; ++ arm-supply = <®_arm>; ++ pu-supply = <®_pu>; ++ soc-supply = <®_soc>; + }; + + cpu@1 { +@@ -32,40 +53,124 @@ + }; + + soc { ++ ++ busfreq { /* BUSFREQ */ ++ compatible = "fsl,imx6_busfreq"; ++ clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>, ++ <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>, <&clks 22> , <&clks 8>; ++ clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", ++ "periph_pre", "periph_clk2", "periph_clk2_sel", "osc", "axi_sel", "pll3_pfd1_540m"; ++ interrupts = <0 107 0x04>, <0 112 0x4>; ++ interrupt-names = "irq_busfreq_0", "irq_busfreq_1"; ++ fsl,max_ddr_freq = <400000000>; ++ }; ++ ++ gpu@00130000 { ++ compatible = "fsl,imx6dl-gpu", "fsl,imx6q-gpu"; ++ reg = <0x00130000 0x4000>, <0x00134000 0x4000>, ++ <0x0 0x0>; ++ reg-names = "iobase_3d", "iobase_2d", ++ "phys_baseaddr"; ++ interrupts = <0 9 0x04>, <0 10 0x04>; ++ interrupt-names = "irq_3d", "irq_2d"; ++ clocks = <&clks 143>, <&clks 27>, ++ <&clks 121>, <&clks 122>, ++ <&clks 0>; ++ clock-names = "gpu2d_axi_clk", "gpu3d_axi_clk", ++ "gpu2d_clk", "gpu3d_clk", ++ "gpu3d_shader_clk"; ++ resets = <&src 0>, <&src 3>; ++ reset-names = "gpu3d", "gpu2d"; ++ pu-supply = <®_pu>; ++ }; ++ + ocram: sram@00900000 { + compatible = "mmio-sram"; + reg = <0x00900000 0x20000>; + clocks = <&clks 142>; + }; + ++ hdmi_core: hdmi_core@00120000 { ++ compatible = "fsl,imx6dl-hdmi-core"; ++ reg = <0x00120000 0x9000>; ++ clocks = <&clks 124>, <&clks 123>; ++ clock-names = "hdmi_isfr", "hdmi_iahb"; ++ status = "disabled"; ++ }; ++ ++ hdmi_video: hdmi_video@020e0000 { ++ compatible = "fsl,imx6dl-hdmi-video"; ++ reg = <0x020e0000 0x1000>; ++ reg-names = "hdmi_gpr"; ++ interrupts = <0 115 0x04>; ++ clocks = <&clks 124>, <&clks 123>; ++ clock-names = "hdmi_isfr", "hdmi_iahb"; ++ status = "disabled"; ++ }; ++ ++ hdmi_audio: hdmi_audio@00120000 { ++ compatible = "fsl,imx6dl-hdmi-audio"; ++ clocks = <&clks 124>, <&clks 123>; ++ clock-names = "hdmi_isfr", "hdmi_iahb"; ++ dmas = <&sdma 2 23 0>; ++ dma-names = "tx"; ++ status = "disabled"; ++ }; ++ ++ hdmi_cec: hdmi_cec@00120000 { ++ compatible = "fsl,imx6dl-hdmi-cec"; ++ interrupts = <0 115 0x04>; ++ status = "disabled"; ++ }; ++ + aips1: aips-bus@02000000 { ++ vpu@02040000 { ++ iramsize = <0>; ++ status = "okay"; ++ }; ++ + iomuxc: iomuxc@020e0000 { + compatible = "fsl,imx6dl-iomuxc"; + }; + + pxp: pxp@020f0000 { ++ compatible = "fsl,imx6dl-pxp-dma"; + reg = <0x020f0000 0x4000>; +- interrupts = <0 98 0x04>; ++ interrupts = <0 98 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 133>; ++ clock-names = "pxp-axi"; ++ status = "disabled"; + }; + + epdc: epdc@020f4000 { + reg = <0x020f4000 0x4000>; +- interrupts = <0 97 0x04>; ++ interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>; + }; + + lcdif: lcdif@020f8000 { + reg = <0x020f8000 0x4000>; +- interrupts = <0 39 0x04>; ++ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + aips2: aips-bus@02100000 { ++ mipi_dsi: mipi@021e0000 { ++ compatible = "fsl,imx6dl-mipi-dsi"; ++ reg = <0x021e0000 0x4000>; ++ interrupts = <0 102 0x04>; ++ gpr = <&gpr>; ++ clocks = <&clks 138>, <&clks 209>; ++ clock-names = "mipi_pllref_clk", "mipi_cfg_clk"; ++ status = "disabled"; ++ }; ++ + i2c4: i2c@021f8000 { + #address-cells = <1>; + #size-cells = <0>; +- compatible = "fsl,imx1-i2c"; ++ compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; + reg = <0x021f8000 0x4000>; +- interrupts = <0 35 0x04>; ++ interrupts = <0 35 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 116>; + status = "disabled"; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-gw51xx.dts linux-openelec/arch/arm/boot/dts/imx6dl-gw51xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-gw51xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-gw51xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6dl.dtsi" ++#include "imx6qdl-gw51xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 DualLite GW51XX"; ++ compatible = "gw,imx6dl-gw51xx", "gw,ventana", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-gw52xx.dts linux-openelec/arch/arm/boot/dts/imx6dl-gw52xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-gw52xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-gw52xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6dl.dtsi" ++#include "imx6qdl-gw52xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 DualLite GW52XX"; ++ compatible = "gw,imx6dl-gw52xx", "gw,ventana", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-gw53xx.dts linux-openelec/arch/arm/boot/dts/imx6dl-gw53xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-gw53xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-gw53xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6dl.dtsi" ++#include "imx6qdl-gw53xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 DualLite GW53XX"; ++ compatible = "gw,imx6dl-gw53xx", "gw,ventana", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-gw54xx.dts linux-openelec/arch/arm/boot/dts/imx6dl-gw54xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-gw54xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-gw54xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6dl.dtsi" ++#include "imx6qdl-gw54xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 DualLite GW54XX"; ++ compatible = "gw,imx6dl-gw54xx", "gw,ventana", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-hummingboard.dts linux-openelec/arch/arm/boot/dts/imx6dl-hummingboard.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-hummingboard.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-hummingboard.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -1,163 +1,13 @@ + /* +- * Copyright (C) 2013,2014 Russell King ++ * Copyright (C) 2014 Rabeeh Khoury (rabeeh@solid-run.com) ++ * Based on work by Russell King + */ + /dts-v1/; + + #include "imx6dl.dtsi" +-#include "imx6qdl-microsom.dtsi" +-#include "imx6qdl-microsom-ar8035.dtsi" ++#include "imx6qdl-hummingboard.dtsi" + + / { +- model = "SolidRun HummingBoard DL/Solo"; +- compatible = "solidrun,hummingboard", "fsl,imx6dl"; +- +- ir_recv: ir-receiver { +- compatible = "gpio-ir-receiver"; +- gpios = <&gpio1 2 1>; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hummingboard_gpio1_2>; +- }; +- +- regulators { +- compatible = "simple-bus"; +- +- reg_3p3v: 3p3v { +- compatible = "regulator-fixed"; +- regulator-name = "3P3V"; +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- regulator-always-on; +- }; +- +- reg_usbh1_vbus: usb-h1-vbus { +- compatible = "regulator-fixed"; +- enable-active-high; +- gpio = <&gpio1 0 0>; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hummingboard_usbh1_vbus>; +- regulator-name = "usb_h1_vbus"; +- regulator-min-microvolt = <5000000>; +- regulator-max-microvolt = <5000000>; +- }; +- +- reg_usbotg_vbus: usb-otg-vbus { +- compatible = "regulator-fixed"; +- enable-active-high; +- gpio = <&gpio3 22 0>; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hummingboard_usbotg_vbus>; +- regulator-name = "usb_otg_vbus"; +- regulator-min-microvolt = <5000000>; +- regulator-max-microvolt = <5000000>; +- }; +- }; +- +- sound-spdif { +- compatible = "fsl,imx-audio-spdif"; +- model = "imx-spdif"; +- /* IMX6 doesn't implement this yet */ +- spdif-controller = <&spdif>; +- spdif-out; +- }; +-}; +- +-&can1 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hummingboard_flexcan1>; +- status = "okay"; +-}; +- +-&i2c1 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hummingboard_i2c1>; +- +- /* +- * Not fitted on Carrier-1 board... yet +- status = "okay"; +- +- rtc: pcf8523@68 { +- compatible = "nxp,pcf8523"; +- reg = <0x68>; +- }; +- */ +-}; +- +-&iomuxc { +- hummingboard { +- pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 { +- fsl,pins = < +- MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000 +- MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000 +- >; +- }; +- +- pinctrl_hummingboard_gpio1_2: hummingboard-gpio1_2 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 +- >; +- }; +- +- pinctrl_hummingboard_i2c1: hummingboard-i2c1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 +- MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_hummingboard_spdif: hummingboard-spdif { +- fsl,pins = ; +- }; +- +- pinctrl_hummingboard_usbh1_vbus: hummingboard-usbh1-vbus { +- fsl,pins = ; +- }; +- +- pinctrl_hummingboard_usbotg_vbus: hummingboard-usbotg-vbus { +- fsl,pins = ; +- }; +- +- pinctrl_hummingboard_usdhc2_aux: hummingboard-usdhc2-aux { +- fsl,pins = < +- MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 +- >; +- }; +- +- pinctrl_hummingboard_usdhc2: hummingboard-usdhc2 { +- fsl,pins = < +- MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 +- MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 +- MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 +- MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 +- MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 +- MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 +- >; +- }; +- }; +-}; +- +-&spdif { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hummingboard_spdif>; +- status = "okay"; +-}; +- +-&usbh1 { +- vbus-supply = <®_usbh1_vbus>; +- status = "okay"; +-}; +- +-&usbotg { +- vbus-supply = <®_usbotg_vbus>; +- status = "okay"; +-}; +- +-&usdhc2 { +- pinctrl-names = "default"; +- pinctrl-0 = < +- &pinctrl_hummingboard_usdhc2_aux +- &pinctrl_hummingboard_usdhc2 +- >; +- vmmc-supply = <®_3p3v>; +- cd-gpios = <&gpio1 4 0>; +- status = "okay"; ++ model = "SolidRun HummingBoard Solo/DualLite"; ++ compatible = "solidrun,hummingboard/dl", "fsl,imx6dl"; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-nitrogen6x.dts linux-openelec/arch/arm/boot/dts/imx6dl-nitrogen6x.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-nitrogen6x.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-nitrogen6x.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,21 @@ ++/* ++ * Copyright 2013 Boundary Devices, Inc. ++ * Copyright 2012 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6dl.dtsi" ++#include "imx6qdl-nitrogen6x.dtsi" ++ ++/ { ++ model = "Freescale i.MX6 DualLite Nitrogen6x Board"; ++ compatible = "fsl,imx6dl-nitrogen6x", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-phytec-pbab01.dts linux-openelec/arch/arm/boot/dts/imx6dl-phytec-pbab01.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-phytec-pbab01.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-phytec-pbab01.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2013 Christian Hemp, Phytec Messtechnik GmbH ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6dl-phytec-pfla02.dtsi" ++#include "imx6qdl-phytec-pbab01.dtsi" ++ ++/ { ++ model = "Phytec phyFLEX-i.MX6 DualLite/Solo Carrier-Board"; ++ compatible = "phytec,imx6dl-pbab01", "phytec,imx6dl-pfla02", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi linux-openelec/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-phytec-pfla02.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,22 @@ ++/* ++ * Copyright 2013 Christian Hemp, Phytec Messtechnik GmbH ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include "imx6dl.dtsi" ++#include "imx6qdl-phytec-pfla02.dtsi" ++ ++/ { ++ model = "Phytec phyFLEX-i.MX6 DualLite/Solo"; ++ compatible = "phytec,imx6dl-pfla02", "fsl,imx6dl"; ++ ++ memory { ++ reg = <0x10000000 0x20000000>; ++ }; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-pinfunc.h linux-openelec/arch/arm/boot/dts/imx6dl-pinfunc.h +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-pinfunc.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-pinfunc.h 2015-05-06 12:05:43.000000000 -0500 +@@ -755,6 +755,7 @@ + #define MX6QDL_PAD_GPIO_5__I2C3_SCL 0x230 0x600 0x878 0x6 0x2 + #define MX6QDL_PAD_GPIO_5__ARM_EVENTI 0x230 0x600 0x000 0x7 0x0 + #define MX6QDL_PAD_GPIO_6__ESAI_TX_CLK 0x234 0x604 0x840 0x0 0x1 ++#define MX6QDL_PAD_GPIO_6__ENET_IRQ 0x234 0x604 0x03c 0x11 0xff000609 + #define MX6QDL_PAD_GPIO_6__I2C3_SDA 0x234 0x604 0x87c 0x2 0x2 + #define MX6QDL_PAD_GPIO_6__GPIO1_IO06 0x234 0x604 0x000 0x5 0x0 + #define MX6QDL_PAD_GPIO_6__SD2_LCTL 0x234 0x604 0x000 0x6 0x0 +@@ -950,6 +951,7 @@ + #define MX6QDL_PAD_RGMII_TXC__GPIO6_IO19 0x2d8 0x6c0 0x000 0x5 0x0 + #define MX6QDL_PAD_RGMII_TXC__XTALOSC_REF_CLK_24M 0x2d8 0x6c0 0x000 0x7 0x0 + #define MX6QDL_PAD_SD1_CLK__SD1_CLK 0x2dc 0x6c4 0x928 0x0 0x1 ++#define MX6QDL_PAD_SD1_CLK__OSC32K_32K_OUT 0x2dc 0x6c4 0x000 0x2 0x0 + #define MX6QDL_PAD_SD1_CLK__GPT_CLKIN 0x2dc 0x6c4 0x000 0x3 0x0 + #define MX6QDL_PAD_SD1_CLK__GPIO1_IO20 0x2dc 0x6c4 0x000 0x5 0x0 + #define MX6QDL_PAD_SD1_CMD__SD1_CMD 0x2e0 0x6c8 0x000 0x0 0x0 +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-sabreauto.dts linux-openelec/arch/arm/boot/dts/imx6dl-sabreauto.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-sabreauto.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-sabreauto.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -15,3 +15,16 @@ + model = "Freescale i.MX6 DualLite/Solo SABRE Automotive Board"; + compatible = "fsl,imx6dl-sabreauto", "fsl,imx6dl"; + }; ++ ++&ldb { ++ ipu_id = <0>; ++ sec_ipu_id = <0>; ++}; ++ ++&mxcfb1 { ++ status = "okay"; ++}; ++ ++&mxcfb2 { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-sabrelite.dts linux-openelec/arch/arm/boot/dts/imx6dl-sabrelite.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-sabrelite.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-sabrelite.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,20 @@ ++/* ++ * Copyright 2011 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6dl.dtsi" ++#include "imx6qdl-sabrelite.dtsi" ++ ++/ { ++ model = "Freescale i.MX6 DualLite SABRE Lite Board"; ++ compatible = "fsl,imx6dl-sabrelite", "fsl,imx6dl"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-sabresd.dts linux-openelec/arch/arm/boot/dts/imx6dl-sabresd.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-sabresd.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-sabresd.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -15,3 +15,20 @@ + model = "Freescale i.MX6 DualLite SABRE Smart Device Board"; + compatible = "fsl,imx6dl-sabresd", "fsl,imx6dl"; + }; ++ ++&ldb { ++ ipu_id = <0>; ++ sec_ipu_id = <0>; ++}; ++ ++&pxp { ++ status = "okay"; ++}; ++ ++&mxcfb1 { ++ status = "okay"; ++}; ++ ++&mxcfb2 { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts linux-openelec/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,19 @@ ++/* ++ * Copyright (C) 2013 Freescale Semiconductor, Inc. ++ * ++ * 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 "imx6dl-sabresd.dts" ++ ++&hdmi_video { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hdmi_hdcp>; ++ fsl,hdcp; ++}; ++ ++&i2c2 { ++ status = "disable"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-arm2.dts linux-openelec/arch/arm/boot/dts/imx6q-arm2.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-arm2.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-arm2.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -23,14 +23,27 @@ + + regulators { + compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; + +- reg_3p3v: 3p3v { ++ reg_3p3v: regulator@0 { + compatible = "regulator-fixed"; ++ reg = <0>; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; ++ ++ reg_usb_otg_vbus: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; + }; + + leds { +@@ -46,7 +59,7 @@ + + &gpmi { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_gpmi_nand_1>; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; + status = "disabled"; /* gpmi nand conflicts with SD */ + }; + +@@ -54,28 +67,131 @@ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + +- hog { ++ imx6q-arm2 { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6QDL_PAD_EIM_D25__GPIO3_IO25 0x80000000 + >; + }; +- }; + +- arm2 { +- pinctrl_usdhc3_arm2: usdhc3grp-arm2 { ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_KEY_COL2__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D26__UART2_RX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D27__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D28__UART2_DTE_CTS_B 0x1b0b1 ++ MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 ++ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 ++ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 ++ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3_cdwp: usdhc3cdwp { + fsl,pins = < + MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 + MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000 + >; + }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 ++ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 ++ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 ++ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 ++ >; ++ }; + }; + }; + + &fec { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_enet_2>; ++ pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; ++ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; + status = "okay"; + }; + +@@ -84,8 +200,8 @@ + wp-gpios = <&gpio6 14 0>; + vmmc-supply = <®_3p3v>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc3_1 +- &pinctrl_usdhc3_arm2>; ++ pinctrl-0 = <&pinctrl_usdhc3 ++ &pinctrl_usdhc3_cdwp>; + status = "okay"; + }; + +@@ -93,13 +209,13 @@ + non-removable; + vmmc-supply = <®_3p3v>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc4_1>; ++ pinctrl-0 = <&pinctrl_usdhc4>; + status = "okay"; + }; + + &uart2 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart2_2>; ++ pinctrl-0 = <&pinctrl_uart2>; + fsl,dte-mode; + fsl,uart-has-rtscts; + status = "okay"; +@@ -107,6 +223,6 @@ + + &uart4 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart4_1>; ++ pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-arm2-hsic.dts linux-openelec/arch/arm/boot/dts/imx6q-arm2-hsic.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-arm2-hsic.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-arm2-hsic.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,32 @@ ++/* ++ * Copyright 2013 Freescale Semiconductor, Inc. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include "imx6q-arm2.dts" ++ ++&fec { ++ status = "disabled"; ++}; ++ ++&usbh2 { ++ pinctrl-names = "idle", "active"; ++ pinctrl-0 = <&pinctrl_usbh2_1>; ++ pinctrl-1 = <&pinctrl_usbh2_2>; ++ osc-clkgate-delay = <0x3>; ++ status = "okay"; ++}; ++ ++&usbh3 { ++ pinctrl-names = "idle", "active"; ++ pinctrl-0 = <&pinctrl_usbh3_1>; ++ pinctrl-1 = <&pinctrl_usbh3_2>; ++ osc-clkgate-delay = <0x3>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-cm-fx6.dts linux-openelec/arch/arm/boot/dts/imx6q-cm-fx6.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-cm-fx6.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-cm-fx6.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,107 @@ ++/* ++ * Copyright 2013 CompuLab Ltd. ++ * ++ * Author: Valentin Raevsky ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++ ++/ { ++ model = "CompuLab CM-FX6"; ++ compatible = "compulab,cm-fx6", "fsl,imx6q"; ++ ++ memory { ++ reg = <0x10000000 0x80000000>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ heartbeat-led { ++ label = "Heartbeat"; ++ gpios = <&gpio2 31 0>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ status = "okay"; ++}; ++ ++&gpmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; ++ status = "okay"; ++}; ++ ++&iomuxc { ++ imx6q-cm-fx6 { ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 ++ >; ++ }; ++ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 ++ >; ++ }; ++ }; ++}; ++ ++&uart4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart4>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-cubox-i.dts linux-openelec/arch/arm/boot/dts/imx6q-cubox-i.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-cubox-i.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-cubox-i.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -13,4 +13,8 @@ + + &sata { + status = "okay"; ++ fsl,transmit-level-mV = <1104>; ++ fsl,transmit-boost-mdB = <0>; ++ fsl,transmit-atten-16ths = <9>; ++ fsl,no-spread-spectrum; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts linux-openelec/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,23 @@ ++/* ++ * Copyright 2013 Sascha Hauer ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef __DTS_V1__ ++#define __DTS_V1__ ++/dts-v1/; ++#endif ++ ++#include "imx6q.dtsi" ++#include "imx6qdl-dfi-fs700-m60.dtsi" ++ ++/ { ++ model = "DFI FS700-M60-6QD i.MX6qd Q7 Board"; ++ compatible = "dfi,fs700-m60-6qd", "dfi,fs700e-m60", "fsl,imx6q"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2015-07-24 18:03:30.208842002 -0500 +@@ -5,11 +5,34 @@ + #include "imx6qdl-microsom-ar8035.dtsi" + + / { ++ chosen { ++ bootargs = "quiet console=ttymxc0,115200 root=/dev/mmcblk0p2 rw"; ++ }; ++ ++ aliases { ++ mxcfb0 = &mxcfb1; ++ }; ++ + ir_recv: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio3 9 1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_ir>; ++ linux,rc-map-name = "rc-rc6-mce"; ++ }; ++ ++ pwmleds { ++ compatible = "pwm-leds"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_pwm1>; ++ ++ front { ++ active-low; ++ label = "imx6:red:front"; ++ max-brightness = <248>; ++ pwms = <&pwm1 0 50000>; ++ linux,default-trigger = "heartbeat"; ++ }; + }; + + regulators { +@@ -49,10 +72,62 @@ + sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; +- /* IMX6 doesn't implement this yet */ + spdif-controller = <&spdif>; + spdif-out; + }; ++ ++ sound-hdmi { ++ compatible = "fsl,imx6q-audio-hdmi", ++ "fsl,imx-audio-hdmi"; ++ model = "imx-audio-hdmi"; ++ hdmi-controller = <&hdmi_audio>; ++ }; ++ ++ mxcfb1: fb@0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <32>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "okay"; ++ }; ++}; ++ ++&hdmi_core { ++ ipu_id = <0>; ++ disp_id = <0>; ++ status = "okay"; ++}; ++ ++&hdmi_video { ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ status = "okay"; ++}; ++ ++&hdmi_audio { ++ status = "okay"; ++}; ++ ++&hdmi_cec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_hdmi>; ++ status = "okay"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_i2c2>; ++ ++ status = "okay"; ++ ++ ddc: imx6_hdmi_i2c@50 { ++ compatible = "fsl,imx6-hdmi-i2c"; ++ reg = <0x50>; ++ }; + }; + + &i2c3 { +@@ -69,6 +144,19 @@ + + &iomuxc { + cubox_i { ++ pinctrl_cubox_i_hdmi: cubox-i-hdmi { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 ++ >; ++ }; ++ ++ pinctrl_cubox_i_i2c2: cubox-i-i2c2 { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ + pinctrl_cubox_i_i2c3: cubox-i-i2c3 { + fsl,pins = < + MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 +@@ -82,16 +170,35 @@ + >; + }; + ++ pinctrl_cubox_i_pwm1: cubox-i-pwm1-front-led { ++ fsl,pins = ; ++ }; ++ + pinctrl_cubox_i_spdif: cubox-i-spdif { + fsl,pins = ; + }; + ++ pinctrl_cubox_i_usbh1: cubox-i-usbh1 { ++ fsl,pins = ; ++ }; ++ + pinctrl_cubox_i_usbh1_vbus: cubox-i-usbh1-vbus { +- fsl,pins = ; ++ fsl,pins = ; ++ }; ++ ++ pinctrl_cubox_i_usbotg: cubox-i-usbotg { ++ /* ++ * The Cubox-i pulls ID low, but as it's pointless ++ * leaving it as a pull-up, even if it is just 10uA. ++ */ ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059 ++ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0 ++ >; + }; + + pinctrl_cubox_i_usbotg_vbus: cubox-i-usbotg-vbus { +- fsl,pins = ; ++ fsl,pins = ; + }; + + pinctrl_cubox_i_usdhc2_aux: cubox-i-usdhc2-aux { +@@ -111,29 +218,76 @@ + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 + >; + }; ++ ++ pinctrl_cubox_i_usdhc2_100mhz: cubox-i-usdhc2-100mhz { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170b9 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100b9 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130b9 ++ >; ++ }; ++ ++ pinctrl_cubox_i_usdhc2_200mhz: cubox-i-usdhc2-200mhz { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170f9 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100f9 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130f9 ++ >; ++ }; + }; + }; + + &spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_spdif>; ++ clocks = <&clks 197>, <&clks 0>, ++ <&clks 197>, <&clks 0>, ++ <&clks 0>, <&clks 0>, ++ <&clks 0>, <&clks 0>, ++ <&clks 0>; ++ clock-names = "core", "rxtx0", ++ "rxtx1", "rxtx2", ++ "rxtx3", "rxtx4", ++ "rxtx5", "rxtx6", ++ "rxtx7"; + status = "okay"; + }; + + &usbh1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usbh1>; + vbus-supply = <®_usbh1_vbus>; + status = "okay"; + }; + + &usbotg { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usbotg>; + vbus-supply = <®_usbotg_vbus>; + status = "okay"; + }; + + &usdhc2 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2>; ++ pinctrl-1 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_100mhz>; ++ pinctrl-2 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_200mhz>; + vmmc-supply = <®_3p3v>; + cd-gpios = <&gpio1 4 0>; ++ no-1-8-v; + status = "okay"; + }; ++ ++ ++&gpc { ++ fsl,cpu_pupscr_sw2iso = <0xf>; ++ fsl,cpu_pupscr_sw = <0xf>; ++ fsl,cpu_pdnscr_iso2sw = <0x1>; ++ fsl,cpu_pdnscr_iso = <0x1>; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,199 @@ ++/ { ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ dummy_reg: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "dummy-supply"; ++ }; ++ ++ reg_usb_otg_vbus: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ chosen { ++ stdout-path = &uart1; ++ }; ++}; ++ ++&ecspi3 { ++ fsl,spi-num-chipselects = <1>; ++ cs-gpios = <&gpio4 24 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ecspi3>; ++ status = "okay"; ++ ++ flash: m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "sst,sst25vf040b", "m25p80"; ++ spi-max-frequency = <20000000>; ++ reg = <0>; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ status = "okay"; ++ phy-mode = "rgmii"; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6qdl-dfi-fs700-m60 { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000 ++ MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x80000000 /* PMIC irq */ ++ MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x80000000 /* MAX11801 irq */ ++ MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x000030b0 /* Backlight enable */ ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D16__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc2: usdhc2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 ++ MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 /* card detect */ ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 ++ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 ++ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 ++ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_ecspi3: ecspi3grp { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1 ++ MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1 ++ MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1 ++ MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x80000000 /* SPI NOR chipselect */ ++ >; ++ }; ++ }; ++}; ++ ++&i2c2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ ++&usdhc2 { /* module slot */ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc2>; ++ cd-gpios = <&gpio2 2 0>; ++ status = "okay"; ++}; ++ ++&usdhc3 { /* baseboard slot */ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++}; ++ ++&usdhc4 { /* eMMC */ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc4>; ++ bus-width = <8>; ++ non-removable; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -10,10 +10,16 @@ + * http://www.gnu.org/copyleft/gpl.html + */ + ++#include ++ + #include "skeleton.dtsi" ++#include + + / { + aliases { ++ ethernet0 = &fec; ++ can0 = &can1; ++ can1 = &can2; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; +@@ -24,6 +30,11 @@ + i2c0 = &i2c1; + i2c1 = &i2c2; + i2c2 = &i2c3; ++ ipu0 = &ipu1; ++ mmc0 = &usdhc1; ++ mmc1 = &usdhc2; ++ mmc2 = &usdhc3; ++ mmc3 = &usdhc4; + serial0 = &uart1; + serial1 = &uart2; + serial2 = &uart3; +@@ -33,13 +44,13 @@ + spi1 = &ecspi2; + spi2 = &ecspi3; + spi3 = &ecspi4; ++ usbphy0 = &usbphy1; ++ usbphy1 = &usbphy2; + }; + + intc: interrupt-controller@00a01000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; +- #address-cells = <1>; +- #size-cells = <1>; + interrupt-controller; + reg = <0x00a01000 0x1000>, + <0x00a00100 0x100>; +@@ -51,20 +62,27 @@ + + ckil { + compatible = "fsl,imx-ckil", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <32768>; + }; + + ckih1 { + compatible = "fsl,imx-ckih1", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <0>; + }; + + osc { + compatible = "fsl,imx-osc", "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; + ++ pu_dummy: pudummy_reg { ++ compatible = "fsl,imx6-dummy-pureg"; /* only used in ldo-bypass */ ++ }; ++ + soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -75,7 +93,10 @@ + dma_apbh: dma-apbh@00110000 { + compatible = "fsl,imx6q-dma-apbh", "fsl,imx28-dma-apbh"; + reg = <0x00110000 0x2000>; +- interrupts = <0 13 0x04>, <0 13 0x04>, <0 13 0x04>, <0 13 0x04>; ++ interrupts = <0 13 IRQ_TYPE_LEVEL_HIGH>, ++ <0 13 IRQ_TYPE_LEVEL_HIGH>, ++ <0 13 IRQ_TYPE_LEVEL_HIGH>, ++ <0 13 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "gpmi0", "gpmi1", "gpmi2", "gpmi3"; + #dma-cells = <1>; + dma-channels = <4>; +@@ -88,7 +109,7 @@ + #size-cells = <1>; + reg = <0x00112000 0x2000>, <0x00114000 0x2000>; + reg-names = "gpmi-nand", "bch"; +- interrupts = <0 15 0x04>; ++ interrupts = <0 15 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "bch"; + clocks = <&clks 152>, <&clks 153>, <&clks 151>, + <&clks 150>, <&clks 149>; +@@ -109,11 +130,13 @@ + L2: l2-cache@00a02000 { + compatible = "arm,pl310-cache"; + reg = <0x00a02000 0x1000>; +- interrupts = <0 92 0x04>; ++ interrupts = <0 92 IRQ_TYPE_LEVEL_HIGH>; + cache-unified; + cache-level = <2>; + arm,tag-latency = <4 2 3>; + arm,data-latency = <4 2 3>; ++ arm,dynamic-clk-gating; ++ arm,standby-mode; + }; + + pcie: pcie@0x01000000 { +@@ -126,15 +149,22 @@ + 0x81000000 0 0 0x01f80000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x01000000 0x01000000 0 0x00f00000>; /* non-prefetchable memory */ + num-lanes = <1>; +- interrupts = <0 123 0x04>; +- clocks = <&clks 189>, <&clks 187>, <&clks 206>, <&clks 144>; +- clock-names = "pcie_ref_125m", "sata_ref_100m", "lvds_gate", "pcie_axi"; ++ interrupts = ; ++ interrupt-names = "pme"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 0 2 &intc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 0 3 &intc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 0 4 &intc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 144>, <&clks 221>, <&clks 189>, <&clks 187>; ++ clock-names = "pcie_axi", "lvds_gate", "pcie_ref_125m", "sata_ref_100m"; + status = "disabled"; + }; + + pmu { + compatible = "arm,cortex-a9-pmu"; +- interrupts = <0 94 0x04>; ++ interrupts = <0 94 IRQ_TYPE_LEVEL_HIGH>; + }; + + aips-bus@02000000 { /* AIPS1 */ +@@ -154,7 +184,7 @@ + spdif: spdif@02004000 { + compatible = "fsl,imx35-spdif"; + reg = <0x02004000 0x4000>; +- interrupts = <0 52 0x04>; ++ interrupts = <0 52 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&sdma 14 18 0>, + <&sdma 15 18 0>; + dma-names = "rx", "tx"; +@@ -176,9 +206,11 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi"; + reg = <0x02008000 0x4000>; +- interrupts = <0 31 0x04>; ++ interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 112>, <&clks 112>; + clock-names = "ipg", "per"; ++ dmas = <&sdma 3 7 1>, <&sdma 4 7 2>; ++ dma-names = "rx", "tx"; + status = "disabled"; + }; + +@@ -187,9 +219,11 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi"; + reg = <0x0200c000 0x4000>; +- interrupts = <0 32 0x04>; ++ interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 113>, <&clks 113>; + clock-names = "ipg", "per"; ++ dmas = <&sdma 5 7 1>, <&sdma 6 7 2>; ++ dma-names = "rx", "tx"; + status = "disabled"; + }; + +@@ -198,9 +232,11 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi"; + reg = <0x02010000 0x4000>; +- interrupts = <0 33 0x04>; ++ interrupts = <0 33 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 114>, <&clks 114>; + clock-names = "ipg", "per"; ++ dmas = <&sdma 7 7 1>, <&sdma 8 7 2>; ++ dma-names = "rx", "tx"; + status = "disabled"; + }; + +@@ -209,16 +245,18 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi"; + reg = <0x02014000 0x4000>; +- interrupts = <0 34 0x04>; ++ interrupts = <0 34 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 115>, <&clks 115>; + clock-names = "ipg", "per"; ++ dmas = <&sdma 9 7 1>, <&sdma 10 7 2>; ++ dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart1: serial@02020000 { + compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x02020000 0x4000>; +- interrupts = <0 26 0x04>; ++ interrupts = <0 26 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 160>, <&clks 161>; + clock-names = "ipg", "per"; + dmas = <&sdma 25 4 0>, <&sdma 26 4 0>; +@@ -227,15 +265,23 @@ + }; + + esai: esai@02024000 { ++ compatible = "fsl,imx6q-esai"; + reg = <0x02024000 0x4000>; +- interrupts = <0 51 0x04>; ++ interrupts = <0 51 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 118>; ++ fsl,esai-dma-events = <24 23>; ++ fsl,flags = <1>; ++ status = "disabled"; + }; + + ssi1: ssi@02028000 { +- compatible = "fsl,imx6q-ssi","fsl,imx21-ssi"; ++ compatible = "fsl,imx6q-ssi", ++ "fsl,imx51-ssi", ++ "fsl,imx21-ssi"; + reg = <0x02028000 0x4000>; +- interrupts = <0 46 0x04>; +- clocks = <&clks 178>; ++ interrupts = <0 46 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 178>, <&clks 157>; ++ clock-names = "ipg", "baud"; + dmas = <&sdma 37 1 0>, + <&sdma 38 1 0>; + dma-names = "rx", "tx"; +@@ -245,10 +291,13 @@ + }; + + ssi2: ssi@0202c000 { +- compatible = "fsl,imx6q-ssi","fsl,imx21-ssi"; ++ compatible = "fsl,imx6q-ssi", ++ "fsl,imx51-ssi", ++ "fsl,imx21-ssi"; + reg = <0x0202c000 0x4000>; +- interrupts = <0 47 0x04>; +- clocks = <&clks 179>; ++ interrupts = <0 47 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 179>, <&clks 158>; ++ clock-names = "ipg", "baud"; + dmas = <&sdma 41 1 0>, + <&sdma 42 1 0>; + dma-names = "rx", "tx"; +@@ -258,10 +307,13 @@ + }; + + ssi3: ssi@02030000 { +- compatible = "fsl,imx6q-ssi","fsl,imx21-ssi"; ++ compatible = "fsl,imx6q-ssi", ++ "fsl,imx51-ssi", ++ "fsl,imx21-ssi"; + reg = <0x02030000 0x4000>; +- interrupts = <0 48 0x04>; +- clocks = <&clks 180>; ++ interrupts = <0 48 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 180>, <&clks 159>; ++ clock-names = "ipg", "baud"; + dmas = <&sdma 45 1 0>, + <&sdma 46 1 0>; + dma-names = "rx", "tx"; +@@ -271,8 +323,25 @@ + }; + + asrc: asrc@02034000 { ++ compatible = "fsl,imx53-asrc"; + reg = <0x02034000 0x4000>; +- interrupts = <0 50 0x04>; ++ interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 107>, <&clks 156>; ++ clock-names = "core", "dma"; ++ dmas = <&sdma 17 20 1>, <&sdma 18 20 1>, <&sdma 19 20 1>, ++ <&sdma 20 20 1>, <&sdma 21 20 1>, <&sdma 22 20 1>; ++ dma-names = "rxa", "rxb", "rxc", ++ "txa", "txb", "txc"; ++ status = "okay"; ++ }; ++ ++ asrc_p2p: asrc_p2p { ++ compatible = "fsl,imx6q-asrc-p2p"; ++ fsl,output-rate = <48000>; ++ fsl,output-width = <16>; ++ fsl,asrc-dma-rx-events = <17 18 19>; ++ fsl,asrc-dma-tx-events = <20 21 22>; ++ status = "okay"; + }; + + spba@0203c000 { +@@ -281,8 +350,19 @@ + }; + + vpu: vpu@02040000 { ++ compatible = "fsl,imx6-vpu"; + reg = <0x02040000 0x3c000>; +- interrupts = <0 3 0x04 0 12 0x04>; ++ reg-names = "vpu_regs"; ++ interrupts = <0 3 IRQ_TYPE_LEVEL_HIGH>, ++ <0 12 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "vpu_jpu_irq", "vpu_ipi_irq"; ++ clocks = <&clks 168>, <&clks 140>, <&clks 142>; ++ clock-names = "vpu_clk", "mmdc_ch0_axi", "ocram"; ++ iramsize = <0x21000>; ++ iram = <&ocram>; ++ resets = <&src 1>; ++ pu-supply = <®_pu>; ++ status = "disabled"; + }; + + aipstz@0207c000 { /* AIPSTZ1 */ +@@ -293,7 +373,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm"; + reg = <0x02080000 0x4000>; +- interrupts = <0 83 0x04>; ++ interrupts = <0 83 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 62>, <&clks 145>; + clock-names = "ipg", "per"; + }; +@@ -302,7 +382,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm"; + reg = <0x02084000 0x4000>; +- interrupts = <0 84 0x04>; ++ interrupts = <0 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 62>, <&clks 146>; + clock-names = "ipg", "per"; + }; +@@ -311,7 +391,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm"; + reg = <0x02088000 0x4000>; +- interrupts = <0 85 0x04>; ++ interrupts = <0 85 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 62>, <&clks 147>; + clock-names = "ipg", "per"; + }; +@@ -320,7 +400,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm"; + reg = <0x0208c000 0x4000>; +- interrupts = <0 86 0x04>; ++ interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 62>, <&clks 148>; + clock-names = "ipg", "per"; + }; +@@ -328,23 +408,25 @@ + can1: flexcan@02090000 { + compatible = "fsl,imx6q-flexcan"; + reg = <0x02090000 0x4000>; +- interrupts = <0 110 0x04>; ++ interrupts = <0 110 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 108>, <&clks 109>; + clock-names = "ipg", "per"; ++ status = "disabled"; + }; + + can2: flexcan@02094000 { + compatible = "fsl,imx6q-flexcan"; + reg = <0x02094000 0x4000>; +- interrupts = <0 111 0x04>; ++ interrupts = <0 111 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 110>, <&clks 111>; + clock-names = "ipg", "per"; ++ status = "disabled"; + }; + + gpt: gpt@02098000 { + compatible = "fsl,imx6q-gpt", "fsl,imx31-gpt"; + reg = <0x02098000 0x4000>; +- interrupts = <0 55 0x04>; ++ interrupts = <0 55 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 119>, <&clks 120>; + clock-names = "ipg", "per"; + }; +@@ -352,7 +434,8 @@ + gpio1: gpio@0209c000 { + compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; + reg = <0x0209c000 0x4000>; +- interrupts = <0 66 0x04 0 67 0x04>; ++ interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>, ++ <0 67 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -362,7 +445,8 @@ + gpio2: gpio@020a0000 { + compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; + reg = <0x020a0000 0x4000>; +- interrupts = <0 68 0x04 0 69 0x04>; ++ interrupts = <0 68 IRQ_TYPE_LEVEL_HIGH>, ++ <0 69 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -372,7 +456,8 @@ + gpio3: gpio@020a4000 { + compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; + reg = <0x020a4000 0x4000>; +- interrupts = <0 70 0x04 0 71 0x04>; ++ interrupts = <0 70 IRQ_TYPE_LEVEL_HIGH>, ++ <0 71 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -382,7 +467,8 @@ + gpio4: gpio@020a8000 { + compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; + reg = <0x020a8000 0x4000>; +- interrupts = <0 72 0x04 0 73 0x04>; ++ interrupts = <0 72 IRQ_TYPE_LEVEL_HIGH>, ++ <0 73 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -392,7 +478,8 @@ + gpio5: gpio@020ac000 { + compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; + reg = <0x020ac000 0x4000>; +- interrupts = <0 74 0x04 0 75 0x04>; ++ interrupts = <0 74 IRQ_TYPE_LEVEL_HIGH>, ++ <0 75 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -402,7 +489,8 @@ + gpio6: gpio@020b0000 { + compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; + reg = <0x020b0000 0x4000>; +- interrupts = <0 76 0x04 0 77 0x04>; ++ interrupts = <0 76 IRQ_TYPE_LEVEL_HIGH>, ++ <0 77 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -412,7 +500,8 @@ + gpio7: gpio@020b4000 { + compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; + reg = <0x020b4000 0x4000>; +- interrupts = <0 78 0x04 0 79 0x04>; ++ interrupts = <0 78 IRQ_TYPE_LEVEL_HIGH>, ++ <0 79 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -421,20 +510,20 @@ + + kpp: kpp@020b8000 { + reg = <0x020b8000 0x4000>; +- interrupts = <0 82 0x04>; ++ interrupts = <0 82 IRQ_TYPE_LEVEL_HIGH>; + }; + + wdog1: wdog@020bc000 { + compatible = "fsl,imx6q-wdt", "fsl,imx21-wdt"; + reg = <0x020bc000 0x4000>; +- interrupts = <0 80 0x04>; ++ interrupts = <0 80 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 0>; + }; + + wdog2: wdog@020c0000 { + compatible = "fsl,imx6q-wdt", "fsl,imx21-wdt"; + reg = <0x020c0000 0x4000>; +- interrupts = <0 81 0x04>; ++ interrupts = <0 81 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 0>; + status = "disabled"; + }; +@@ -442,14 +531,17 @@ + clks: ccm@020c4000 { + compatible = "fsl,imx6q-ccm"; + reg = <0x020c4000 0x4000>; +- interrupts = <0 87 0x04 0 88 0x04>; ++ interrupts = <0 87 IRQ_TYPE_LEVEL_HIGH>, ++ <0 88 IRQ_TYPE_LEVEL_HIGH>; + #clock-cells = <1>; + }; + + anatop: anatop@020c8000 { + compatible = "fsl,imx6q-anatop", "syscon", "simple-bus"; + reg = <0x020c8000 0x1000>; +- interrupts = <0 49 0x04 0 54 0x04 0 127 0x04>; ++ interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>, ++ <0 54 IRQ_TYPE_LEVEL_HIGH>, ++ <0 127 IRQ_TYPE_LEVEL_HIGH>; + + regulator-1p1@110 { + compatible = "fsl,anatop-regulator"; +@@ -495,7 +587,7 @@ + + reg_arm: regulator-vddcore@140 { + compatible = "fsl,anatop-regulator"; +- regulator-name = "cpu"; ++ regulator-name = "vddarm"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1450000>; + regulator-always-on; +@@ -515,7 +607,6 @@ + regulator-name = "vddpu"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1450000>; +- regulator-always-on; + anatop-reg-offset = <0x140>; + anatop-vol-bit-shift = <9>; + anatop-vol-bit-width = <5>; +@@ -547,23 +638,38 @@ + + tempmon: tempmon { + compatible = "fsl,imx6q-tempmon"; +- interrupts = <0 49 0x04>; ++ interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>; + fsl,tempmon = <&anatop>; + fsl,tempmon-data = <&ocotp>; ++ clocks = <&clks 172>; + }; + + usbphy1: usbphy@020c9000 { + compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; + reg = <0x020c9000 0x1000>; +- interrupts = <0 44 0x04>; ++ interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 182>; ++ fsl,anatop = <&anatop>; + }; + + usbphy2: usbphy@020ca000 { + compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; + reg = <0x020ca000 0x1000>; +- interrupts = <0 45 0x04>; ++ interrupts = <0 45 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 183>; ++ fsl,anatop = <&anatop>; ++ }; ++ ++ usbphy_nop1: usbphy_nop1 { ++ compatible = "usb-nop-xceiv"; ++ clocks = <&clks 182>; ++ clock-names = "main_clk"; ++ }; ++ ++ usbphy_nop2: usbphy_nop2 { ++ compatible = "usb-nop-xceiv"; ++ clocks = <&clks 182>; ++ clock-names = "main_clk"; + }; + + snvs@020cc000 { +@@ -575,31 +681,39 @@ + snvs-rtc-lp@34 { + compatible = "fsl,sec-v4.0-mon-rtc-lp"; + reg = <0x34 0x58>; +- interrupts = <0 19 0x04 0 20 0x04>; ++ interrupts = <0 19 IRQ_TYPE_LEVEL_HIGH>, ++ <0 20 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + epit1: epit@020d0000 { /* EPIT1 */ + reg = <0x020d0000 0x4000>; +- interrupts = <0 56 0x04>; ++ interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + }; + + epit2: epit@020d4000 { /* EPIT2 */ + reg = <0x020d4000 0x4000>; +- interrupts = <0 57 0x04>; ++ interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + }; + + src: src@020d8000 { + compatible = "fsl,imx6q-src", "fsl,imx51-src"; + reg = <0x020d8000 0x4000>; +- interrupts = <0 91 0x04 0 96 0x04>; ++ interrupts = <0 91 IRQ_TYPE_LEVEL_HIGH>, ++ <0 96 IRQ_TYPE_LEVEL_HIGH>; + #reset-cells = <1>; + }; + + gpc: gpc@020dc000 { + compatible = "fsl,imx6q-gpc"; + reg = <0x020dc000 0x4000>; +- interrupts = <0 89 0x04 0 90 0x04>; ++ interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>, ++ <0 90 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 122>, <&clks 74>, <&clks 121>, ++ <&clks 26>, <&clks 143>, <&clks 168>, <&clks 62>; ++ clock-names = "gpu3d_core", "gpu3d_shader", "gpu2d_core", ++ "gpu2d_axi", "openvg_axi", "vpu_axi", "ipg"; ++ pu-supply = <®_pu>; + }; + + gpr: iomuxc-gpr@020e0000 { +@@ -610,778 +724,40 @@ + iomuxc: iomuxc@020e0000 { + compatible = "fsl,imx6dl-iomuxc", "fsl,imx6q-iomuxc"; + reg = <0x020e0000 0x4000>; +- +- audmux { +- pinctrl_audmux_1: audmux-1 { +- fsl,pins = < +- MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x80000000 +- MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x80000000 +- MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x80000000 +- MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x80000000 +- >; +- }; +- +- pinctrl_audmux_2: audmux-2 { +- fsl,pins = < +- MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x80000000 +- MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x80000000 +- MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x80000000 +- MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x80000000 +- >; +- }; +- +- pinctrl_audmux_3: audmux-3 { +- fsl,pins = < +- MX6QDL_PAD_DISP0_DAT16__AUD5_TXC 0x80000000 +- MX6QDL_PAD_DISP0_DAT18__AUD5_TXFS 0x80000000 +- MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x80000000 +- >; +- }; +- }; +- +- ecspi1 { +- pinctrl_ecspi1_1: ecspi1grp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 +- MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 +- MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 +- >; +- }; +- +- pinctrl_ecspi1_2: ecspi1grp-2 { +- fsl,pins = < +- MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1 +- MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1 +- MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1 +- >; +- }; +- }; +- +- ecspi3 { +- pinctrl_ecspi3_1: ecspi3grp-1 { +- fsl,pins = < +- MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1 +- MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1 +- MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1 +- >; +- }; +- }; +- +- enet { +- pinctrl_enet_1: enetgrp-1 { +- fsl,pins = < +- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 +- MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 +- MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 +- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 +- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 +- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 +- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 +- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 +- MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 +- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 +- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 +- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 +- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 +- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 +- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 +- MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 +- >; +- }; +- +- pinctrl_enet_2: enetgrp-2 { +- fsl,pins = < +- MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0 +- MX6QDL_PAD_KEY_COL2__ENET_MDC 0x1b0b0 +- MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 +- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 +- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 +- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 +- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 +- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 +- MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 +- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 +- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 +- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 +- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 +- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 +- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 +- >; +- }; +- +- pinctrl_enet_3: enetgrp-3 { +- fsl,pins = < +- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 +- MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 +- MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 +- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 +- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 +- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 +- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 +- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 +- MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 +- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 +- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 +- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 +- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 +- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 +- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 +- MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0 +- >; +- }; +- }; +- +- esai { +- pinctrl_esai_1: esaigrp-1 { +- fsl,pins = < +- MX6QDL_PAD_ENET_RXD0__ESAI_TX_HF_CLK 0x1b030 +- MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1b030 +- MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1b030 +- MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1b030 +- MX6QDL_PAD_ENET_TXD1__ESAI_TX2_RX3 0x1b030 +- MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1b030 +- MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1b030 +- MX6QDL_PAD_NANDF_CS2__ESAI_TX0 0x1b030 +- MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x1b030 +- >; +- }; +- +- pinctrl_esai_2: esaigrp-2 { +- fsl,pins = < +- MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1b030 +- MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1b030 +- MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1b030 +- MX6QDL_PAD_GPIO_5__ESAI_TX2_RX3 0x1b030 +- MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1b030 +- MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1b030 +- MX6QDL_PAD_GPIO_17__ESAI_TX0 0x1b030 +- MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x1b030 +- MX6QDL_PAD_ENET_MDIO__ESAI_RX_CLK 0x1b030 +- MX6QDL_PAD_GPIO_9__ESAI_RX_FS 0x1b030 +- >; +- }; +- }; +- +- flexcan1 { +- pinctrl_flexcan1_1: flexcan1grp-1 { +- fsl,pins = < +- MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000 +- MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000 +- >; +- }; +- +- pinctrl_flexcan1_2: flexcan1grp-2 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x80000000 +- MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000 +- >; +- }; +- }; +- +- flexcan2 { +- pinctrl_flexcan2_1: flexcan2grp-1 { +- fsl,pins = < +- MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x80000000 +- MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x80000000 +- >; +- }; +- }; +- +- gpmi-nand { +- pinctrl_gpmi_nand_1: gpmi-nand-1 { +- fsl,pins = < +- MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 +- MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 +- MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 +- MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 +- MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 +- MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 +- MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 +- MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 +- MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 +- MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 +- MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 +- MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 +- MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 +- MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 +- MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 +- MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 +- MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 +- >; +- }; +- }; +- +- hdmi_hdcp { +- pinctrl_hdmi_hdcp_1: hdmihdcpgrp-1 { +- fsl,pins = < +- MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x4001b8b1 +- MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_hdmi_hdcp_2: hdmihdcpgrp-2 { +- fsl,pins = < +- MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x4001b8b1 +- MX6QDL_PAD_EIM_D16__HDMI_TX_DDC_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_hdmi_hdcp_3: hdmihdcpgrp-3 { +- fsl,pins = < +- MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x4001b8b1 +- MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 +- >; +- }; +- }; +- +- hdmi_cec { +- pinctrl_hdmi_cec_1: hdmicecgrp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x1f8b0 +- >; +- }; +- +- pinctrl_hdmi_cec_2: hdmicecgrp-2 { +- fsl,pins = < +- MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 +- >; +- }; +- }; +- +- i2c1 { +- pinctrl_i2c1_1: i2c1grp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 +- MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_i2c1_2: i2c1grp-2 { +- fsl,pins = < +- MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1 +- MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1 +- >; +- }; +- }; +- +- i2c2 { +- pinctrl_i2c2_1: i2c2grp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 +- MX6QDL_PAD_EIM_D16__I2C2_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_i2c2_2: i2c2grp-2 { +- fsl,pins = < +- MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 +- MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_i2c2_3: i2c2grp-3 { +- fsl,pins = < +- MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 +- MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 +- >; +- }; +- }; +- +- i2c3 { +- pinctrl_i2c3_1: i2c3grp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 +- MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_i2c3_2: i2c3grp-2 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 +- MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_i2c3_3: i2c3grp-3 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1 +- MX6QDL_PAD_GPIO_16__I2C3_SDA 0x4001b8b1 +- >; +- }; +- +- pinctrl_i2c3_4: i2c3grp-4 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 +- MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 +- >; +- }; +- }; +- +- ipu1 { +- pinctrl_ipu1_1: ipu1grp-1 { +- fsl,pins = < +- MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10 +- MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x10 +- MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x10 +- MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x10 +- MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000000 +- MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x10 +- MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x10 +- MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x10 +- MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x10 +- MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x10 +- MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x10 +- MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x10 +- MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x10 +- MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x10 +- MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x10 +- MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x10 +- MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x10 +- MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x10 +- MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x10 +- MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x10 +- MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x10 +- MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x10 +- MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x10 +- MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x10 +- MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x10 +- MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x10 +- MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x10 +- MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x10 +- MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x10 +- >; +- }; +- +- pinctrl_ipu1_2: ipu1grp-2 { /* parallel camera */ +- fsl,pins = < +- MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000 +- MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000 +- MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000 +- MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000 +- MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000 +- MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000 +- MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000 +- MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000 +- MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000 +- MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000 +- MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000 +- MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000 +- >; +- }; +- +- pinctrl_ipu1_3: ipu1grp-3 { /* parallel port 16-bit */ +- fsl,pins = < +- MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x80000000 +- MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x80000000 +- MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x80000000 +- MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x80000000 +- MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x80000000 +- MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x80000000 +- MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x80000000 +- MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x80000000 +- MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000 +- MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000 +- MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000 +- MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000 +- MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000 +- MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000 +- MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000 +- MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000 +- MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000 +- MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000 +- MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000 +- >; +- }; +- }; +- +- mlb { +- pinctrl_mlb_1: mlbgrp-1 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_3__MLB_CLK 0x71 +- MX6QDL_PAD_GPIO_6__MLB_SIG 0x71 +- MX6QDL_PAD_GPIO_2__MLB_DATA 0x71 +- >; +- }; +- +- pinctrl_mlb_2: mlbgrp-2 { +- fsl,pins = < +- MX6QDL_PAD_ENET_TXD1__MLB_CLK 0x71 +- MX6QDL_PAD_GPIO_6__MLB_SIG 0x71 +- MX6QDL_PAD_GPIO_2__MLB_DATA 0x71 +- >; +- }; +- }; +- +- pwm0 { +- pinctrl_pwm0_1: pwm0grp-1 { +- fsl,pins = < +- MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 +- >; +- }; +- }; +- +- pwm3 { +- pinctrl_pwm3_1: pwm3grp-1 { +- fsl,pins = < +- MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 +- >; +- }; +- }; +- +- spdif { +- pinctrl_spdif_1: spdifgrp-1 { +- fsl,pins = < +- MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0 +- >; +- }; +- +- pinctrl_spdif_2: spdifgrp-2 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 +- MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x1b0b0 +- >; +- }; +- +- pinctrl_spdif_3: spdifgrp-3 { +- fsl,pins = < +- MX6QDL_PAD_ENET_RXD0__SPDIF_OUT 0x1b0b0 +- >; +- }; +- }; +- +- uart1 { +- pinctrl_uart1_1: uart1grp-1 { +- fsl,pins = < +- MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 +- MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 +- >; +- }; +- }; +- +- uart2 { +- pinctrl_uart2_1: uart2grp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 +- MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 +- >; +- }; +- +- pinctrl_uart2_2: uart2grp-2 { /* DTE mode */ +- fsl,pins = < +- MX6QDL_PAD_EIM_D26__UART2_RX_DATA 0x1b0b1 +- MX6QDL_PAD_EIM_D27__UART2_TX_DATA 0x1b0b1 +- MX6QDL_PAD_EIM_D28__UART2_DTE_CTS_B 0x1b0b1 +- MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B 0x1b0b1 +- >; +- }; +- }; +- +- uart3 { +- pinctrl_uart3_1: uart3grp-1 { +- fsl,pins = < +- MX6QDL_PAD_SD4_CLK__UART3_RX_DATA 0x1b0b1 +- MX6QDL_PAD_SD4_CMD__UART3_TX_DATA 0x1b0b1 +- MX6QDL_PAD_EIM_D30__UART3_CTS_B 0x1b0b1 +- MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1 +- >; +- }; +- +- pinctrl_uart3_2: uart3grp-2 { +- fsl,pins = < +- MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1 +- MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1 +- MX6QDL_PAD_EIM_D23__UART3_CTS_B 0x1b0b1 +- MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1 +- >; +- }; +- }; +- +- uart4 { +- pinctrl_uart4_1: uart4grp-1 { +- fsl,pins = < +- MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 +- MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 +- >; +- }; +- }; +- +- usbotg { +- pinctrl_usbotg_1: usbotggrp-1 { +- fsl,pins = < +- MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 +- >; +- }; +- +- pinctrl_usbotg_2: usbotggrp-2 { +- fsl,pins = < +- MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 +- >; +- }; +- }; +- +- usbh2 { +- pinctrl_usbh2_1: usbh2grp-1 { +- fsl,pins = < +- MX6QDL_PAD_RGMII_TXC__USB_H2_DATA 0x40013030 +- MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x40013030 +- >; +- }; +- +- pinctrl_usbh2_2: usbh2grp-2 { +- fsl,pins = < +- MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x40017030 +- >; +- }; +- }; +- +- usbh3 { +- pinctrl_usbh3_1: usbh3grp-1 { +- fsl,pins = < +- MX6QDL_PAD_RGMII_RX_CTL__USB_H3_DATA 0x40013030 +- MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x40013030 +- >; +- }; +- +- pinctrl_usbh3_2: usbh3grp-2 { +- fsl,pins = < +- MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x40017030 +- >; +- }; +- }; +- +- usdhc1 { +- pinctrl_usdhc1_1: usdhc1grp-1 { +- fsl,pins = < +- MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 +- MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 +- MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 +- MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 +- MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 +- MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 +- MX6QDL_PAD_NANDF_D0__SD1_DATA4 0x17059 +- MX6QDL_PAD_NANDF_D1__SD1_DATA5 0x17059 +- MX6QDL_PAD_NANDF_D2__SD1_DATA6 0x17059 +- MX6QDL_PAD_NANDF_D3__SD1_DATA7 0x17059 +- >; +- }; +- +- pinctrl_usdhc1_2: usdhc1grp-2 { +- fsl,pins = < +- MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 +- MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 +- MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 +- MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 +- MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 +- MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 +- >; +- }; +- }; +- +- usdhc2 { +- pinctrl_usdhc2_1: usdhc2grp-1 { +- fsl,pins = < +- MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 +- MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 +- MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 +- MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 +- MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 +- MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 +- MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x17059 +- MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x17059 +- MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x17059 +- MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x17059 +- >; +- }; +- +- pinctrl_usdhc2_2: usdhc2grp-2 { +- fsl,pins = < +- MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 +- MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 +- MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 +- MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 +- MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 +- MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 +- >; +- }; +- }; +- +- usdhc3 { +- pinctrl_usdhc3_1: usdhc3grp-1 { +- fsl,pins = < +- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 +- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 +- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 +- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 +- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 +- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 +- MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 +- MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 +- MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 +- MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 +- >; +- }; +- +- pinctrl_usdhc3_1_100mhz: usdhc3grp-1-100mhz { /* 100Mhz */ +- fsl,pins = < +- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170b9 +- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100b9 +- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170b9 +- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170b9 +- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170b9 +- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170b9 +- MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170b9 +- MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170b9 +- MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170b9 +- MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170b9 +- >; +- }; +- +- pinctrl_usdhc3_1_200mhz: usdhc3grp-1-200mhz { /* 200Mhz */ +- fsl,pins = < +- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170f9 +- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100f9 +- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170f9 +- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170f9 +- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170f9 +- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170f9 +- MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170f9 +- MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170f9 +- MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170f9 +- MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170f9 +- >; +- }; +- +- pinctrl_usdhc3_2: usdhc3grp-2 { +- fsl,pins = < +- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 +- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 +- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 +- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 +- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 +- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 +- >; +- }; +- }; +- +- usdhc4 { +- pinctrl_usdhc4_1: usdhc4grp-1 { +- fsl,pins = < +- MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 +- MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 +- MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 +- MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 +- MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 +- MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 +- MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 +- MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 +- MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 +- MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 +- >; +- }; +- +- pinctrl_usdhc4_2: usdhc4grp-2 { +- fsl,pins = < +- MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 +- MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 +- MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 +- MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 +- MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 +- MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 +- >; +- }; +- }; +- +- weim { +- pinctrl_weim_cs0_1: weim_cs0grp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_CS0__EIM_CS0_B 0xb0b1 +- >; +- }; +- +- pinctrl_weim_nor_1: weim_norgrp-1 { +- fsl,pins = < +- MX6QDL_PAD_EIM_OE__EIM_OE_B 0xb0b1 +- MX6QDL_PAD_EIM_RW__EIM_RW 0xb0b1 +- MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B 0xb060 +- /* data */ +- MX6QDL_PAD_EIM_D16__EIM_DATA16 0x1b0b0 +- MX6QDL_PAD_EIM_D17__EIM_DATA17 0x1b0b0 +- MX6QDL_PAD_EIM_D18__EIM_DATA18 0x1b0b0 +- MX6QDL_PAD_EIM_D19__EIM_DATA19 0x1b0b0 +- MX6QDL_PAD_EIM_D20__EIM_DATA20 0x1b0b0 +- MX6QDL_PAD_EIM_D21__EIM_DATA21 0x1b0b0 +- MX6QDL_PAD_EIM_D22__EIM_DATA22 0x1b0b0 +- MX6QDL_PAD_EIM_D23__EIM_DATA23 0x1b0b0 +- MX6QDL_PAD_EIM_D24__EIM_DATA24 0x1b0b0 +- MX6QDL_PAD_EIM_D25__EIM_DATA25 0x1b0b0 +- MX6QDL_PAD_EIM_D26__EIM_DATA26 0x1b0b0 +- MX6QDL_PAD_EIM_D27__EIM_DATA27 0x1b0b0 +- MX6QDL_PAD_EIM_D28__EIM_DATA28 0x1b0b0 +- MX6QDL_PAD_EIM_D29__EIM_DATA29 0x1b0b0 +- MX6QDL_PAD_EIM_D30__EIM_DATA30 0x1b0b0 +- MX6QDL_PAD_EIM_D31__EIM_DATA31 0x1b0b0 +- /* address */ +- MX6QDL_PAD_EIM_A23__EIM_ADDR23 0xb0b1 +- MX6QDL_PAD_EIM_A22__EIM_ADDR22 0xb0b1 +- MX6QDL_PAD_EIM_A21__EIM_ADDR21 0xb0b1 +- MX6QDL_PAD_EIM_A20__EIM_ADDR20 0xb0b1 +- MX6QDL_PAD_EIM_A19__EIM_ADDR19 0xb0b1 +- MX6QDL_PAD_EIM_A18__EIM_ADDR18 0xb0b1 +- MX6QDL_PAD_EIM_A17__EIM_ADDR17 0xb0b1 +- MX6QDL_PAD_EIM_A16__EIM_ADDR16 0xb0b1 +- MX6QDL_PAD_EIM_DA15__EIM_AD15 0xb0b1 +- MX6QDL_PAD_EIM_DA14__EIM_AD14 0xb0b1 +- MX6QDL_PAD_EIM_DA13__EIM_AD13 0xb0b1 +- MX6QDL_PAD_EIM_DA12__EIM_AD12 0xb0b1 +- MX6QDL_PAD_EIM_DA11__EIM_AD11 0xb0b1 +- MX6QDL_PAD_EIM_DA10__EIM_AD10 0xb0b1 +- MX6QDL_PAD_EIM_DA9__EIM_AD09 0xb0b1 +- MX6QDL_PAD_EIM_DA8__EIM_AD08 0xb0b1 +- MX6QDL_PAD_EIM_DA7__EIM_AD07 0xb0b1 +- MX6QDL_PAD_EIM_DA6__EIM_AD06 0xb0b1 +- MX6QDL_PAD_EIM_DA5__EIM_AD05 0xb0b1 +- MX6QDL_PAD_EIM_DA4__EIM_AD04 0xb0b1 +- MX6QDL_PAD_EIM_DA3__EIM_AD03 0xb0b1 +- MX6QDL_PAD_EIM_DA2__EIM_AD02 0xb0b1 +- MX6QDL_PAD_EIM_DA1__EIM_AD01 0xb0b1 +- MX6QDL_PAD_EIM_DA0__EIM_AD00 0xb0b1 +- >; +- }; +- }; + }; + + ldb: ldb@020e0008 { +- #address-cells = <1>; +- #size-cells = <0>; + compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb"; +- gpr = <&gpr>; ++ reg = <0x020e0000 0x4000>; ++ clocks = <&clks 135>, <&clks 136>, ++ <&clks 39>, <&clks 40>, ++ <&clks 41>, <&clks 42>, ++ <&clks 184>, <&clks 185>, ++ <&clks 210>, <&clks 211>, ++ <&clks 212>, <&clks 213>; ++ clock-names = "ldb_di0", "ldb_di1", ++ "ipu1_di0_sel", "ipu1_di1_sel", ++ "ipu2_di0_sel", "ipu2_di1_sel", ++ "di0_div_3_5", "di1_div_3_5", ++ "di0_div_7", "di1_div_7", ++ "di0_div_sel", "di1_div_sel"; + status = "disabled"; +- +- lvds-channel@0 { +- reg = <0>; +- status = "disabled"; +- }; +- +- lvds-channel@1 { +- reg = <1>; +- status = "disabled"; +- }; + }; + + dcic1: dcic@020e4000 { + reg = <0x020e4000 0x4000>; +- interrupts = <0 124 0x04>; ++ interrupts = <0 124 IRQ_TYPE_LEVEL_HIGH>; + }; + + dcic2: dcic@020e8000 { + reg = <0x020e8000 0x4000>; +- interrupts = <0 125 0x04>; ++ interrupts = <0 125 IRQ_TYPE_LEVEL_HIGH>; + }; + + sdma: sdma@020ec000 { + compatible = "fsl,imx6q-sdma", "fsl,imx35-sdma"; + reg = <0x020ec000 0x4000>; +- interrupts = <0 2 0x04>; ++ interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 155>, <&clks 155>; + clock-names = "ipg", "ahb"; + #dma-cells = <3>; +@@ -1396,9 +772,29 @@ + reg = <0x02100000 0x100000>; + ranges; + +- caam@02100000 { +- reg = <0x02100000 0x40000>; +- interrupts = <0 105 0x04 0 106 0x04>; ++ crypto: caam@02100000 { ++ compatible = "fsl,sec-v4.0"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x2100000 0x40000>; ++ ranges = <0 0x2100000 0x40000>; ++ interrupt-parent = <&intc>; /* interrupts = <0 92 0x4>; */ ++ clocks = <&clks 214>, <&clks 215>, <&clks 216>, <&clks 196>; ++ clock-names = "caam_mem", "caam_aclk", "caam_ipg", "caam_emi_slow"; ++ ++ sec_jr0: jr0@1000 { ++ compatible = "fsl,sec-v4.0-job-ring"; ++ reg = <0x1000 0x1000>; ++ interrupt-parent = <&intc>; ++ interrupts = <0 105 IRQ_TYPE_LEVEL_HIGH>; ++ }; ++ ++ sec_jr1: jr1@2000 { ++ compatible = "fsl,sec-v4.0-job-ring"; ++ reg = <0x2000 0x1000>; ++ interrupt-parent = <&intc>; ++ interrupts = <0 106 IRQ_TYPE_LEVEL_HIGH>; ++ }; + }; + + aipstz@0217c000 { /* AIPSTZ2 */ +@@ -1408,7 +804,7 @@ + usbotg: usb@02184000 { + compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + reg = <0x02184000 0x200>; +- interrupts = <0 43 0x04>; ++ interrupts = <0 43 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 162>; + fsl,usbphy = <&usbphy1>; + fsl,usbmisc = <&usbmisc 0>; +@@ -1418,7 +814,7 @@ + usbh1: usb@02184200 { + compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + reg = <0x02184200 0x200>; +- interrupts = <0 40 0x04>; ++ interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 162>; + fsl,usbphy = <&usbphy2>; + fsl,usbmisc = <&usbmisc 1>; +@@ -1428,18 +824,24 @@ + usbh2: usb@02184400 { + compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + reg = <0x02184400 0x200>; +- interrupts = <0 41 0x04>; ++ interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 162>; + fsl,usbmisc = <&usbmisc 2>; ++ phy_type = "hsic"; ++ fsl,usbphy = <&usbphy_nop1>; ++ fsl,anatop = <&anatop>; + status = "disabled"; + }; + + usbh3: usb@02184600 { + compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + reg = <0x02184600 0x200>; +- interrupts = <0 42 0x04>; ++ interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 162>; + fsl,usbmisc = <&usbmisc 3>; ++ phy_type = "hsic"; ++ fsl,usbphy = <&usbphy_nop2>; ++ fsl,anatop = <&anatop>; + status = "disabled"; + }; + +@@ -1453,7 +855,9 @@ + fec: ethernet@02188000 { + compatible = "fsl,imx6q-fec"; + reg = <0x02188000 0x4000>; +- interrupts = <0 118 0x04 0 119 0x04>; ++ interrupts-extended = ++ <&intc 0 118 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 117>, <&clks 117>, <&clks 190>; + clock-names = "ipg", "ahb", "ptp"; + status = "disabled"; +@@ -1461,13 +865,15 @@ + + mlb@0218c000 { + reg = <0x0218c000 0x4000>; +- interrupts = <0 53 0x04 0 117 0x04 0 126 0x04>; ++ interrupts = <0 53 IRQ_TYPE_LEVEL_HIGH>, ++ <0 117 IRQ_TYPE_LEVEL_HIGH>, ++ <0 126 IRQ_TYPE_LEVEL_HIGH>; + }; + + usdhc1: usdhc@02190000 { + compatible = "fsl,imx6q-usdhc"; + reg = <0x02190000 0x4000>; +- interrupts = <0 22 0x04>; ++ interrupts = <0 22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 163>, <&clks 163>, <&clks 163>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; +@@ -1477,7 +883,7 @@ + usdhc2: usdhc@02194000 { + compatible = "fsl,imx6q-usdhc"; + reg = <0x02194000 0x4000>; +- interrupts = <0 23 0x04>; ++ interrupts = <0 23 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 164>, <&clks 164>, <&clks 164>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; +@@ -1487,7 +893,7 @@ + usdhc3: usdhc@02198000 { + compatible = "fsl,imx6q-usdhc"; + reg = <0x02198000 0x4000>; +- interrupts = <0 24 0x04>; ++ interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 165>, <&clks 165>, <&clks 165>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; +@@ -1497,7 +903,7 @@ + usdhc4: usdhc@0219c000 { + compatible = "fsl,imx6q-usdhc"; + reg = <0x0219c000 0x4000>; +- interrupts = <0 25 0x04>; ++ interrupts = <0 25 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 166>, <&clks 166>, <&clks 166>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; +@@ -1509,7 +915,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; + reg = <0x021a0000 0x4000>; +- interrupts = <0 36 0x04>; ++ interrupts = <0 36 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 125>; + status = "disabled"; + }; +@@ -1519,7 +925,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; + reg = <0x021a4000 0x4000>; +- interrupts = <0 37 0x04>; ++ interrupts = <0 37 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 126>; + status = "disabled"; + }; +@@ -1529,7 +935,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; + reg = <0x021a8000 0x4000>; +- interrupts = <0 38 0x04>; ++ interrupts = <0 38 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 127>; + status = "disabled"; + }; +@@ -1538,6 +944,11 @@ + reg = <0x021ac000 0x4000>; + }; + ++ mmdc0-1@021b0000 { /* MMDC0-1 */ ++ compatible = "fsl,imx6q-mmdc-combine"; ++ reg = <0x021b0000 0x8000>; ++ }; ++ + mmdc0: mmdc@021b0000 { /* MMDC0 */ + compatible = "fsl,imx6q-mmdc"; + reg = <0x021b0000 0x4000>; +@@ -1550,23 +961,29 @@ + weim: weim@021b8000 { + compatible = "fsl,imx6q-weim"; + reg = <0x021b8000 0x4000>; +- interrupts = <0 14 0x04>; ++ interrupts = <0 14 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 196>; + }; + +- ocotp: ocotp@021bc000 { +- compatible = "fsl,imx6q-ocotp", "syscon"; ++ ocotp: ocotp-ctrl@021bc000 { ++ compatible = "syscon"; + reg = <0x021bc000 0x4000>; + }; + ++ ocotp-fuse@021bc000 { ++ compatible = "fsl,imx6q-ocotp"; ++ reg = <0x021bc000 0x4000>; ++ clocks = <&clks 128>; ++ }; ++ + tzasc@021d0000 { /* TZASC1 */ + reg = <0x021d0000 0x4000>; +- interrupts = <0 108 0x04>; ++ interrupts = <0 108 IRQ_TYPE_LEVEL_HIGH>; + }; + + tzasc@021d4000 { /* TZASC2 */ + reg = <0x021d4000 0x4000>; +- interrupts = <0 109 0x04>; ++ interrupts = <0 109 IRQ_TYPE_LEVEL_HIGH>; + }; + + audmux: audmux@021d8000 { +@@ -1575,23 +992,32 @@ + status = "disabled"; + }; + +- mipi@021dc000 { /* MIPI-CSI */ ++ mipi_csi: mipi_csi@021dc000 { ++ compatible = "fsl,imx6q-mipi-csi2"; + reg = <0x021dc000 0x4000>; +- }; +- +- mipi@021e0000 { /* MIPI-DSI */ +- reg = <0x021e0000 0x4000>; ++ interrupts = <0 100 0x04>, <0 101 0x04>; ++ clocks = <&clks 138>, <&clks 53>, <&clks 204>; ++ /* Note: clks 138 is hsi_tx, however, the dphy_c ++ * hsi_tx and pll_refclk use the same clk gate. ++ * In current clk driver, open/close clk gate do ++ * use hsi_tx for a temporary debug purpose. ++ */ ++ clock-names = "dphy_clk", "pixel_clk", "cfg_clk"; ++ status = "disabled"; + }; + + vdoa@021e4000 { ++ compatible = "fsl,imx6q-vdoa"; + reg = <0x021e4000 0x4000>; +- interrupts = <0 18 0x04>; ++ interrupts = <0 18 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 202>; ++ iram = <&ocram>; + }; + + uart2: serial@021e8000 { + compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x021e8000 0x4000>; +- interrupts = <0 27 0x04>; ++ interrupts = <0 27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 160>, <&clks 161>; + clock-names = "ipg", "per"; + dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; +@@ -1602,7 +1028,7 @@ + uart3: serial@021ec000 { + compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x021ec000 0x4000>; +- interrupts = <0 28 0x04>; ++ interrupts = <0 28 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 160>, <&clks 161>; + clock-names = "ipg", "per"; + dmas = <&sdma 29 4 0>, <&sdma 30 4 0>; +@@ -1613,7 +1039,7 @@ + uart4: serial@021f0000 { + compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x021f0000 0x4000>; +- interrupts = <0 29 0x04>; ++ interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 160>, <&clks 161>; + clock-names = "ipg", "per"; + dmas = <&sdma 31 4 0>, <&sdma 32 4 0>; +@@ -1624,7 +1050,7 @@ + uart5: serial@021f4000 { + compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x021f4000 0x4000>; +- interrupts = <0 30 0x04>; ++ interrupts = <0 30 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 160>, <&clks 161>; + clock-names = "ipg", "per"; + dmas = <&sdma 33 4 0>, <&sdma 34 4 0>; +@@ -1634,13 +1060,18 @@ + }; + + ipu1: ipu@02400000 { +- #crtc-cells = <1>; + compatible = "fsl,imx6q-ipu"; + reg = <0x02400000 0x400000>; +- interrupts = <0 6 0x4 0 5 0x4>; +- clocks = <&clks 130>, <&clks 131>, <&clks 132>; +- clock-names = "bus", "di0", "di1"; ++ interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>, ++ <0 5 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 130>, <&clks 131>, <&clks 132>, ++ <&clks 39>, <&clks 40>, ++ <&clks 135>, <&clks 136>; ++ clock-names = "bus", "di0", "di1", ++ "di0_sel", "di1_sel", ++ "ldb_di0", "ldb_di1"; + resets = <&src 2>; ++ bypass_reset = <0>; + }; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,374 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/ { ++ /* these are used by bootloader for disabling nodes */ ++ aliases { ++ can0 = &can1; ++ ethernet0 = &fec; ++ led0 = &led0; ++ led1 = &led1; ++ nand = &gpmi; ++ usb0 = &usbh1; ++ usb1 = &usbotg; ++ }; ++ ++ chosen { ++ bootargs = "console=ttymxc1,115200"; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ led0: user1 { ++ label = "user1"; ++ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */ ++ default-state = "on"; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led1: user2 { ++ label = "user2"; ++ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */ ++ default-state = "off"; ++ }; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x20000000>; ++ }; ++ ++ pps { ++ compatible = "pps-gpio"; ++ gpios = <&gpio1 26 0>; ++ status = "okay"; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_3p3v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_5p0v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "5P0V"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_vbus: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio1 30 0>; ++ status = "okay"; ++}; ++ ++&gpmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ eeprom1: eeprom@50 { ++ compatible = "atmel,24c02"; ++ reg = <0x50>; ++ pagesize = <16>; ++ }; ++ ++ eeprom2: eeprom@51 { ++ compatible = "atmel,24c02"; ++ reg = <0x51>; ++ pagesize = <16>; ++ }; ++ ++ eeprom3: eeprom@52 { ++ compatible = "atmel,24c02"; ++ reg = <0x52>; ++ pagesize = <16>; ++ }; ++ ++ eeprom4: eeprom@53 { ++ compatible = "atmel,24c02"; ++ reg = <0x53>; ++ pagesize = <16>; ++ }; ++ ++ gpio: pca9555@23 { ++ compatible = "nxp,pca9555"; ++ reg = <0x23>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ hwmon: gsc@29 { ++ compatible = "gw,gsp"; ++ reg = <0x29>; ++ }; ++ ++ rtc: ds1672@68 { ++ compatible = "dallas,ds1672"; ++ reg = <0x68>; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ pmic: ltc3676@3c { ++ compatible = "ltc,ltc3676"; ++ reg = <0x3c>; ++ ++ regulators { ++ sw1_reg: ltc3676__sw1 { ++ regulator-min-microvolt = <1175000>; ++ regulator-max-microvolt = <1175000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw2_reg: ltc3676__sw2 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3_reg: ltc3676__sw3 { ++ regulator-min-microvolt = <1175000>; ++ regulator-max-microvolt = <1175000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: ltc3676__sw4 { ++ regulator-min-microvolt = <1500000>; ++ regulator-max-microvolt = <1500000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo2_reg: ltc3676__ldo2 { ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo4_reg: ltc3676__ldo4 { ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ status = "okay"; ++ ++ videoin: adv7180@20 { ++ compatible = "adi,adv7180"; ++ reg = <0x20>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6qdl-gw51xx { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x80000000 /* MEZZ_DIO0 */ ++ MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x80000000 /* MEZZ_DIO1 */ ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */ ++ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */ ++ MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 /* PHY Reset */ ++ MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x80000000 /* PCIE_RST# */ ++ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */ ++ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */ ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart3: uart3grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart5: uart5grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ }; ++}; ++ ++&pcie { ++ reset-gpio = <&gpio1 0 0>; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&uart3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart3>; ++ status = "okay"; ++}; ++ ++&uart5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart5>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,527 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/ { ++ /* these are used by bootloader for disabling nodes */ ++ aliases { ++ ethernet0 = &fec; ++ led0 = &led0; ++ led1 = &led1; ++ led2 = &led2; ++ nand = &gpmi; ++ ssi0 = &ssi1; ++ usb0 = &usbh1; ++ usb1 = &usbotg; ++ usdhc2 = &usdhc3; ++ }; ++ ++ chosen { ++ bootargs = "console=ttymxc1,115200"; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm4 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ led0: user1 { ++ label = "user1"; ++ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */ ++ default-state = "on"; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led1: user2 { ++ label = "user2"; ++ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */ ++ default-state = "off"; ++ }; ++ ++ led2: user3 { ++ label = "user3"; ++ gpios = <&gpio4 15 1>; /* 111 - MX6_LOCLED# */ ++ default-state = "off"; ++ }; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x20000000>; ++ }; ++ ++ pps { ++ compatible = "pps-gpio"; ++ gpios = <&gpio1 26 0>; ++ status = "okay"; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_1p0v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "1P0V"; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ regulator-always-on; ++ }; ++ ++ /* remove this fixed regulator once ltc3676__sw2 driver available */ ++ reg_1p8v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "1P8V"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-always-on; ++ }; ++ ++ reg_3p3v: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_5p0v: regulator@3 { ++ compatible = "regulator-fixed"; ++ reg = <3>; ++ regulator-name = "5P0V"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_vbus: regulator@4 { ++ compatible = "regulator-fixed"; ++ reg = <4>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ sound { ++ compatible = "fsl,imx6q-sabrelite-sgtl5000", ++ "fsl,imx-audio-sgtl5000"; ++ model = "imx6q-sabrelite-sgtl5000"; ++ ssi-controller = <&ssi1>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ mux-int-port = <1>; ++ mux-ext-port = <4>; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux>; ++ status = "okay"; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio1 30 0>; ++ status = "okay"; ++}; ++ ++&gpmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ eeprom1: eeprom@50 { ++ compatible = "atmel,24c02"; ++ reg = <0x50>; ++ pagesize = <16>; ++ }; ++ ++ eeprom2: eeprom@51 { ++ compatible = "atmel,24c02"; ++ reg = <0x51>; ++ pagesize = <16>; ++ }; ++ ++ eeprom3: eeprom@52 { ++ compatible = "atmel,24c02"; ++ reg = <0x52>; ++ pagesize = <16>; ++ }; ++ ++ eeprom4: eeprom@53 { ++ compatible = "atmel,24c02"; ++ reg = <0x53>; ++ pagesize = <16>; ++ }; ++ ++ gpio: pca9555@23 { ++ compatible = "nxp,pca9555"; ++ reg = <0x23>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ hwmon: gsc@29 { ++ compatible = "gw,gsp"; ++ reg = <0x29>; ++ }; ++ ++ rtc: ds1672@68 { ++ compatible = "dallas,ds1672"; ++ reg = <0x68>; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ pciswitch: pex8609@3f { ++ compatible = "plx,pex8609"; ++ reg = <0x3f>; ++ }; ++ ++ pmic: ltc3676@3c { ++ compatible = "ltc,ltc3676"; ++ reg = <0x3c>; ++ ++ regulators { ++ sw1_reg: ltc3676__sw1 { ++ regulator-min-microvolt = <1175000>; ++ regulator-max-microvolt = <1175000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw2_reg: ltc3676__sw2 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3_reg: ltc3676__sw3 { ++ regulator-min-microvolt = <1175000>; ++ regulator-max-microvolt = <1175000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: ltc3676__sw4 { ++ regulator-min-microvolt = <1500000>; ++ regulator-max-microvolt = <1500000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo2_reg: ltc3676__ldo2 { ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo3_reg: ltc3676__ldo3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ ldo4_reg: ltc3676__ldo4 { ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ status = "okay"; ++ ++ accelerometer: fxos8700@1e { ++ compatible = "fsl,fxos8700"; ++ reg = <0x13>; ++ }; ++ ++ codec: sgtl5000@0a { ++ compatible = "fsl,sgtl5000"; ++ reg = <0x0a>; ++ clocks = <&clks 169>; ++ VDDA-supply = <®_1p8v>; ++ VDDIO-supply = <®_3p3v>; ++ }; ++ ++ touchscreen: egalax_ts@04 { ++ compatible = "eeti,egalax_ts"; ++ reg = <0x04>; ++ interrupt-parent = <&gpio7>; ++ interrupts = <12 2>; /* gpio7_12 active low */ ++ wakeup-gpios = <&gpio7 12 0>; ++ }; ++ ++ videoin: adv7180@20 { ++ compatible = "adi,adv7180"; ++ reg = <0x20>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6qdl-gw52xx { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x80000000 /* MEZZ_DIO0 */ ++ MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x80000000 /* MEZZ_DIO1 */ ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */ ++ MX6QDL_PAD_EIM_D31__GPIO3_IO31 0x80000000 /* VIDDEC_PDN# */ ++ MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 /* PHY Reset */ ++ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE_RST# */ ++ MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000 /* GPS_PWDN */ ++ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */ ++ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */ ++ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 /* USB_SEL_PCI */ ++ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* TOUCH_IRQ# */ ++ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */ ++ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */ ++ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */ ++ MX6QDL_PAD_SD2_CMD__GPIO1_IO11 0x80000000 /* LVDS_TCH# */ ++ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 /* SD3_CD# */ ++ MX6QDL_PAD_SD4_DAT3__GPIO2_IO11 0x80000000 /* UART2_EN# */ ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0 ++ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0 ++ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0 ++ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_pwm4: pwm4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart5: uart5grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++ ++ lvds-channel@0 { ++ fsl,data-mapping = "spwg"; ++ fsl,data-width = <18>; ++ status = "okay"; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: hsd100pxn1 { ++ clock-frequency = <65000000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hback-porch = <220>; ++ hfront-porch = <40>; ++ vback-porch = <21>; ++ vfront-porch = <7>; ++ hsync-len = <60>; ++ vsync-len = <10>; ++ }; ++ }; ++ }; ++}; ++ ++&pcie { ++ reset-gpio = <&gpio1 29 0>; ++ status = "okay"; ++}; ++ ++&pwm4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm4>; ++ status = "okay"; ++}; ++ ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&uart5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart5>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ cd-gpios = <&gpio7 0 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,572 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/ { ++ /* these are used by bootloader for disabling nodes */ ++ aliases { ++ can0 = &can1; ++ ethernet0 = &fec; ++ ethernet1 = ð1; ++ led0 = &led0; ++ led1 = &led1; ++ led2 = &led2; ++ nand = &gpmi; ++ sky2 = ð1; ++ ssi0 = &ssi1; ++ usb0 = &usbh1; ++ usb1 = &usbotg; ++ usdhc2 = &usdhc3; ++ }; ++ ++ chosen { ++ bootargs = "console=ttymxc1,115200"; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm4 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ led0: user1 { ++ label = "user1"; ++ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */ ++ default-state = "on"; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led1: user2 { ++ label = "user2"; ++ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */ ++ default-state = "off"; ++ }; ++ ++ led2: user3 { ++ label = "user3"; ++ gpios = <&gpio4 15 1>; /* 111 -> MX6_LOCLED# */ ++ default-state = "off"; ++ }; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x40000000>; ++ }; ++ ++ pps { ++ compatible = "pps-gpio"; ++ gpios = <&gpio1 26 0>; ++ status = "okay"; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_1p0v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "1P0V"; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ regulator-always-on; ++ }; ++ ++ /* remove when pmic 1p8 regulator available */ ++ reg_1p8v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "1P8V"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-always-on; ++ }; ++ ++ reg_3p3v: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_h1_vbus: regulator@3 { ++ compatible = "regulator-fixed"; ++ reg = <3>; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_vbus: regulator@4 { ++ compatible = "regulator-fixed"; ++ reg = <4>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ sound { ++ compatible = "fsl,imx6q-sabrelite-sgtl5000", ++ "fsl,imx-audio-sgtl5000"; ++ model = "imx6q-sabrelite-sgtl5000"; ++ ssi-controller = <&ssi1>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ mux-int-port = <1>; ++ mux-ext-port = <4>; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux>; ++ status = "okay"; ++}; ++ ++&can1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_flexcan1>; ++ status = "okay"; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio1 30 0>; ++ status = "okay"; ++}; ++ ++&gpmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ eeprom1: eeprom@50 { ++ compatible = "atmel,24c02"; ++ reg = <0x50>; ++ pagesize = <16>; ++ }; ++ ++ eeprom2: eeprom@51 { ++ compatible = "atmel,24c02"; ++ reg = <0x51>; ++ pagesize = <16>; ++ }; ++ ++ eeprom3: eeprom@52 { ++ compatible = "atmel,24c02"; ++ reg = <0x52>; ++ pagesize = <16>; ++ }; ++ ++ eeprom4: eeprom@53 { ++ compatible = "atmel,24c02"; ++ reg = <0x53>; ++ pagesize = <16>; ++ }; ++ ++ gpio: pca9555@23 { ++ compatible = "nxp,pca9555"; ++ reg = <0x23>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ hwmon: gsc@29 { ++ compatible = "gw,gsp"; ++ reg = <0x29>; ++ }; ++ ++ rtc: ds1672@68 { ++ compatible = "dallas,ds1672"; ++ reg = <0x68>; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ pciclkgen: si53156@6b { ++ compatible = "sil,si53156"; ++ reg = <0x6b>; ++ }; ++ ++ pciswitch: pex8606@3f { ++ compatible = "plx,pex8606"; ++ reg = <0x3f>; ++ }; ++ ++ pmic: ltc3676@3c { ++ compatible = "ltc,ltc3676"; ++ reg = <0x3c>; ++ ++ regulators { ++ /* VDD_SOC */ ++ sw1_reg: ltc3676__sw1 { ++ regulator-min-microvolt = <1175000>; ++ regulator-max-microvolt = <1175000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ /* VDD_1P8 */ ++ sw2_reg: ltc3676__sw2 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ /* VDD_ARM */ ++ sw3_reg: ltc3676__sw3 { ++ regulator-min-microvolt = <1175000>; ++ regulator-max-microvolt = <1175000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ /* VDD_DDR */ ++ sw4_reg: ltc3676__sw4 { ++ regulator-min-microvolt = <1500000>; ++ regulator-max-microvolt = <1500000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ /* VDD_2P5 */ ++ ldo2_reg: ltc3676__ldo2 { ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ /* VDD_1P8 */ ++ ldo3_reg: ltc3676__ldo3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ /* VDD_HIGH */ ++ ldo4_reg: ltc3676__ldo4 { ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ status = "okay"; ++ ++ accelerometer: fxos8700@1e { ++ compatible = "fsl,fxos8700"; ++ reg = <0x1e>; ++ }; ++ ++ codec: sgtl5000@0a { ++ compatible = "fsl,sgtl5000"; ++ reg = <0x0a>; ++ clocks = <&clks 201>; ++ VDDA-supply = <®_1p8v>; ++ VDDIO-supply = <®_3p3v>; ++ }; ++ ++ hdmiin: adv7611@4c { ++ compatible = "adi,adv7611"; ++ reg = <0x4c>; ++ }; ++ ++ touchscreen: egalax_ts@04 { ++ compatible = "eeti,egalax_ts"; ++ reg = <0x04>; ++ interrupt-parent = <&gpio1>; ++ interrupts = <11 2>; /* gpio1_11 active low */ ++ wakeup-gpios = <&gpio1 11 0>; ++ }; ++ ++ videoout: adv7393@2a { ++ compatible = "adi,adv7393"; ++ reg = <0x2a>; ++ }; ++ ++ videoin: adv7180@20 { ++ compatible = "adi,adv7180"; ++ reg = <0x20>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6qdl-gw53xx { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x80000000 /* PCIE6EXP_DIO0 */ ++ MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x80000000 /* PCIE6EXP_DIO1 */ ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */ ++ MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000 /* GPS_SHDN */ ++ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */ ++ MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 /* PCIE IRQ */ ++ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE RST */ ++ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */ ++ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 /* CAN_STBY */ ++ MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x80000000 /* PMIC_IRQ# */ ++ MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x80000000 /* HUB_RST# */ ++ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* PCIE_WDIS# */ ++ MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x80000000 /* ACCEL_IRQ# */ ++ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */ ++ MX6QDL_PAD_KEY_COL4__GPIO4_IO14 0x80000000 /* USBOTG_OC# */ ++ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */ ++ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */ ++ MX6QDL_PAD_SD2_CMD__GPIO1_IO11 0x80000000 /* TOUCH_IRQ# */ ++ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 /* SD3_DET# */ ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0 ++ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0 ++ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0 ++ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_flexcan1: flexcan1grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000 ++ MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_pwm4: pwm4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart5: uart5grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++ ++ lvds-channel@1 { ++ fsl,data-mapping = "spwg"; ++ fsl,data-width = <18>; ++ status = "okay"; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: hsd100pxn1 { ++ clock-frequency = <65000000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hback-porch = <220>; ++ hfront-porch = <40>; ++ vback-porch = <21>; ++ vfront-porch = <7>; ++ hsync-len = <60>; ++ vsync-len = <10>; ++ }; ++ }; ++ }; ++}; ++ ++&pcie { ++ reset-gpio = <&gpio1 29 0>; ++ status = "okay"; ++ ++ eth1: sky2@8 { /* MAC/PHY on bus 8 */ ++ compatible = "marvell,sky2"; ++ }; ++}; ++ ++&pwm4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm4>; ++ status = "okay"; ++}; ++ ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&uart5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart5>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ vbus-supply = <®_usb_h1_vbus>; ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ cd-gpios = <&gpio7 0 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,599 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/ { ++ /* these are used by bootloader for disabling nodes */ ++ aliases { ++ can0 = &can1; ++ ethernet0 = &fec; ++ ethernet1 = ð1; ++ led0 = &led0; ++ led1 = &led1; ++ led2 = &led2; ++ nand = &gpmi; ++ sky2 = ð1; ++ ssi0 = &ssi1; ++ usb0 = &usbh1; ++ usb1 = &usbotg; ++ usdhc2 = &usdhc3; ++ }; ++ ++ chosen { ++ bootargs = "console=ttymxc1,115200"; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm4 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ led0: user1 { ++ label = "user1"; ++ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */ ++ default-state = "on"; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led1: user2 { ++ label = "user2"; ++ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */ ++ default-state = "off"; ++ }; ++ ++ led2: user3 { ++ label = "user3"; ++ gpios = <&gpio4 15 1>; /* 111 -> MX6_LOCLED# */ ++ default-state = "off"; ++ }; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x40000000>; ++ }; ++ ++ pps { ++ compatible = "pps-gpio"; ++ gpios = <&gpio1 26 0>; ++ status = "okay"; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_1p0v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "1P0V"; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ regulator-always-on; ++ }; ++ ++ reg_3p3v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_h1_vbus: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_vbus: regulator@3 { ++ compatible = "regulator-fixed"; ++ reg = <3>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ sound { ++ compatible = "fsl,imx6q-sabrelite-sgtl5000", ++ "fsl,imx-audio-sgtl5000"; ++ model = "imx6q-sabrelite-sgtl5000"; ++ ssi-controller = <&ssi1>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ mux-int-port = <1>; ++ mux-ext-port = <4>; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux>; /* AUD4<->sgtl5000 */ ++ status = "okay"; ++}; ++ ++&can1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_flexcan1>; ++ status = "okay"; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio1 30 0>; ++ status = "okay"; ++}; ++ ++&gpmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ eeprom1: eeprom@50 { ++ compatible = "atmel,24c02"; ++ reg = <0x50>; ++ pagesize = <16>; ++ }; ++ ++ eeprom2: eeprom@51 { ++ compatible = "atmel,24c02"; ++ reg = <0x51>; ++ pagesize = <16>; ++ }; ++ ++ eeprom3: eeprom@52 { ++ compatible = "atmel,24c02"; ++ reg = <0x52>; ++ pagesize = <16>; ++ }; ++ ++ eeprom4: eeprom@53 { ++ compatible = "atmel,24c02"; ++ reg = <0x53>; ++ pagesize = <16>; ++ }; ++ ++ gpio: pca9555@23 { ++ compatible = "nxp,pca9555"; ++ reg = <0x23>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ hwmon: gsc@29 { ++ compatible = "gw,gsp"; ++ reg = <0x29>; ++ }; ++ ++ rtc: ds1672@68 { ++ compatible = "dallas,ds1672"; ++ reg = <0x68>; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3950000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen2_reg: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen3_reg: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++ ++ pciswitch: pex8609@3f { ++ compatible = "plx,pex8609"; ++ reg = <0x3f>; ++ }; ++ ++ pciclkgen: si52147@6b { ++ compatible = "sil,si52147"; ++ reg = <0x6b>; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ status = "okay"; ++ ++ accelerometer: fxos8700@1e { ++ compatible = "fsl,fxos8700"; ++ reg = <0x1e>; ++ }; ++ ++ codec: sgtl5000@0a { ++ compatible = "fsl,sgtl5000"; ++ reg = <0x0a>; ++ clocks = <&clks 201>; ++ VDDA-supply = <&sw4_reg>; ++ VDDIO-supply = <®_3p3v>; ++ }; ++ ++ hdmiin: adv7611@4c { ++ compatible = "adi,adv7611"; ++ reg = <0x4c>; ++ }; ++ ++ touchscreen: egalax_ts@04 { ++ compatible = "eeti,egalax_ts"; ++ reg = <0x04>; ++ interrupt-parent = <&gpio7>; ++ interrupts = <12 2>; /* gpio7_12 active low */ ++ wakeup-gpios = <&gpio7 12 0>; ++ }; ++ ++ videoout: adv7393@2a { ++ compatible = "adi,adv7393"; ++ reg = <0x2a>; ++ }; ++ ++ videoin: adv7180@20 { ++ compatible = "adi,adv7180"; ++ reg = <0x20>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6qdl-gw54xx { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */ ++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 /* SPINOR_CS0# */ ++ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */ ++ MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 /* PCIE IRQ */ ++ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE RST */ ++ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */ ++ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 /* CAN_STBY */ ++ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* TOUCH_IRQ# */ ++ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */ ++ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */ ++ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */ ++ MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000 /* USBHUB_RST# */ ++ MX6QDL_PAD_SD1_DAT3__GPIO1_IO21 0x80000000 /* MIPI_DIO */ ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0 ++ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0 ++ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0 ++ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_flexcan1: flexcan1grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000 ++ MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_pwm4: pwm4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart5: uart5grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++ ++ lvds-channel@1 { ++ fsl,data-mapping = "spwg"; ++ fsl,data-width = <18>; ++ status = "okay"; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: hsd100pxn1 { ++ clock-frequency = <65000000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hback-porch = <220>; ++ hfront-porch = <40>; ++ vback-porch = <21>; ++ vfront-porch = <7>; ++ hsync-len = <60>; ++ vsync-len = <10>; ++ }; ++ }; ++ }; ++}; ++ ++&pcie { ++ reset-gpio = <&gpio1 29 0>; ++ status = "okay"; ++ ++ eth1: sky2@8 { /* MAC/PHY on bus 8 */ ++ compatible = "marvell,sky2"; ++ }; ++}; ++ ++&pwm4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm4>; ++ status = "okay"; ++}; ++ ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&ssi2 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&uart5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart5>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ vbus-supply = <®_usb_h1_vbus>; ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ cd-gpios = <&gpio7 0 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,367 @@ ++/* ++ * Copyright (C) 2013,2014 Russell King ++ */ ++#include "imx6qdl-microsom.dtsi" ++#include "imx6qdl-microsom-ar8035.dtsi" ++ ++/ { ++ chosen { ++ bootargs = "quiet console=ttymxc0,115200 root=/dev/mmcblk0p2 rw"; ++ }; ++ ++ aliases { ++ mxcfb0 = &mxcfb1; ++ }; ++ ++ ir_recv: ir-receiver { ++ compatible = "gpio-ir-receiver"; ++ gpios = <&gpio3 5 1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_gpio3_5>; ++ linux,rc-map-name = "rc-rc6-mce"; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ ++ reg_3p3v: 3p3v { ++ compatible = "regulator-fixed"; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usbh1_vbus: usb-h1-vbus { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio1 0 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_usbh1_vbus>; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ ++ reg_usbotg_vbus: usb-otg-vbus { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio3 22 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_usbotg_vbus>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ }; ++ ++ sound-sgtl5000 { ++ audio-codec = <&sgtl5000>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ compatible = "fsl,imx-audio-sgtl5000"; ++ model = "On-board Codec"; ++ mux-ext-port = <5>; ++ mux-int-port = <1>; ++ ssi-controller = <&ssi1>; ++ }; ++ ++ sound-spdif { ++ compatible = "fsl,imx-audio-spdif"; ++ model = "imx-spdif"; ++ spdif-controller = <&spdif>; ++ spdif-out; ++ }; ++ ++ sound-hdmi { ++ compatible = "fsl,imx6q-audio-hdmi", ++ "fsl,imx-audio-hdmi"; ++ model = "imx-audio-hdmi"; ++ hdmi-controller = <&hdmi_audio>; ++ }; ++ ++ mxcfb1: fb@0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <32>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "okay"; ++ }; ++}; ++ ++&hdmi_core { ++ ipu_id = <0>; ++ disp_id = <0>; ++ status = "okay"; ++}; ++ ++&hdmi_video { ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ status = "okay"; ++}; ++ ++&hdmi_audio { ++ status = "okay"; ++}; ++ ++&hdmi_cec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_hdmi>; ++ status = "okay"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_i2c2>; ++ status = "okay"; ++ ++ ddc: imx6_hdmi_i2c@50 { ++ compatible = "fsl,imx6-hdmi-i2c"; ++ reg = <0x50>; ++ }; ++}; ++ ++&audmux { ++ status = "okay"; ++}; ++ ++&can1 { ++ pinctrl-names = "default"; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_i2c1>; ++ status = "okay"; ++ ++ /* Pro model */ ++ rtc: pcf8523@68 { ++ compatible = "nxp,pcf8523"; ++ reg = <0x68>; ++ }; ++ ++ /* Pro model */ ++ sgtl5000: sgtl5000@0a { ++ clocks = <&clks 201>; ++ compatible = "fsl,sgtl5000"; ++ pinctrl-0 = <&pinctrl_hummingboard_sgtl5000>; ++ pinctrl-names = "default"; ++ reg = <0x0a>; ++ VDDA-supply = <®_3p3v>; ++ VDDIO-supply = <®_3p3v>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ hummingboard { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ /* ++ * 26 pin header GPIO description. The pins ++ * numbering as following - ++ * GPIO number | GPIO (bank,num) | PIN number ++ * ------------+-----------------+------------ ++ * gpio1 | (1,1) | IO7 ++ * gpio73 | (3,9) | IO11 ++ * gpio72 | (3,8) | IO12 ++ * gpio71 | (3,7) | IO13 ++ * gpio70 | (3,6) | IO15 ++ * gpio194 | (7,2) | IO16 ++ * gpio195 | (7,3) | IO18 ++ * gpio67 | (3,3) | IO22 ++ * ++ * Notice the gpioX and GPIO (Y,Z) mapping forumla : ++ * X = (Y-1) * 32 + Z ++ */ ++ MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x400130b1 ++ MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x400130b1 ++ MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x400130b1 ++ MX6QDL_PAD_EIM_DA7__GPIO3_IO07 0x400130b1 ++ MX6QDL_PAD_EIM_DA6__GPIO3_IO06 0x400130b1 ++ MX6QDL_PAD_SD3_CMD__GPIO7_IO02 0x400130b1 ++ MX6QDL_PAD_SD3_CLK__GPIO7_IO03 0x400130b1 ++ MX6QDL_PAD_EIM_DA3__GPIO3_IO03 0x400130b1 ++ >; ++ }; ++ ++ pinctrl_hummingboard_gpio3_5: hummingboard-gpio3_5 { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x80000000 ++ >; ++ }; ++ ++ pinctrl_hummingboard_hdmi: hummingboard-hdmi { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 ++ >; ++ }; ++ ++ pinctrl_hummingboard_i2c1: hummingboard-i2c1 { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_hummingboard_i2c2: hummingboard-i2c2 { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_hummingboard_sgtl5000: hummingboard-sgtl5000 { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x130b0 /*brk*/ ++ MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x130b0 /*ok*/ ++ MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x110b0 /*brk*/ ++ MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x130b0 /*ok*/ ++ MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x130b0 ++ >; ++ }; ++ ++ pinctrl_hummingboard_spdif: hummingboard-spdif { ++ fsl,pins = ; ++ }; ++ ++ pinctrl_hummingboard_usbh1_vbus: hummingboard-usbh1-vbus { ++ fsl,pins = ; ++ }; ++ ++ pinctrl_hummingboard_usbotg_id: hummingboard-usbotg-id { ++ /* ++ * Similar to pinctrl_usbotg_2, but we want it ++ * pulled down for a fixed host connection. ++ */ ++ fsl,pins = ; ++ }; ++ ++ pinctrl_hummingboard_usbotg_vbus: hummingboard-usbotg-vbus { ++ fsl,pins = ; ++ }; ++ ++ pinctrl_hummingboard_usdhc2_aux: hummingboard-usdhc2-aux { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 ++ >; ++ }; ++ ++ pinctrl_hummingboard_usdhc2: hummingboard-usdhc2 { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 ++ >; ++ }; ++ ++ pinctrl_hummingboard_pcie_reset: hummingboard-pcie-reset { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_DA4__GPIO3_IO04 0x80000000 ++ >; ++ }; ++ ++ pinctrl_pwm1: pwm1grp { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT8__PWM1_OUT 0x1b0b1 ++ >; ++ }; ++ ++ }; ++}; ++ ++&spdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_spdif>; ++ clocks = <&clks 197>, <&clks 0>, ++ <&clks 197>, <&clks 0>, ++ <&clks 0>, <&clks 0>, ++ <&clks 0>, <&clks 0>, ++ <&clks 0>; ++ clock-names = "core", "rxtx0", ++ "rxtx1", "rxtx2", ++ "rxtx3", "rxtx4", ++ "rxtx5", "rxtx6", ++ "rxtx7"; ++ status = "okay"; ++}; ++ ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ disable-over-current; ++ vbus-supply = <®_usbh1_vbus>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ disable-over-current; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_usbotg_id>; ++ vbus-supply = <®_usbotg_vbus>; ++ status = "okay"; ++}; ++ ++&usdhc2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = < ++ &pinctrl_hummingboard_usdhc2_aux ++ &pinctrl_hummingboard_usdhc2 ++ >; ++ vmmc-supply = <®_3p3v>; ++ cd-gpios = <&gpio1 4 0>; ++ status = "okay"; ++}; ++ ++&gpc { ++ fsl,cpu_pupscr_sw2iso = <0xf>; ++ fsl,cpu_pupscr_sw = <0xf>; ++ fsl,cpu_pdnscr_iso2sw = <0x1>; ++ fsl,cpu_pdnscr_iso = <0x1>; ++}; ++ ++&pcie { ++ pinctrl-names = "default"; ++ pinctrl-0 = < ++ &pinctrl_hummingboard_pcie_reset ++ >; ++ reset-gpio = <&gpio3 4 0>; ++ status = "okay"; ++ no-msi; ++}; ++ ++&pwm1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm1>; ++ status = "okay"; ++}; ++ ++&pwm2 { ++ pinctrl-names = "default"; ++ status = "okay"; ++}; ++ ++&pwm3 { ++ status = "disabled"; ++}; ++ ++&pwm4 { ++ status = "disabled"; ++}; ++ +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -17,7 +17,7 @@ + enet { + pinctrl_microsom_enet_ar8035: microsom-enet-ar8035 { + fsl,pins = < +- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b8b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + /* AR8035 reset */ + MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x130b0 +@@ -26,25 +26,25 @@ + /* GPIO16 -> AR8035 25MHz */ + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0xc0000000 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x80000000 +- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 +- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 +- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 +- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 +- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b030 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b030 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b030 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b030 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b030 + /* AR8035 CLK_25M --> ENET_REF_CLK (V22) */ + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x0a0b1 + /* AR8035 pin strapping: IO voltage: pull up */ +- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b030 + /* AR8035 pin strapping: PHYADDR#0: pull down */ +- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x130b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x13030 + /* AR8035 pin strapping: PHYADDR#1: pull down */ +- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x130b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x13030 + /* AR8035 pin strapping: MODE#1: pull up */ +- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b030 + /* AR8035 pin strapping: MODE#3: pull up */ +- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b030 + /* AR8035 pin strapping: MODE#0: pull down */ +- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x130b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x13030 + + /* + * As the RMII pins are also connected to RGMII +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-microsom.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-microsom.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -1,9 +1,69 @@ + /* + * Copyright (C) 2013,2014 Russell King + */ ++#include ++/ { ++ regulators { ++ compatible = "simple-bus"; ++ ++ reg_brcm_osc: brcm-osc-reg { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio5 5 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_osc_reg>; ++ regulator-name = "brcm_osc_reg"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ reg_brcm: brcm-reg { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio3 19 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_reg>; ++ regulator-name = "brcm_reg"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ startup-delay-us = <200000>; ++ }; ++ }; ++}; + + &iomuxc { + microsom { ++ pinctrl_microsom_brcm_bt: microsom-brcm-bt { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x40013070 ++ MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x40013070 ++ MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_osc_reg: microsom-brcm-osc-reg { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_reg: microsom-brcm-reg { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_wifi: microsom-brcm-wifi { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x1b0b0 ++ MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x40013070 ++ MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x40013070 ++ MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x40013070 ++ >; ++ }; ++ + pinctrl_microsom_uart1: microsom-uart1 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 +@@ -11,12 +71,24 @@ + >; + }; + +- pinctrl_microsom_usbotg: microsom-usbotg { +- /* +- * Similar to pinctrl_usbotg_2, but we want it +- * pulled down for a fixed host connection. +- */ +- fsl,pins = ; ++ pinctrl_microsom_uart4_1: microsom-uart4 { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_microsom_usdhc1: microsom-usdhc1 { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 ++ MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 ++ MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 ++ MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 ++ MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 ++ MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 ++ >; + }; + }; + }; +@@ -27,7 +99,23 @@ + status = "okay"; + }; + +-&usbotg { ++/* UART4 - Connected to optional BRCM Wifi/BT/FM */ ++&uart4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_bt &pinctrl_microsom_uart4_1>; ++ fsl,uart-has-rtscts; ++ status = "okay"; ++}; ++ ++/* USDHC1 - Connected to optional BRCM Wifi/BT/FM */ ++&usdhc1 { ++ card-external-vcc-supply = <®_brcm>; ++ card-reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>, <&gpio6 0 GPIO_ACTIVE_LOW>; ++ keep-power-in-suspend; ++ non-removable; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_microsom_usbotg>; ++ pinctrl-0 = <&pinctrl_microsom_brcm_wifi &pinctrl_microsom_usdhc1>; ++ vmmc-supply = <®_brcm>; ++ status = "okay"; + }; ++ +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,426 @@ ++/* ++ * Copyright 2013 Boundary Devices, Inc. ++ * Copyright 2011 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++#include ++#include ++ ++/ { ++ chosen { ++ stdout-path = &uart2; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x40000000>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_2p5v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "2P5V"; ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-always-on; ++ }; ++ ++ reg_3p3v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_vbus: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpio_keys>; ++ ++ power { ++ label = "Power Button"; ++ gpios = <&gpio2 3 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ gpio-key,wakeup; ++ }; ++ ++ menu { ++ label = "Menu"; ++ gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ home { ++ label = "Home"; ++ gpios = <&gpio2 4 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ back { ++ label = "Back"; ++ gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ volume-up { ++ label = "Volume Up"; ++ gpios = <&gpio7 13 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ volume-down { ++ label = "Volume Down"; ++ gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ }; ++ ++ sound { ++ compatible = "fsl,imx6q-nitrogen6x-sgtl5000", ++ "fsl,imx-audio-sgtl5000"; ++ model = "imx6q-nitrogen6x-sgtl5000"; ++ ssi-controller = <&ssi1>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ mux-int-port = <1>; ++ mux-ext-port = <3>; ++ }; ++ ++ backlight_lcd { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm1 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ power-supply = <®_3p3v>; ++ status = "okay"; ++ }; ++ ++ backlight_lvds { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm4 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ power-supply = <®_3p3v>; ++ status = "okay"; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux>; ++ status = "okay"; ++}; ++ ++&ecspi1 { ++ fsl,spi-num-chipselects = <1>; ++ cs-gpios = <&gpio3 19 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ecspi1>; ++ status = "okay"; ++ ++ flash: m25p80@0 { ++ compatible = "sst,sst25vf016b"; ++ spi-max-frequency = <20000000>; ++ reg = <0>; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio1 27 0>; ++ txen-skew-ps = <0>; ++ txc-skew-ps = <3000>; ++ rxdv-skew-ps = <0>; ++ rxc-skew-ps = <3000>; ++ rxd0-skew-ps = <0>; ++ rxd1-skew-ps = <0>; ++ rxd2-skew-ps = <0>; ++ rxd3-skew-ps = <0>; ++ txd0-skew-ps = <0>; ++ txd1-skew-ps = <0>; ++ txd2-skew-ps = <0>; ++ txd3-skew-ps = <0>; ++ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ codec: sgtl5000@0a { ++ compatible = "fsl,sgtl5000"; ++ reg = <0x0a>; ++ clocks = <&clks 201>; ++ VDDA-supply = <®_2p5v>; ++ VDDIO-supply = <®_3p3v>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6q-nitrogen6x { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ /* SGTL5000 sys_mclk */ ++ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x030b0 ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0 ++ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0 ++ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0 ++ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_ecspi1: ecspi1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 ++ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 ++ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 ++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x000b1 /* CS */ ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x100b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x100b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x100b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x100b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x100b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x100b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x100b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x100b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x100b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ /* Phy reset */ ++ MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x000b0 ++ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1 ++ >; ++ }; ++ ++ pinctrl_gpio_keys: gpio_keysgrp { ++ fsl,pins = < ++ /* Power Button */ ++ MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x1b0b0 ++ /* Menu Button */ ++ MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x1b0b0 ++ /* Home Button */ ++ MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x1b0b0 ++ /* Back Button */ ++ MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x1b0b0 ++ /* Volume Up Button */ ++ MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x1b0b0 ++ /* Volume Down Button */ ++ MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x1b0b0 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_pwm1: pwm1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_pwm3: pwm3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_DAT1__PWM3_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_pwm4: pwm4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0 ++ /* power enable, high active */ ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x000b0 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x1b0b0 /* CD */ ++ >; ++ }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x1b0b0 /* CD */ ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++ ++ lvds-channel@0 { ++ fsl,data-mapping = "spwg"; ++ fsl,data-width = <18>; ++ status = "okay"; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: hsd100pxn1 { ++ clock-frequency = <65000000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hback-porch = <220>; ++ hfront-porch = <40>; ++ vback-porch = <21>; ++ vfront-porch = <7>; ++ hsync-len = <60>; ++ vsync-len = <10>; ++ }; ++ }; ++ }; ++}; ++ ++&pcie { ++ status = "okay"; ++}; ++ ++&pwm1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm1>; ++ status = "okay"; ++}; ++ ++&pwm3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm3>; ++ status = "okay"; ++}; ++ ++&pwm4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm4>; ++ status = "okay"; ++}; ++ ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ cd-gpios = <&gpio7 0 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; ++ ++&usdhc4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc4>; ++ cd-gpios = <&gpio2 6 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-phytec-pbab01.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-phytec-pbab01.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-phytec-pbab01.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-phytec-pbab01.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,98 @@ ++/* ++ * Copyright 2013 Christian Hemp, Phytec Messtechnik GmbH ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/ { ++ chosen { ++ linux,stdout-path = &uart4; ++ }; ++}; ++ ++&fec { ++ status = "okay"; ++}; ++ ++&gpmi { ++ status = "okay"; ++}; ++ ++&i2c2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ clock-frequency = <100000>; ++ status = "okay"; ++ ++ tlv320@18 { ++ compatible = "ti,tlv320aic3x"; ++ reg = <0x18>; ++ }; ++ ++ stmpe@41 { ++ compatible = "st,stmpe811"; ++ reg = <0x41>; ++ }; ++ ++ rtc@51 { ++ compatible = "nxp,rtc8564"; ++ reg = <0x51>; ++ }; ++ ++ adc@64 { ++ compatible = "maxim,max1037"; ++ reg = <0x64>; ++ }; ++}; ++ ++&i2c3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ clock-frequency = <100000>; ++ status = "okay"; ++}; ++ ++&uart3 { ++ status = "okay"; ++}; ++ ++&uart4 { ++ status = "okay"; ++}; ++ ++&usbh1 { ++ status = "okay"; ++}; ++ ++&usbotg { ++ status = "okay"; ++}; ++ ++&usdhc2 { ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ status = "okay"; ++}; ++ ++&iomuxc { ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D16__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,356 @@ ++/* ++ * Copyright 2013 Christian Hemp, Phytec Messtechnik GmbH ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++ ++/ { ++ model = "Phytec phyFLEX-i.MX6 Ouad"; ++ compatible = "phytec,imx6q-pfla02", "fsl,imx6q"; ++ ++ memory { ++ reg = <0x10000000 0x80000000>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_usb_otg_vbus: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio4 15 0>; ++ }; ++ ++ reg_usb_h1_vbus: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio1 0 0>; ++ }; ++ }; ++ ++ gpio_leds: leds { ++ compatible = "gpio-leds"; ++ ++ green { ++ label = "phyflex:green"; ++ gpios = <&gpio1 30 0>; ++ }; ++ ++ red { ++ label = "phyflex:red"; ++ gpios = <&gpio2 31 0>; ++ }; ++ }; ++}; ++ ++&ecspi3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ecspi3>; ++ status = "okay"; ++ fsl,spi-num-chipselects = <1>; ++ cs-gpios = <&gpio4 24 0>; ++ ++ flash@0 { ++ compatible = "m25p80"; ++ spi-max-frequency = <20000000>; ++ reg = <0>; ++ }; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ eeprom@50 { ++ compatible = "atmel,24c32"; ++ reg = <0x50>; ++ }; ++ ++ pmic@58 { ++ compatible = "dialog,da9063"; ++ reg = <0x58>; ++ interrupt-parent = <&gpio4>; ++ interrupts = <17 0x8>; /* active-low GPIO4_17 */ ++ ++ regulators { ++ vddcore_reg: bcore1 { ++ regulator-min-microvolt = <730000>; ++ regulator-max-microvolt = <1380000>; ++ regulator-always-on; ++ }; ++ ++ vddsoc_reg: bcore2 { ++ regulator-min-microvolt = <730000>; ++ regulator-max-microvolt = <1380000>; ++ regulator-always-on; ++ }; ++ ++ vdd_ddr3_reg: bpro { ++ regulator-min-microvolt = <1500000>; ++ regulator-max-microvolt = <1500000>; ++ regulator-always-on; ++ }; ++ ++ vdd_3v3_reg: bperi { ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vdd_buckmem_reg: bmem { ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vdd_eth_reg: bio { ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-always-on; ++ }; ++ ++ vdd_eth_io_reg: ldo4 { ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-always-on; ++ }; ++ ++ vdd_mx6_snvs_reg: ldo5 { ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-always-on; ++ }; ++ ++ vdd_3v3_pmic_io_reg: ldo6 { ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vdd_sd0_reg: ldo9 { ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vdd_sd1_reg: ldo10 { ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vdd_mx6_high_reg: ldo11 { ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6q-phytec-pfla02 { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 ++ MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x80000000 /* SPI NOR chipselect */ ++ MX6QDL_PAD_DI0_PIN15__GPIO4_IO17 0x80000000 /* PMIC interrupt */ ++ MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 /* Green LED */ ++ MX6QDL_PAD_EIM_EB3__GPIO2_IO31 0x80000000 /* Red LED */ ++ >; ++ }; ++ ++ pinctrl_ecspi3: ecspi3grp { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1 ++ MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1 ++ MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_uart3: uart3grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D30__UART3_RTS_B 0x1b0b1 ++ MX6QDL_PAD_EIM_D31__UART3_CTS_B 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbh1: usbh1grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_0__USB_H1_PWR 0x80000000 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0 ++ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 ++ >; ++ }; ++ ++ pinctrl_usdhc2: usdhc2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3_cdwp: usdhc3cdwp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000 ++ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 ++ >; ++ }; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>; ++ status = "disabled"; ++}; ++ ++&gpmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; ++ nand-on-flash-bbt; ++ status = "disabled"; ++}; ++ ++&uart3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart3>; ++ status = "disabled"; ++}; ++ ++&uart4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart4>; ++ status = "disabled"; ++}; ++ ++&usbh1 { ++ vbus-supply = <®_usb_h1_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbh1>; ++ status = "disabled"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "disabled"; ++}; ++ ++&usdhc2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc2>; ++ cd-gpios = <&gpio1 4 0>; ++ wp-gpios = <&gpio1 2 0>; ++ status = "disabled"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3 ++ &pinctrl_usdhc3_cdwp>; ++ cd-gpios = <&gpio1 27 0>; ++ wp-gpios = <&gpio1 29 0>; ++ status = "disabled"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -10,17 +10,146 @@ + * http://www.gnu.org/copyleft/gpl.html + */ + ++#include ++ + / { ++ aliases { ++ mxcfb0 = &mxcfb1; ++ mxcfb1 = &mxcfb2; ++ mxcfb2 = &mxcfb3; ++ mxcfb3 = &mxcfb4; ++ }; ++ + memory { + reg = <0x10000000 0x80000000>; + }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpio_leds>; ++ ++ user { ++ label = "debug"; ++ gpios = <&gpio5 15 GPIO_ACTIVE_HIGH>; ++ }; ++ }; ++ ++ sound-spdif { ++ compatible = "fsl,imx-audio-spdif", ++ "fsl,imx-sabreauto-spdif"; ++ model = "imx-spdif"; ++ spdif-controller = <&spdif>; ++ spdif-in; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm3 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ status = "okay"; ++ }; ++ ++ max7310_reset: max7310-reset { ++ compatible = "gpio-reset"; ++ reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; ++ reset-delay-us = <1>; ++ #reset-cells = <0>; ++ }; ++ ++ mxcfb1: fb@0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ interface_pix_fmt = "RGB666"; ++ mode_str ="LDB-XGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb2: fb@1 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <24>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb3: fb@2 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "lcd"; ++ interface_pix_fmt = "RGB565"; ++ mode_str ="CLAA-WVGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb4: fb@3 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ interface_pix_fmt = "RGB666"; ++ mode_str ="LDB-XGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm3 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ reg_audio: cs42888_supply { ++ compatible = "regulator-fixed"; ++ regulator-name = "cs42888_supply"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ ++ sound-cs42888 { ++ compatible = "fsl,imx6-sabreauto-cs42888", ++ "fsl,imx-audio-cs42888"; ++ model = "imx-cs42888"; ++ esai-controller = <&esai>; ++ asrc-controller = <&asrc_p2p>; ++ audio-codec = <&codec>; ++ }; ++ ++ sound-hdmi { ++ compatible = "fsl,imx6q-audio-hdmi", ++ "fsl,imx-audio-hdmi"; ++ model = "imx-audio-hdmi"; ++ hdmi-controller = <&hdmi_audio>; ++ }; ++ ++ clocks { ++ codec_osc: anaclk2 { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24576000>; ++ }; ++ }; + }; + + &ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio3 19 0>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_ecspi1_1 &pinctrl_ecspi1_sabreauto>; ++ pinctrl-0 = <&pinctrl_ecspi1 &pinctrl_ecspi1_cs>; + status = "disabled"; /* pin conflict with WEIM NOR */ + + flash: m25p80@0 { +@@ -34,51 +163,481 @@ + + &fec { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_enet_2>; ++ pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; ++ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; + }; + + &gpmi { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_gpmi_nand_1>; ++ pinctrl-0 = <&pinctrl_gpmi_nand>; ++ status = "okay"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ egalax_ts@04 { ++ compatible = "eeti,egalax_ts"; ++ reg = <0x04>; ++ interrupt-parent = <&gpio2>; ++ interrupts = <28 2>; ++ wakeup-gpios = <&gpio2 28 0>; ++ }; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen2_reg: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen3_reg: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++ ++ codec: cs42888@048 { ++ compatible = "cirrus,cs42888"; ++ reg = <0x048>; ++ clocks = <&codec_osc 0>; ++ clock-names = "codec_osc"; ++ VA-supply = <®_audio>; ++ VD-supply = <®_audio>; ++ VLS-supply = <®_audio>; ++ VLC-supply = <®_audio>; ++ }; ++ ++ hdmi: edid@50 { ++ compatible = "fsl,imx6-hdmi-i2c"; ++ reg = <0x50>; ++ }; ++}; ++ ++&i2c3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ pinctrl-assert-gpios = <&gpio5 4 GPIO_ACTIVE_HIGH>; + status = "okay"; ++ ++ max7310_a: gpio@30 { ++ compatible = "maxim,max7310"; ++ reg = <0x30>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ resets = <&max7310_reset>; ++ }; ++ ++ max7310_b: gpio@32 { ++ compatible = "maxim,max7310"; ++ reg = <0x32>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ max7310_c: gpio@34 { ++ compatible = "maxim,max7310"; ++ reg = <0x34>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; + }; + + &iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + +- hog { ++ imx6qdl-sabreauto { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000 + MX6QDL_PAD_SD2_DAT2__GPIO1_IO13 0x80000000 ++ MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x80000000 ++ MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x80000000 + MX6QDL_PAD_GPIO_18__SD3_VSELECT 0x17059 + >; + }; +- }; + +- ecspi1 { +- pinctrl_ecspi1_sabreauto: ecspi1-sabreauto { ++ pinctrl_esai1: esai1grp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1b030 ++ MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1b030 ++ MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1b030 ++ MX6QDL_PAD_GPIO_5__ESAI_TX2_RX3 0x1b030 ++ MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1b030 ++ MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1b030 ++ MX6QDL_PAD_GPIO_17__ESAI_TX0 0x1b030 ++ MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x1b030 ++ MX6QDL_PAD_ENET_MDIO__ESAI_RX_CLK 0x1b030 ++ MX6QDL_PAD_GPIO_9__ESAI_RX_FS 0x1b030 ++ >; ++ }; ++ ++ pinctrl_ecspi1: ecspi1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 ++ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 ++ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 ++ >; ++ }; ++ ++ pinctrl_ecspi1_cs: ecspi1cs { + fsl,pins = < + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 + >; + }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_KEY_COL2__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1 ++ >; ++ }; ++ ++ pinctrl_gpio_leds: gpioledsgrp { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15 0x80000000 ++ >; ++ }; ++ ++ pinctrl_gpmi_nand: gpminandgrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 ++ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 ++ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 ++ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 ++ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 ++ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 ++ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 ++ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 ++ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 ++ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 ++ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 ++ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 ++ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 ++ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 ++ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 ++ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 ++ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 ++ >; ++ }; ++ ++ pinctrl_hdmi_cec_2: hdmicecgrp-2 { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_pwm1: pwm1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_spdif: spdifgrp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0 ++ >; ++ }; ++ ++ pinctrl_uart3: uart3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CLK__UART3_RX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD4_CMD__UART3_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D30__UART3_CTS_B 0x1b0b1 ++ MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 ++ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 ++ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 ++ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3_100mhz: usdhc3grp100mhz { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170b9 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100b9 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170b9 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170b9 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170b9 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170b9 ++ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170b9 ++ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170b9 ++ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170b9 ++ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170b9 ++ >; ++ }; ++ ++ pinctrl_usdhc3_200mhz: usdhc3grp200mhz { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170f9 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100f9 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170f9 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170f9 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170f9 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170f9 ++ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170f9 ++ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170f9 ++ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170f9 ++ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170f9 ++ >; ++ }; ++ ++ pinctrl_weim_cs0: weimcs0grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_CS0__EIM_CS0_B 0xb0b1 ++ >; ++ }; ++ ++ pinctrl_weim_nor: weimnorgrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_OE__EIM_OE_B 0xb0b1 ++ MX6QDL_PAD_EIM_RW__EIM_RW 0xb0b1 ++ MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B 0xb060 ++ MX6QDL_PAD_EIM_D16__EIM_DATA16 0x1b0b0 ++ MX6QDL_PAD_EIM_D17__EIM_DATA17 0x1b0b0 ++ MX6QDL_PAD_EIM_D18__EIM_DATA18 0x1b0b0 ++ MX6QDL_PAD_EIM_D19__EIM_DATA19 0x1b0b0 ++ MX6QDL_PAD_EIM_D20__EIM_DATA20 0x1b0b0 ++ MX6QDL_PAD_EIM_D21__EIM_DATA21 0x1b0b0 ++ MX6QDL_PAD_EIM_D22__EIM_DATA22 0x1b0b0 ++ MX6QDL_PAD_EIM_D23__EIM_DATA23 0x1b0b0 ++ MX6QDL_PAD_EIM_D24__EIM_DATA24 0x1b0b0 ++ MX6QDL_PAD_EIM_D25__EIM_DATA25 0x1b0b0 ++ MX6QDL_PAD_EIM_D26__EIM_DATA26 0x1b0b0 ++ MX6QDL_PAD_EIM_D27__EIM_DATA27 0x1b0b0 ++ MX6QDL_PAD_EIM_D28__EIM_DATA28 0x1b0b0 ++ MX6QDL_PAD_EIM_D29__EIM_DATA29 0x1b0b0 ++ MX6QDL_PAD_EIM_D30__EIM_DATA30 0x1b0b0 ++ MX6QDL_PAD_EIM_D31__EIM_DATA31 0x1b0b0 ++ MX6QDL_PAD_EIM_A23__EIM_ADDR23 0xb0b1 ++ MX6QDL_PAD_EIM_A22__EIM_ADDR22 0xb0b1 ++ MX6QDL_PAD_EIM_A21__EIM_ADDR21 0xb0b1 ++ MX6QDL_PAD_EIM_A20__EIM_ADDR20 0xb0b1 ++ MX6QDL_PAD_EIM_A19__EIM_ADDR19 0xb0b1 ++ MX6QDL_PAD_EIM_A18__EIM_ADDR18 0xb0b1 ++ MX6QDL_PAD_EIM_A17__EIM_ADDR17 0xb0b1 ++ MX6QDL_PAD_EIM_A16__EIM_ADDR16 0xb0b1 ++ MX6QDL_PAD_EIM_DA15__EIM_AD15 0xb0b1 ++ MX6QDL_PAD_EIM_DA14__EIM_AD14 0xb0b1 ++ MX6QDL_PAD_EIM_DA13__EIM_AD13 0xb0b1 ++ MX6QDL_PAD_EIM_DA12__EIM_AD12 0xb0b1 ++ MX6QDL_PAD_EIM_DA11__EIM_AD11 0xb0b1 ++ MX6QDL_PAD_EIM_DA10__EIM_AD10 0xb0b1 ++ MX6QDL_PAD_EIM_DA9__EIM_AD09 0xb0b1 ++ MX6QDL_PAD_EIM_DA8__EIM_AD08 0xb0b1 ++ MX6QDL_PAD_EIM_DA7__EIM_AD07 0xb0b1 ++ MX6QDL_PAD_EIM_DA6__EIM_AD06 0xb0b1 ++ MX6QDL_PAD_EIM_DA5__EIM_AD05 0xb0b1 ++ MX6QDL_PAD_EIM_DA4__EIM_AD04 0xb0b1 ++ MX6QDL_PAD_EIM_DA3__EIM_AD03 0xb0b1 ++ MX6QDL_PAD_EIM_DA2__EIM_AD02 0xb0b1 ++ MX6QDL_PAD_EIM_DA1__EIM_AD01 0xb0b1 ++ MX6QDL_PAD_EIM_DA0__EIM_AD00 0xb0b1 ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++ ++ lvds-channel@0 { ++ fsl,data-mapping = "spwg"; ++ fsl,data-width = <18>; ++ status = "okay"; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: hsd100pxn1 { ++ clock-frequency = <65000000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hback-porch = <220>; ++ hfront-porch = <40>; ++ vback-porch = <21>; ++ vfront-porch = <7>; ++ hsync-len = <60>; ++ vsync-len = <10>; ++ }; ++ }; + }; + }; + ++&pcie { ++ status = "okay"; ++}; ++ ++&pwm3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm1>; ++ status = "okay"; ++}; ++ ++&spdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_spdif>; ++ status = "okay"; ++}; ++ ++&uart3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart3>; ++ pinctrl-assert-gpios = <&max7310_b 4 GPIO_ACTIVE_HIGH>, /* CTS */ ++ <&max7310_c 3 GPIO_ACTIVE_HIGH>; /* RXD and TXD */ ++ fsl,uart-has-rtscts; ++ status = "okay"; ++}; ++ + &uart4 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart4_1>; ++ pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; + }; + + &usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; +- pinctrl-0 = <&pinctrl_usdhc3_1>; +- pinctrl-1 = <&pinctrl_usdhc3_1_100mhz>; +- pinctrl-2 = <&pinctrl_usdhc3_1_200mhz>; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ pinctrl-1 = <&pinctrl_usdhc3_100mhz>; ++ pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + cd-gpios = <&gpio6 15 0>; + wp-gpios = <&gpio1 13 0>; + status = "okay"; +@@ -86,7 +645,7 @@ + + &weim { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_weim_nor_1 &pinctrl_weim_cs0_1>; ++ pinctrl-0 = <&pinctrl_weim_nor &pinctrl_weim_cs0>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x08000000>; +@@ -102,3 +661,48 @@ + 0x0000c000 0x1404a38e 0x00000000>; + }; + }; ++ ++&ldb { ++ ipu_id = <1>; ++ disp_id = <0>; ++ ext_ref = <1>; ++ mode = "sep0"; ++ sec_ipu_id = <1>; ++ sec_disp_id = <1>; ++ status = "okay"; ++}; ++ ++&esai { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_esai1>; ++ status = "okay"; ++}; ++ ++&hdmi_core { ++ ipu_id = <0>; ++ disp_id = <1>; ++ status = "okay"; ++}; ++ ++&hdmi_video { ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ status = "okay"; ++}; ++ ++&hdmi_audio { ++ status = "okay"; ++}; ++ ++&hdmi_cec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hdmi_cec_2>; ++ status = "okay"; ++}; ++ ++&gpc { ++ fsl,cpu_pupscr_sw2iso = <0xf>; ++ fsl,cpu_pupscr_sw = <0xf>; ++ fsl,cpu_pdnscr_iso2sw = <0x1>; ++ fsl,cpu_pdnscr_iso = <0x1>; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,427 @@ ++/* ++ * Copyright 2011 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++#include ++#include ++ ++/ { ++ chosen { ++ stdout-path = &uart2; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x40000000>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_2p5v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "2P5V"; ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-always-on; ++ }; ++ ++ reg_3p3v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_vbus: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpio_keys>; ++ ++ power { ++ label = "Power Button"; ++ gpios = <&gpio2 3 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ gpio-key,wakeup; ++ }; ++ ++ menu { ++ label = "Menu"; ++ gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ home { ++ label = "Home"; ++ gpios = <&gpio2 4 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ back { ++ label = "Back"; ++ gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ volume-up { ++ label = "Volume Up"; ++ gpios = <&gpio7 13 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ ++ volume-down { ++ label = "Volume Down"; ++ gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ }; ++ }; ++ ++ sound { ++ compatible = "fsl,imx6q-sabrelite-sgtl5000", ++ "fsl,imx-audio-sgtl5000"; ++ model = "imx6q-sabrelite-sgtl5000"; ++ ssi-controller = <&ssi1>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ mux-int-port = <1>; ++ mux-ext-port = <4>; ++ }; ++ ++ backlight_lcd { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm1 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ power-supply = <®_3p3v>; ++ status = "okay"; ++ }; ++ ++ backlight_lvds { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm4 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ power-supply = <®_3p3v>; ++ status = "okay"; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux>; ++ status = "okay"; ++}; ++ ++&ecspi1 { ++ fsl,spi-num-chipselects = <1>; ++ cs-gpios = <&gpio3 19 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ecspi1>; ++ status = "okay"; ++ ++ flash: m25p80@0 { ++ compatible = "sst,sst25vf016b"; ++ spi-max-frequency = <20000000>; ++ reg = <0>; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>; ++ txen-skew-ps = <0>; ++ txc-skew-ps = <3000>; ++ rxdv-skew-ps = <0>; ++ rxc-skew-ps = <3000>; ++ rxd0-skew-ps = <0>; ++ rxd1-skew-ps = <0>; ++ rxd2-skew-ps = <0>; ++ rxd3-skew-ps = <0>; ++ txd0-skew-ps = <0>; ++ txd1-skew-ps = <0>; ++ txd2-skew-ps = <0>; ++ txd3-skew-ps = <0>; ++ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ codec: sgtl5000@0a { ++ compatible = "fsl,sgtl5000"; ++ reg = <0x0a>; ++ clocks = <&clks 201>; ++ VDDA-supply = <®_2p5v>; ++ VDDIO-supply = <®_3p3v>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6q-sabrelite { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ /* SGTL5000 sys_mclk */ ++ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x030b0 ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0 ++ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0 ++ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0 ++ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_ecspi1: ecspi1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 ++ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 ++ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 ++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x000b1 /* CS */ ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x100b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x100b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x100b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x100b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x100b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x100b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x100b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x100b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x100b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ /* Phy reset */ ++ MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x000b0 ++ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1 ++ >; ++ }; ++ ++ pinctrl_gpio_keys: gpio_keysgrp { ++ fsl,pins = < ++ /* Power Button */ ++ MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x1b0b0 ++ /* Menu Button */ ++ MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x1b0b0 ++ /* Home Button */ ++ MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x1b0b0 ++ /* Back Button */ ++ MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x1b0b0 ++ /* Volume Up Button */ ++ MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x1b0b0 ++ /* Volume Down Button */ ++ MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x1b0b0 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_pwm1: pwm1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_pwm3: pwm3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_DAT1__PWM3_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_pwm4: pwm4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0 ++ /* power enable, high active */ ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x000b0 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x1b0b0 /* CD */ ++ MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x1f0b0 /* WP */ ++ >; ++ }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x1b0b0 /* CD */ ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++ ++ lvds-channel@0 { ++ fsl,data-mapping = "spwg"; ++ fsl,data-width = <18>; ++ status = "okay"; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: hsd100pxn1 { ++ clock-frequency = <65000000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hback-porch = <220>; ++ hfront-porch = <40>; ++ vback-porch = <21>; ++ vfront-porch = <7>; ++ hsync-len = <60>; ++ vsync-len = <10>; ++ }; ++ }; ++ }; ++}; ++ ++&pcie { ++ status = "okay"; ++}; ++ ++&pwm1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm1>; ++ status = "okay"; ++}; ++ ++&pwm3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm3>; ++ status = "okay"; ++}; ++ ++&pwm4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm4>; ++ status = "okay"; ++}; ++ ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ cd-gpios = <&gpio7 0 0>; ++ wp-gpios = <&gpio7 1 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; ++ ++&usdhc4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc4>; ++ cd-gpios = <&gpio2 6 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-sabresd.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-sabresd.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-sabresd.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -10,16 +10,33 @@ + * http://www.gnu.org/copyleft/gpl.html + */ + ++#include ++#include ++ + / { ++ aliases { ++ mxcfb0 = &mxcfb1; ++ mxcfb1 = &mxcfb2; ++ mxcfb2 = &mxcfb3; ++ mxcfb3 = &mxcfb4; ++ }; ++ ++ chosen { ++ stdout-path = &uart1; ++ }; ++ + memory { + reg = <0x10000000 0x40000000>; + }; + + regulators { + compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; + +- reg_usb_otg_vbus: usb_otg_vbus { ++ reg_usb_otg_vbus: regulator@0 { + compatible = "regulator-fixed"; ++ reg = <0>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; +@@ -27,8 +44,9 @@ + enable-active-high; + }; + +- reg_usb_h1_vbus: usb_h1_vbus { ++ reg_usb_h1_vbus: regulator@1 { + compatible = "regulator-fixed"; ++ reg = <1>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; +@@ -36,29 +54,46 @@ + enable-active-high; + }; + +- reg_audio: wm8962_supply { ++ reg_audio: regulator@2 { + compatible = "regulator-fixed"; ++ reg = <2>; + regulator-name = "wm8962-supply"; + gpio = <&gpio4 10 0>; + enable-active-high; + }; ++ ++ reg_mipi_dsi_pwr_on: mipi_dsi_pwr_on { ++ compatible = "regulator-fixed"; ++ regulator-name = "mipi_dsi_pwr_on"; ++ gpio = <&gpio6 14 0>; ++ enable-active-high; ++ }; + }; + + gpio-keys { + compatible = "gpio-keys"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpio_keys>; ++ ++ power { ++ label = "Power Button"; ++ gpios = <&gpio3 29 GPIO_ACTIVE_LOW>; ++ gpio-key,wakeup; ++ linux,code = ; ++ }; + + volume-up { + label = "Volume Up"; +- gpios = <&gpio1 4 0>; ++ gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; + gpio-key,wakeup; +- linux,code = <115>; /* KEY_VOLUMEUP */ ++ linux,code = ; + }; + + volume-down { + label = "Volume Down"; +- gpios = <&gpio1 5 0>; ++ gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; + gpio-key,wakeup; +- linux,code = <114>; /* KEY_VOLUMEDOWN */ ++ linux,code = ; + }; + }; + +@@ -88,11 +123,107 @@ + default-brightness-level = <7>; + status = "okay"; + }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpio_leds>; ++ ++ red { ++ gpios = <&gpio1 2 0>; ++ default-state = "on"; ++ }; ++ }; ++ ++ sound-hdmi { ++ compatible = "fsl,imx6q-audio-hdmi", ++ "fsl,imx-audio-hdmi"; ++ model = "imx-audio-hdmi"; ++ hdmi-controller = <&hdmi_audio>; ++ }; ++ ++ mxcfb1: fb@0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ interface_pix_fmt = "RGB666"; ++ mode_str ="LDB-XGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb2: fb@1 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <24>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb3: fb@2 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "lcd"; ++ interface_pix_fmt = "RGB565"; ++ mode_str ="CLAA-WVGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb4: fb@3 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ interface_pix_fmt = "RGB666"; ++ mode_str ="LDB-XGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ lcd@0 { ++ compatible = "fsl,lcd"; ++ ipu_id = <0>; ++ disp_id = <0>; ++ default_ifmt = "RGB565"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ipu1>; ++ status = "okay"; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm1 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ }; ++ ++ v4l2_out { ++ compatible = "fsl,mxc_v4l2_output"; ++ status = "okay"; ++ }; ++ ++ lvds_cabc_ctrl { ++ lvds0-gpios = <&gpio6 15 0>; ++ lvds1-gpios = <&gpio6 16 0>; ++ }; ++ ++ mipi_dsi_reset: mipi-dsi-reset { ++ compatible = "gpio-reset"; ++ reset-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>; ++ reset-delay-us = <50>; ++ #reset-cells = <0>; ++ }; + }; + + &audmux { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_audmux_2>; ++ pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; + }; + +@@ -100,7 +231,7 @@ + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio4 9 0>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_ecspi1_2>; ++ pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { +@@ -114,7 +245,7 @@ + + &fec { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_enet_1>; ++ pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + phy-reset-gpios = <&gpio1 25 0>; + status = "okay"; +@@ -123,7 +254,7 @@ + &i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_i2c1_2>; ++ pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + codec: wm8962@1a { +@@ -149,10 +280,121 @@ + }; + }; + ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ hdmi: edid@50 { ++ compatible = "fsl,imx6-hdmi-i2c"; ++ reg = <0x50>; ++ }; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen2_reg: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen3_reg: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++}; ++ + &i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_i2c3_2>; ++ pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; + + egalax_ts@04 { +@@ -168,11 +410,9 @@ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + +- hog { ++ imx6qdl-sabresd { + pinctrl_hog: hoggrp { + fsl,pins = < +- MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 +- MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000 + MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x80000000 + MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x80000000 + MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 +@@ -182,6 +422,202 @@ + MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 + MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000 ++ MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000 ++ MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 ++ MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 ++ MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000 ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0 ++ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0 ++ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0 ++ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_ecspi1: ecspi1grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1 ++ MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1 ++ MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_gpio_keys: gpio_keysgrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 ++ MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 ++ MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000 ++ >; ++ }; ++ ++ pinctrl_hdmi_cec: hdmi_cecgrp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 ++ >; ++ }; ++ ++ pinctrl_hdmi_hdcp: hdmi_hdcpgrp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1 ++ MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_ipu1: ipu1grp { ++ fsl,pins = < ++ MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10 ++ MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x10 ++ MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x10 ++ MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x10 ++ MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000000 ++ MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x10 ++ MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x10 ++ MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x10 ++ MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x10 ++ MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x10 ++ MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x10 ++ MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x10 ++ MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x10 ++ MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x10 ++ MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x10 ++ MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x10 ++ MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x10 ++ MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x10 ++ MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x10 ++ MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x10 ++ MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x10 ++ MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x10 ++ MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x10 ++ MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x10 ++ MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x10 ++ MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x10 ++ MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x10 ++ MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x10 ++ MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x10 ++ >; ++ }; ++ ++ pinctrl_pcie: pciegrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 ++ >; ++ }; ++ ++ pinctrl_pwm1: pwm1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc2: usdhc2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 ++ MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x17059 ++ MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x17059 ++ MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x17059 ++ MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 ++ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 ++ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 ++ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 ++ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 ++ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 ++ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 ++ >; ++ }; ++ }; ++ ++ gpio_leds { ++ pinctrl_gpio_leds: gpioledsgrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 + >; + }; + }; +@@ -212,9 +648,33 @@ + }; + }; + ++&pcie { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pcie>; ++ reset-gpio = <&gpio7 12 0>; ++ status = "okay"; ++}; ++ ++&pcie { ++ power-on-gpio = <&gpio3 19 0>; ++ reset-gpio = <&gpio7 12 0>; ++ status = "okay"; ++}; ++ ++ + &pwm1 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_pwm0_1>; ++ pinctrl-0 = <&pinctrl_pwm1>; ++ status = "okay"; ++}; ++ ++&ldb { ++ ipu_id = <1>; ++ disp_id = <1>; ++ ext_ref = <1>; ++ mode = "sep1"; ++ sec_ipu_id = <1>; ++ sec_disp_id = <0>; + status = "okay"; + }; + +@@ -225,7 +685,16 @@ + + &uart1 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart1_1>; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&mipi_dsi { ++ dev_id = <0>; ++ disp_id = <0>; ++ lcd_panel = "TRULY-WVGA"; ++ disp-power-on-supply = <®_mipi_dsi_pwr_on>; ++ resets = <&mipi_dsi_reset>; + status = "okay"; + }; + +@@ -237,14 +706,14 @@ + &usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usbotg_2>; ++ pinctrl-0 = <&pinctrl_usbotg>; + disable-over-current; + status = "okay"; + }; + + &usdhc2 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc2_1>; ++ pinctrl-0 = <&pinctrl_usdhc2>; + bus-width = <8>; + cd-gpios = <&gpio2 2 0>; + wp-gpios = <&gpio2 3 0>; +@@ -253,9 +722,47 @@ + + &usdhc3 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc3_1>; ++ pinctrl-0 = <&pinctrl_usdhc3>; + bus-width = <8>; + cd-gpios = <&gpio2 0 0>; + wp-gpios = <&gpio2 1 0>; + status = "okay"; + }; ++ ++&usdhc4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc4>; ++ bus-width = <8>; ++ non-removable; ++ no-1-8-v; ++ status = "okay"; ++}; ++ ++&hdmi_core { ++ ipu_id = <0>; ++ disp_id = <0>; ++ status = "okay"; ++}; ++ ++&hdmi_video { ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ status = "okay"; ++}; ++ ++&hdmi_audio { ++ status = "okay"; ++}; ++ ++&hdmi_cec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hdmi_cec>; ++ status = "okay"; ++}; ++ ++&gpc { ++ fsl,cpu_pupscr_sw2iso = <0xf>; ++ fsl,cpu_pupscr_sw = <0xf>; ++ fsl,cpu_pdnscr_iso2sw = <0x1>; ++ fsl,cpu_pdnscr_iso = <0x1>; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-tbs2910.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-tbs2910.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-tbs2910.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-tbs2910.dtsi 2015-07-24 18:03:30.204842002 -0500 +@@ -0,0 +1,800 @@ ++/* ++ * Copyright 2012 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++ ++/ { ++ aliases { ++ mxcfb0 = &mxcfb1; ++ mxcfb1 = &mxcfb2; ++ mxcfb2 = &mxcfb3; ++ mxcfb3 = &mxcfb4; ++ }; ++ ++ ir_recv: ir-receiver { ++ compatible = "gpio-ir-receiver"; ++ gpios = <&gpio3 18 1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sabresd_ir>; ++ linux,rc-map-name = "rc-rc6-mce"; ++ }; ++ ++ chosen { ++ stdout-path = &uart1; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x40000000>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_usb_otg_vbus: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ ++ reg_usb_h1_vbus: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio1 29 0>; ++ enable-active-high; ++ }; ++ ++ reg_audio: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "sgtl5000-supply"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_mipi_dsi_pwr_on: mipi_dsi_pwr_on { ++ compatible = "regulator-fixed"; ++ regulator-name = "mipi_dsi_pwr_on"; ++ gpio = <&gpio6 14 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpio_keys>; ++ ++ power { ++ label = "Power Button"; ++ gpios = <&gpio3 29 GPIO_ACTIVE_LOW>; ++ gpio-key,wakeup; ++ linux,code = ; ++ }; ++ ++ volume-up { ++ label = "Volume Up"; ++ gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; ++ gpio-key,wakeup; ++ linux,code = ; ++ }; ++ ++ volume-down { ++ label = "Volume Down"; ++ gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; ++ gpio-key,wakeup; ++ linux,code = ; ++ }; ++ }; ++ ++ sound { ++ compatible = "fsl,imx-audio-sgtl5000"; ++ model = "imx-sgtl5000"; ++ ssi-controller = <&ssi2>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ mux-int-port = <2>; ++ mux-ext-port = <3>; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm1 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ status = "okay"; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_gpio_leds>; ++ ++ red { ++ gpios = <&gpio1 2 0>; ++ default-state = "on"; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ fan { ++ gpios = <&gpio3 28 0>; ++ default-state = "off"; ++ }; ++ }; ++ ++ sound-spdif { ++ compatible = "fsl,imx-audio-spdif"; ++ model = "imx-spdif"; ++ spdif-controller = <&spdif>; ++ spdif-out; ++ }; ++ ++ sound-hdmi { ++ compatible = "fsl,imx6q-audio-hdmi", ++ "fsl,imx-audio-hdmi"; ++ model = "imx-audio-hdmi"; ++ hdmi-controller = <&hdmi_audio>; ++ }; ++ ++ mxcfb1: fb@0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ interface_pix_fmt = "RGB666"; ++ mode_str ="LDB-XGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb2: fb@1 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <24>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb3: fb@2 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "lcd"; ++ interface_pix_fmt = "RGB565"; ++ mode_str ="CLAA-WVGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb4: fb@3 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ interface_pix_fmt = "RGB666"; ++ mode_str ="LDB-XGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ lcd@0 { ++ compatible = "fsl,lcd"; ++ ipu_id = <0>; ++ disp_id = <0>; ++ default_ifmt = "RGB565"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ipu1>; ++ status = "okay"; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm1 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <7>; ++ }; ++ ++ v4l2_out { ++ compatible = "fsl,mxc_v4l2_output"; ++ status = "okay"; ++ }; ++ ++ lvds_cabc_ctrl { ++ lvds0-gpios = <&gpio6 15 0>; ++ lvds1-gpios = <&gpio6 16 0>; ++ }; ++ ++ mipi_dsi_reset: mipi-dsi-reset { ++ compatible = "gpio-reset"; ++ reset-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>; ++ reset-delay-us = <50>; ++ #reset-cells = <0>; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux>; ++ status = "okay"; ++}; ++ ++&ecspi1 { ++ fsl,spi-num-chipselects = <1>; ++ cs-gpios = <&gpio4 9 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ecspi1>; ++ status = "okay"; ++ ++ flash: m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "st,m25p32"; ++ spi-max-frequency = <20000000>; ++ reg = <0>; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio1 25 0>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ codec: sgtl5000@0a { ++ clocks = <&clks 201>; ++ compatible = "fsl,sgtl5000"; ++ reg = <0x0a>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sgtl5000>; ++ VDDA-supply = <®_audio>; ++ VDDIO-supply = <®_audio>; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ hdmi: edid@50 { ++ compatible = "fsl,imx6-hdmi-i2c"; ++ reg = <0x50>; ++ }; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen2_reg: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen3_reg: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++}; ++ ++&i2c3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ status = "okay"; ++ ++ rtc: rtc@68 { ++ compatible = "dallas,ds1307"; ++ reg = <0x68>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6qdl-sabresd { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x80000000 ++ MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x80000000 ++ MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 ++ MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x80000000 ++ MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0x80000000 ++ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 ++ MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000 ++ MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000 ++ MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 ++ MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 ++ MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000 ++ >; ++ }; ++ ++ pinctrl_sgtl5000: sgtl5000grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0 ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0 ++ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0 ++ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0 ++ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_ecspi1: ecspi1grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1 ++ MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1 ++ MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_gpio_keys: gpio_keysgrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 ++ MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 ++ MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000 ++ >; ++ }; ++ ++ pinctrl_sabresd_ir: sabresd-ir { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D18__GPIO3_IO18 0x80000000 ++ >; ++ }; ++ ++ pinctrl_sabresd_spdif: sabresd-spdif { ++ fsl,pins = ; ++ }; ++ ++ pinctrl_hdmi_cec: hdmi_cecgrp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 ++ >; ++ }; ++ ++ pinctrl_hdmi_hdcp: hdmi_hdcpgrp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1 ++ MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_ipu1: ipu1grp { ++ fsl,pins = < ++ MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10 ++ MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x10 ++ MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x10 ++ MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x10 ++ MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000000 ++ MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x10 ++ MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x10 ++ MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x10 ++ MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x10 ++ MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x10 ++ MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x10 ++ MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x10 ++ MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x10 ++ MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x10 ++ MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x10 ++ MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x10 ++ MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x10 ++ MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x10 ++ MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x10 ++ MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x10 ++ MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x10 ++ MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x10 ++ MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x10 ++ MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x10 ++ MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x10 ++ MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x10 ++ MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x10 ++ MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x10 ++ MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x10 ++ >; ++ }; ++ ++ pinctrl_pcie: pciegrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 ++ >; ++ }; ++ ++ pinctrl_pwm1: pwm1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc2: usdhc2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 ++ MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x17059 ++ MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x17059 ++ MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x17059 ++ MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 ++ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 ++ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 ++ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 ++ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 ++ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 ++ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 ++ >; ++ }; ++ }; ++ ++ gpio_leds { ++ pinctrl_gpio_leds: gpioledsgrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 ++ MX6QDL_PAD_EIM_D28__GPIO3_IO28 0x80000000 ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++ ++ lvds-channel@1 { ++ fsl,data-mapping = "spwg"; ++ fsl,data-width = <18>; ++ status = "okay"; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: hsd100pxn1 { ++ clock-frequency = <65000000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hback-porch = <220>; ++ hfront-porch = <40>; ++ vback-porch = <21>; ++ vfront-porch = <7>; ++ hsync-len = <60>; ++ vsync-len = <10>; ++ }; ++ }; ++ }; ++}; ++ ++&pcie { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pcie>; ++ reset-gpio = <&gpio7 12 0>; ++ status = "okay"; ++}; ++ ++&pcie { ++ power-on-gpio = <&gpio3 19 0>; ++ reset-gpio = <&gpio7 12 0>; ++ status = "okay"; ++}; ++ ++ ++&pwm1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm1>; ++ status = "okay"; ++}; ++ ++&ldb { ++ ipu_id = <1>; ++ disp_id = <1>; ++ ext_ref = <1>; ++ mode = "sep1"; ++ sec_ipu_id = <1>; ++ sec_disp_id = <0>; ++ status = "okay"; ++}; ++ ++&ssi2 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&mipi_dsi { ++ dev_id = <0>; ++ disp_id = <0>; ++ lcd_panel = "TRULY-WVGA"; ++ disp-power-on-supply = <®_mipi_dsi_pwr_on>; ++ resets = <&mipi_dsi_reset>; ++ status = "okay"; ++}; ++ ++&spdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_sabresd_spdif>; ++ clocks = <&clks 197>, <&clks 0>, ++ <&clks 197>, <&clks 0>, ++ <&clks 0>, <&clks 0>, ++ <&clks 0>, <&clks 0>, ++ <&clks 0>; ++ clock-names = "core", "rxtx0", ++ "rxtx1", "rxtx2", ++ "rxtx3", "rxtx4", ++ "rxtx5", "rxtx6", ++ "rxtx7"; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ vbus-supply = <®_usb_h1_vbus>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usdhc2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc2>; ++ bus-width = <8>; ++ cd-gpios = <&gpio2 2 0>; ++ wp-gpios = <&gpio2 3 0>; ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ bus-width = <8>; ++ cd-gpios = <&gpio2 0 0>; ++ wp-gpios = <&gpio2 1 0>; ++ status = "okay"; ++}; ++ ++&usdhc4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc4>; ++ bus-width = <8>; ++ non-removable; ++ no-1-8-v; ++ status = "okay"; ++}; ++ ++&hdmi_core { ++ ipu_id = <0>; ++ disp_id = <0>; ++ status = "okay"; ++}; ++ ++&hdmi_video { ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ status = "okay"; ++}; ++ ++&hdmi_audio { ++ status = "okay"; ++}; ++ ++&hdmi_cec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hdmi_cec>; ++ status = "okay"; ++}; ++ ++&gpc { ++ fsl,cpu_pupscr_sw2iso = <0xf>; ++ fsl,cpu_pupscr_sw = <0xf>; ++ fsl,cpu_pdnscr_iso2sw = <0x1>; ++ fsl,cpu_pdnscr_iso = <0x1>; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6qdl-wandboard.dtsi linux-openelec/arch/arm/boot/dts/imx6qdl-wandboard.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6qdl-wandboard.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6qdl-wandboard.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -12,17 +12,21 @@ + / { + regulators { + compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; + +- reg_2p5v: 2p5v { ++ reg_2p5v: regulator@0 { + compatible = "regulator-fixed"; ++ reg = <0>; + regulator-name = "2P5V"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + }; + +- reg_3p3v: 3p3v { ++ reg_3p3v: regulator@1 { + compatible = "regulator-fixed"; ++ reg = <1>; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; +@@ -54,14 +58,14 @@ + + &audmux { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_audmux_2>; ++ pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; + }; + + &i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_i2c2_2>; ++ pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + codec: sgtl5000@0a { +@@ -77,7 +81,7 @@ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + +- hog { ++ imx6qdl-wandboard { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0 +@@ -91,20 +95,121 @@ + MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 + >; + }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0 ++ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0 ++ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0 ++ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_spdif: spdifgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_RXD0__SPDIF_OUT 0x1b0b0 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart3: uart3grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D23__UART3_CTS_B 0x1b0b1 ++ MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc1: usdhc1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 ++ MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 ++ MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 ++ MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 ++ MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 ++ MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc2: usdhc2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; + }; + }; + + &fec { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_enet_1>; ++ pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + phy-reset-gpios = <&gpio3 29 0>; ++ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; + }; + + &spdif { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_spdif_3>; ++ pinctrl-0 = <&pinctrl_spdif>; + status = "okay"; + }; + +@@ -115,13 +220,13 @@ + + &uart1 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart1_1>; ++ pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; + }; + + &uart3 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart3_2>; ++ pinctrl-0 = <&pinctrl_uart3>; + fsl,uart-has-rtscts; + status = "okay"; + }; +@@ -132,7 +237,7 @@ + + &usbotg { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usbotg_1>; ++ pinctrl-0 = <&pinctrl_usbotg>; + disable-over-current; + dr_mode = "peripheral"; + status = "okay"; +@@ -140,21 +245,21 @@ + + &usdhc1 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc1_2>; ++ pinctrl-0 = <&pinctrl_usdhc1>; + cd-gpios = <&gpio1 2 0>; + status = "okay"; + }; + + &usdhc2 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc2_2>; ++ pinctrl-0 = <&pinctrl_usdhc2>; + non-removable; + status = "okay"; + }; + + &usdhc3 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc3_2>; ++ pinctrl-0 = <&pinctrl_usdhc3>; + cd-gpios = <&gpio3 9 0>; + status = "okay"; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts linux-openelec/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,432 @@ ++/* ++ * Copyright 2013 Data Modul AG ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++ ++#include ++#include "imx6q.dtsi" ++ ++/ { ++ model = "Data Modul eDM-QMX6 Board"; ++ compatible = "dmo,imx6q-edmqmx6", "fsl,imx6q"; ++ ++ chosen { ++ stdout-path = &uart2; ++ }; ++ ++ aliases { ++ gpio7 = &stmpe_gpio1; ++ gpio8 = &stmpe_gpio2; ++ stmpe-i2c0 = &stmpe1; ++ stmpe-i2c1 = &stmpe2; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x80000000>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_3p3v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_switch: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "usb_otg_switch"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio7 12 0>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ reg_usb_host1: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "usb_host1_en"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ gpio = <&gpio3 31 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ gpio-leds { ++ compatible = "gpio-leds"; ++ ++ led-blue { ++ label = "blue"; ++ gpios = <&stmpe_gpio1 8 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led-green { ++ label = "green"; ++ gpios = <&stmpe_gpio1 9 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ led-pink { ++ label = "pink"; ++ gpios = <&stmpe_gpio1 10 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ led-red { ++ label = "red"; ++ gpios = <&stmpe_gpio1 11 GPIO_ACTIVE_HIGH>; ++ }; ++ }; ++}; ++ ++&ecspi5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ecspi5>; ++ fsl,spi-num-chipselects = <1>; ++ cs-gpios = <&gpio1 12 0>; ++ status = "okay"; ++ ++ flash: m25p80@0 { ++ compatible = "m25p80"; ++ spi-max-frequency = <40000000>; ++ reg = <0>; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio3 23 0>; ++ phy-supply = <&vgen2_1v2_eth>; ++ status = "okay"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2 ++ &pinctrl_stmpe1 ++ &pinctrl_stmpe2 ++ &pinctrl_pfuze>; ++ status = "okay"; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ interrupt-parent = <&gpio3>; ++ interrupts = <20 8>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-always-on; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ regulator-always-on; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen2_1v2_eth: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vdd_high_in: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++ ++ stmpe1: stmpe1601@40 { ++ compatible = "st,stmpe1601"; ++ reg = <0x40>; ++ interrupts = <30 0>; ++ interrupt-parent = <&gpio3>; ++ vcc-supply = <&sw2_reg>; ++ vio-supply = <&sw2_reg>; ++ ++ stmpe_gpio1: stmpe_gpio { ++ #gpio-cells = <2>; ++ compatible = "st,stmpe-gpio"; ++ }; ++ }; ++ ++ stmpe2: stmpe1601@44 { ++ compatible = "st,stmpe1601"; ++ reg = <0x44>; ++ interrupts = <2 0>; ++ interrupt-parent = <&gpio5>; ++ vcc-supply = <&sw2_reg>; ++ vio-supply = <&sw2_reg>; ++ ++ stmpe_gpio2: stmpe_gpio { ++ #gpio-cells = <2>; ++ compatible = "st,stmpe-gpio"; ++ }; ++ }; ++ ++ temp1: ad7414@4c { ++ compatible = "ad,ad7414"; ++ reg = <0x4c>; ++ }; ++ ++ temp2: ad7414@4d { ++ compatible = "ad,ad7414"; ++ reg = <0x4d>; ++ }; ++ ++ rtc: m41t62@68 { ++ compatible = "stm,m41t62"; ++ reg = <0x68>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6q-dmo-edmqmx6 { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_A16__GPIO2_IO22 0x80000000 ++ MX6QDL_PAD_EIM_A17__GPIO2_IO21 0x80000000 ++ >; ++ }; ++ ++ pinctrl_ecspi5: ecspi5rp-1 { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_DAT0__ECSPI5_MISO 0x80000000 ++ MX6QDL_PAD_SD1_CMD__ECSPI5_MOSI 0x80000000 ++ MX6QDL_PAD_SD1_CLK__ECSPI5_SCLK 0x80000000 ++ MX6QDL_PAD_SD2_DAT3__GPIO1_IO12 0x80000000 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_pfuze: pfuze100grp1 { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 ++ >; ++ }; ++ ++ pinctrl_stmpe1: stmpe1grp { ++ fsl,pins = ; ++ }; ++ ++ pinctrl_stmpe2: stmpe2grp { ++ fsl,pins = ; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 ++ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 ++ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 ++ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 ++ >; ++ }; ++ }; ++}; ++ ++&sata { ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ vbus-supply = <®_usb_host1>; ++ disable-over-current; ++ dr_mode = "host"; ++ status = "okay"; ++}; ++ ++&usbotg { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; ++ ++&usdhc4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc4>; ++ vmmc-supply = <®_3p3v>; ++ non-removable; ++ bus-width = <8>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q.dtsi linux-openelec/arch/arm/boot/dts/imx6q.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6q.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -8,10 +8,16 @@ + * + */ + ++#include + #include "imx6q-pinfunc.h" + #include "imx6qdl.dtsi" + + / { ++ aliases { ++ ipu1 = &ipu2; ++ spi4 = &ecspi5; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +@@ -25,8 +31,17 @@ + /* kHz uV */ + 1200000 1275000 + 996000 1250000 ++ 852000 1250000 + 792000 1150000 +- 396000 950000 ++ 396000 975000 ++ >; ++ fsl,soc-operating-points = < ++ /* ARM kHz SOC-PU uV */ ++ 1200000 1275000 ++ 996000 1250000 ++ 852000 1250000 ++ 792000 1175000 ++ 396000 1175000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + clocks = <&clks 104>, <&clks 6>, <&clks 16>, +@@ -61,12 +76,77 @@ + }; + + soc { ++ ++ busfreq { /* BUSFREQ */ ++ compatible = "fsl,imx6_busfreq"; ++ clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>, ++ <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>; ++ clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", ++ "periph_pre", "periph_clk2", "periph_clk2_sel", "osc"; ++ interrupts = <0 107 0x04>, <0 112 0x4>, <0 113 0x4>, <0 114 0x4>; ++ interrupt-names = "irq_busfreq_0", "irq_busfreq_1", "irq_busfreq_2", "irq_busfreq_3"; ++ fsl,max_ddr_freq = <528000000>; ++ }; ++ ++ gpu@00130000 { ++ compatible = "fsl,imx6q-gpu"; ++ reg = <0x00130000 0x4000>, <0x00134000 0x4000>, ++ <0x02204000 0x4000>, <0x0 0x0>; ++ reg-names = "iobase_3d", "iobase_2d", ++ "iobase_vg", "phys_baseaddr"; ++ interrupts = <0 9 0x04>, <0 10 0x04>,<0 11 0x04>; ++ interrupt-names = "irq_3d", "irq_2d", "irq_vg"; ++ clocks = <&clks 26>, <&clks 143>, ++ <&clks 27>, <&clks 121>, ++ <&clks 122>, <&clks 74>; ++ clock-names = "gpu2d_axi_clk", "openvg_axi_clk", ++ "gpu3d_axi_clk", "gpu2d_clk", ++ "gpu3d_clk", "gpu3d_shader_clk"; ++ resets = <&src 0>, <&src 3>, <&src 3>; ++ reset-names = "gpu3d", "gpu2d", "gpuvg"; ++ pu-supply = <®_pu>; ++ }; ++ + ocram: sram@00900000 { + compatible = "mmio-sram"; + reg = <0x00900000 0x40000>; + clocks = <&clks 142>; + }; + ++ hdmi_core: hdmi_core@00120000 { ++ compatible = "fsl,imx6q-hdmi-core"; ++ reg = <0x00120000 0x9000>; ++ clocks = <&clks 124>, <&clks 123>; ++ clock-names = "hdmi_isfr", "hdmi_iahb"; ++ status = "disabled"; ++ }; ++ ++ hdmi_video: hdmi_video@020e0000 { ++ compatible = "fsl,imx6q-hdmi-video"; ++ reg = <0x020e0000 0x1000>; ++ reg-names = "hdmi_gpr"; ++ interrupts = <0 115 0x04>; ++ clocks = <&clks 124>, <&clks 123>; ++ clock-names = "hdmi_isfr", "hdmi_iahb"; ++ status = "disabled"; ++ }; ++ ++ hdmi_audio: hdmi_audio@00120000 { ++ compatible = "fsl,imx6q-hdmi-audio"; ++ clocks = <&clks 124>, <&clks 123>; ++ clock-names = "hdmi_isfr", "hdmi_iahb"; ++ dmas = <&sdma 2 23 0>; ++ dma-names = "tx"; ++ status = "disabled"; ++ }; ++ ++ hdmi_cec: hdmi_cec@00120000 { ++ compatible = "fsl,imx6q-hdmi-cec"; ++ interrupts = <0 115 0x04>; ++ status = "disabled"; ++ }; ++ ++ + aips-bus@02000000 { /* AIPS1 */ + spba-bus@02000000 { + ecspi5: ecspi@02018000 { +@@ -74,13 +154,17 @@ + #size-cells = <0>; + compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi"; + reg = <0x02018000 0x4000>; +- interrupts = <0 35 0x04>; ++ interrupts = <0 35 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 116>, <&clks 116>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + }; + ++ vpu@02040000 { ++ status = "okay"; ++ }; ++ + iomuxc: iomuxc@020e0000 { + compatible = "fsl,imx6q-iomuxc"; + +@@ -122,40 +206,40 @@ + }; + }; + ++ aips-bus@02100000 { /* AIPS2 */ ++ mipi_dsi: mipi@021e0000 { ++ compatible = "fsl,imx6q-mipi-dsi"; ++ reg = <0x021e0000 0x4000>; ++ interrupts = <0 102 0x04>; ++ gpr = <&gpr>; ++ clocks = <&clks 138>, <&clks 209>; ++ clock-names = "mipi_pllref_clk", "mipi_cfg_clk"; ++ status = "disabled"; ++ }; ++ }; ++ + sata: sata@02200000 { + compatible = "fsl,imx6q-ahci"; + reg = <0x02200000 0x4000>; +- interrupts = <0 39 0x04>; ++ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks 154>, <&clks 187>, <&clks 105>; + clock-names = "sata", "sata_ref", "ahb"; + status = "disabled"; + }; + + ipu2: ipu@02800000 { +- #crtc-cells = <1>; + compatible = "fsl,imx6q-ipu"; + reg = <0x02800000 0x400000>; +- interrupts = <0 8 0x4 0 7 0x4>; +- clocks = <&clks 133>, <&clks 134>, <&clks 137>; +- clock-names = "bus", "di0", "di1"; ++ interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>, ++ <0 7 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 133>, <&clks 134>, <&clks 137>, ++ <&clks 41>, <&clks 42>, ++ <&clks 135>, <&clks 136>; ++ clock-names = "bus", "di0", "di1", ++ "di0_sel", "di1_sel", ++ "ldb_di0", "ldb_di1"; + resets = <&src 4>; ++ bypass_reset = <0>; + }; + }; + }; +- +-&ldb { +- clocks = <&clks 33>, <&clks 34>, +- <&clks 39>, <&clks 40>, <&clks 41>, <&clks 42>, +- <&clks 135>, <&clks 136>; +- clock-names = "di0_pll", "di1_pll", +- "di0_sel", "di1_sel", "di2_sel", "di3_sel", +- "di0", "di1"; +- +- lvds-channel@0 { +- crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; +- }; +- +- lvds-channel@1 { +- crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; +- }; +-}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-gk802.dts linux-openelec/arch/arm/boot/dts/imx6q-gk802.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-gk802.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-gk802.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,229 @@ ++/* ++ * Copyright (C) 2013 Philipp Zabel ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++ ++/ { ++ model = "Zealz GK802"; ++ compatible = "zealz,imx6q-gk802", "fsl,imx6q"; ++ ++ aliases { ++ mxcfb0 = &mxcfb1; ++ }; ++ ++ chosen { ++ stdout-path = &uart4; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x40000000>; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_3p3v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_h1_vbus: usb_h1_vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio2 0 0>; ++ }; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ ++ recovery-button { ++ label = "recovery"; ++ gpios = <&gpio3 16 1>; ++ linux,code = <0x198>; /* KEY_RESTART */ ++ gpio-key,wakeup; ++ }; ++ ++ }; ++ ++ sound-hdmi { ++ compatible = "fsl,imx6q-audio-hdmi", ++ "fsl,imx-audio-hdmi"; ++ model = "imx-audio-hdmi"; ++ hdmi-controller = <&hdmi_audio>; ++ }; ++ ++ mxcfb1: fb@0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <32>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "okay"; ++ }; ++}; ++ ++&hdmi_core { ++ ipu_id = <0>; ++ disp_id = <0>; ++ status = "okay"; ++}; ++ ++&hdmi_video { ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ status = "okay"; ++}; ++ ++&hdmi_audio { ++ status = "okay"; ++}; ++ ++ ++/* Internal I2C */ ++&i2c2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ clock-frequency = <100000>; ++ status = "okay"; ++ ++ /* SDMC DM2016 1024 bit EEPROM + 128 bit OTP */ ++ eeprom: dm2016@51 { ++ compatible = "sdmc,dm2016"; ++ reg = <0x51>; ++ }; ++}; ++ ++/* External I2C via HDMI */ ++&i2c3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ clock-frequency = <100000>; ++ status = "okay"; ++ ++ ddc: imx6_hdmi_i2c@50 { ++ compatible = "fsl,imx6-hdmi-i2c"; ++ reg = <0x50>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6q-gk802 { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ /* Recovery button, active-low */ ++ MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x100b1 ++ /* RTL8192CU enable GPIO, active-low */ ++ MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x1b0b0 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_16__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc4: usdhc4grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 ++ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 ++ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 ++ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 ++ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 ++ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 ++ >; ++ }; ++ }; ++}; ++ ++&uart2 { ++ status = "okay"; ++}; ++ ++&uart4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart4>; ++ status = "okay"; ++}; ++ ++/* External USB-A port (USBOTG) */ ++&usbotg { ++ phy_type = "utmi"; ++ dr_mode = "host"; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++/* Internal USB port (USBH1), connected to RTL8192CU */ ++&usbh1 { ++ phy_type = "utmi"; ++ dr_mode = "host"; ++ vbus-supply = <®_usb_h1_vbus>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++/* External microSD */ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ bus-width = <4>; ++ cd-gpios = <&gpio6 11 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; ++ ++/* Internal microSD */ ++&usdhc4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc4>; ++ bus-width = <4>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-gw51xx.dts linux-openelec/arch/arm/boot/dts/imx6q-gw51xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-gw51xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-gw51xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,19 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++#include "imx6qdl-gw54xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 Quad GW51XX"; ++ compatible = "gw,imx6q-gw51xx", "gw,ventana", "fsl,imx6q"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-gw52xx.dts linux-openelec/arch/arm/boot/dts/imx6q-gw52xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-gw52xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-gw52xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,23 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++#include "imx6qdl-gw52xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 Quad GW52XX"; ++ compatible = "gw,imx6q-gw52xx", "gw,ventana", "fsl,imx6q"; ++}; ++ ++&sata { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-gw53xx.dts linux-openelec/arch/arm/boot/dts/imx6q-gw53xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-gw53xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-gw53xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,23 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++#include "imx6qdl-gw53xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 Quad GW53XX"; ++ compatible = "gw,imx6q-gw53xx", "gw,ventana", "fsl,imx6q"; ++}; ++ ++&sata { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-gw5400-a.dts linux-openelec/arch/arm/boot/dts/imx6q-gw5400-a.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-gw5400-a.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-gw5400-a.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,543 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++ ++/ { ++ model = "Gateworks Ventana GW5400-A"; ++ compatible = "gw,imx6q-gw5400-a", "gw,ventana", "fsl,imx6q"; ++ ++ /* these are used by bootloader for disabling nodes */ ++ aliases { ++ ethernet0 = &fec; ++ ethernet1 = ð1; ++ i2c0 = &i2c1; ++ i2c1 = &i2c2; ++ i2c2 = &i2c3; ++ led0 = &led0; ++ led1 = &led1; ++ led2 = &led2; ++ sky2 = ð1; ++ ssi0 = &ssi1; ++ spi0 = &ecspi1; ++ usb0 = &usbh1; ++ usb1 = &usbotg; ++ usdhc2 = &usdhc3; ++ }; ++ ++ chosen { ++ bootargs = "console=ttymxc1,115200"; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ led0: user1 { ++ label = "user1"; ++ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */ ++ default-state = "on"; ++ linux,default-trigger = "heartbeat"; ++ }; ++ ++ led1: user2 { ++ label = "user2"; ++ gpios = <&gpio4 10 0>; /* 106 -> MX6_PANLEDR */ ++ default-state = "off"; ++ }; ++ ++ led2: user3 { ++ label = "user3"; ++ gpios = <&gpio4 15 1>; /* 111 -> MX6_LOCLED# */ ++ default-state = "off"; ++ }; ++ }; ++ ++ memory { ++ reg = <0x10000000 0x40000000>; ++ }; ++ ++ pps { ++ compatible = "pps-gpio"; ++ gpios = <&gpio1 5 0>; ++ status = "okay"; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ reg_1p0v: regulator@0 { ++ compatible = "regulator-fixed"; ++ reg = <0>; ++ regulator-name = "1P0V"; ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <1000000>; ++ regulator-always-on; ++ }; ++ ++ reg_3p3v: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_h1_vbus: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ }; ++ ++ reg_usb_otg_vbus: regulator@3 { ++ compatible = "regulator-fixed"; ++ reg = <3>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio3 22 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ sound { ++ compatible = "fsl,imx6q-sabrelite-sgtl5000", ++ "fsl,imx-audio-sgtl5000"; ++ model = "imx6q-sabrelite-sgtl5000"; ++ ssi-controller = <&ssi1>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ mux-int-port = <1>; ++ mux-ext-port = <4>; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux>; ++ status = "okay"; ++}; ++ ++&ecspi1 { ++ fsl,spi-num-chipselects = <1>; ++ cs-gpios = <&gpio3 19 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ecspi1>; ++ status = "okay"; ++ ++ flash: m25p80@0 { ++ compatible = "sst,w25q256"; ++ spi-max-frequency = <30000000>; ++ reg = <0>; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ phy-reset-gpios = <&gpio1 30 0>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ eeprom1: eeprom@50 { ++ compatible = "atmel,24c02"; ++ reg = <0x50>; ++ pagesize = <16>; ++ }; ++ ++ eeprom2: eeprom@51 { ++ compatible = "atmel,24c02"; ++ reg = <0x51>; ++ pagesize = <16>; ++ }; ++ ++ eeprom3: eeprom@52 { ++ compatible = "atmel,24c02"; ++ reg = <0x52>; ++ pagesize = <16>; ++ }; ++ ++ eeprom4: eeprom@53 { ++ compatible = "atmel,24c02"; ++ reg = <0x53>; ++ pagesize = <16>; ++ }; ++ ++ gpio: pca9555@23 { ++ compatible = "nxp,pca9555"; ++ reg = <0x23>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ hwmon: gsc@29 { ++ compatible = "gw,gsp"; ++ reg = <0x29>; ++ }; ++ ++ rtc: ds1672@68 { ++ compatible = "dallas,ds1672"; ++ reg = <0x68>; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3950000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen2_reg: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen3_reg: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++ ++ pciswitch: pex8609@3f { ++ compatible = "plx,pex8609"; ++ reg = <0x3f>; ++ }; ++ ++ pciclkgen: si52147@6b { ++ compatible = "sil,si52147"; ++ reg = <0x6b>; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3>; ++ status = "okay"; ++ ++ accelerometer: mma8450@1c { ++ compatible = "fsl,mma8450"; ++ reg = <0x1c>; ++ }; ++ ++ codec: sgtl5000@0a { ++ compatible = "fsl,sgtl5000"; ++ reg = <0x0a>; ++ clocks = <&clks 201>; ++ VDDA-supply = <&sw4_reg>; ++ VDDIO-supply = <®_3p3v>; ++ }; ++ ++ hdmiin: adv7611@4c { ++ compatible = "adi,adv7611"; ++ reg = <0x4c>; ++ }; ++ ++ touchscreen: egalax_ts@04 { ++ compatible = "eeti,egalax_ts"; ++ reg = <0x04>; ++ interrupt-parent = <&gpio7>; ++ interrupts = <12 2>; /* gpio7_12 active low */ ++ wakeup-gpios = <&gpio7 12 0>; ++ }; ++ ++ videoout: adv7393@2a { ++ compatible = "adi,adv7393"; ++ reg = <0x2a>; ++ }; ++ ++ videoin: adv7180@20 { ++ compatible = "adi,adv7180"; ++ reg = <0x20>; ++ }; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6q-gw5400-a { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */ ++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 /* SPINOR_CS0# */ ++ MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 /* PCIE IRQ */ ++ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE RST */ ++ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */ ++ MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000 /* GPS_PPS */ ++ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* TOUCH_IRQ# */ ++ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */ ++ MX6QDL_PAD_KEY_COL2__GPIO4_IO10 0x80000000 /* user2 led */ ++ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */ ++ MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000 /* USBHUB_RST# */ ++ MX6QDL_PAD_SD1_DAT3__GPIO1_IO21 0x80000000 /* MIPI_DIO */ ++ >; ++ }; ++ ++ pinctrl_audmux: audmuxgrp { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0 ++ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0 ++ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0 ++ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0 ++ >; ++ }; ++ ++ pinctrl_ecspi1: ecspi1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 ++ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 ++ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3: i2c3grp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart5: uart5grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ }; ++}; ++ ++&ldb { ++ status = "okay"; ++}; ++ ++&pcie { ++ reset-gpio = <&gpio1 29 0>; ++ status = "okay"; ++ ++ eth1: sky2@8 { /* MAC/PHY on bus 8 */ ++ compatible = "marvell,sky2"; ++ }; ++}; ++ ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart1>; ++ status = "okay"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart2>; ++ status = "okay"; ++}; ++ ++&uart5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart5>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ vbus-supply = <®_usb_otg_vbus>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usbotg>; ++ disable-over-current; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ vbus-supply = <®_usb_h1_vbus>; ++ status = "okay"; ++}; ++ ++&usdhc3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ cd-gpios = <&gpio7 0 0>; ++ vmmc-supply = <®_3p3v>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-gw54xx.dts linux-openelec/arch/arm/boot/dts/imx6q-gw54xx.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-gw54xx.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-gw54xx.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,23 @@ ++/* ++ * Copyright 2013 Gateworks Corporation ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++#include "imx6qdl-gw54xx.dtsi" ++ ++/ { ++ model = "Gateworks Ventana i.MX6 Quad GW54XX"; ++ compatible = "gw,imx6q-gw54xx", "gw,ventana", "fsl,imx6q"; ++}; ++ ++&sata { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-hummingboard.dts linux-openelec/arch/arm/boot/dts/imx6q-hummingboard.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-hummingboard.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-hummingboard.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,21 @@ ++/* ++ * Copyright (C) 2014 Rabeeh Khoury (rabeeh@solid-run.com) ++ * Based on work by Russell King ++ */ ++/dts-v1/; ++ ++#include "imx6q.dtsi" ++#include "imx6qdl-hummingboard.dtsi" ++ ++/ { ++ model = "SolidRun HummingBoard Dual/Quad"; ++ compatible = "solidrun,hummingboard/q", "fsl,imx6q"; ++}; ++ ++&sata { ++ status = "okay"; ++ fsl,transmit-level-mV = <1104>; ++ fsl,transmit-boost-mdB = <0>; ++ fsl,transmit-atten-16ths = <9>; ++ fsl,no-spread-spectrum; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-nitrogen6x.dts linux-openelec/arch/arm/boot/dts/imx6q-nitrogen6x.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-nitrogen6x.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-nitrogen6x.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,25 @@ ++/* ++ * Copyright 2013 Boundary Devices, Inc. ++ * Copyright 2012 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++#include "imx6q.dtsi" ++#include "imx6qdl-nitrogen6x.dtsi" ++ ++/ { ++ model = "Freescale i.MX6 Quad Nitrogen6x Board"; ++ compatible = "fsl,imx6q-nitrogen6x", "fsl,imx6q"; ++}; ++ ++&sata { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-phytec-pbab01.dts linux-openelec/arch/arm/boot/dts/imx6q-phytec-pbab01.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-phytec-pbab01.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-phytec-pbab01.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -11,24 +11,17 @@ + + /dts-v1/; + #include "imx6q-phytec-pfla02.dtsi" ++#include "imx6qdl-phytec-pbab01.dtsi" + + / { + model = "Phytec phyFLEX-i.MX6 Quad Carrier-Board"; + compatible = "phytec,imx6q-pbab01", "phytec,imx6q-pfla02", "fsl,imx6q"; +-}; +- +-&fec { +- status = "okay"; +-}; +- +-&uart4 { +- status = "okay"; +-}; + +-&usdhc2 { +- status = "okay"; ++ chosen { ++ stdout-path = &uart4; ++ }; + }; + +-&usdhc3 { +- status = "okay"; ++&sata { ++ status = "okay"; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi linux-openelec/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -10,171 +10,13 @@ + */ + + #include "imx6q.dtsi" ++#include "imx6qdl-phytec-pfla02.dtsi" + + / { +- model = "Phytec phyFLEX-i.MX6 Ouad"; ++ model = "Phytec phyFLEX-i.MX6 Quad"; + compatible = "phytec,imx6q-pfla02", "fsl,imx6q"; + + memory { + reg = <0x10000000 0x80000000>; + }; + }; +- +-&ecspi3 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_ecspi3_1>; +- status = "okay"; +- fsl,spi-num-chipselects = <1>; +- cs-gpios = <&gpio4 24 0>; +- +- flash@0 { +- compatible = "m25p80"; +- spi-max-frequency = <20000000>; +- reg = <0>; +- }; +-}; +- +-&i2c1 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_i2c1_1>; +- status = "okay"; +- +- eeprom@50 { +- compatible = "atmel,24c32"; +- reg = <0x50>; +- }; +- +- pmic@58 { +- compatible = "dialog,da9063"; +- reg = <0x58>; +- interrupt-parent = <&gpio4>; +- interrupts = <17 0x8>; /* active-low GPIO4_17 */ +- +- regulators { +- vddcore_reg: bcore1 { +- regulator-min-microvolt = <730000>; +- regulator-max-microvolt = <1380000>; +- regulator-always-on; +- }; +- +- vddsoc_reg: bcore2 { +- regulator-min-microvolt = <730000>; +- regulator-max-microvolt = <1380000>; +- regulator-always-on; +- }; +- +- vdd_ddr3_reg: bpro { +- regulator-min-microvolt = <1500000>; +- regulator-max-microvolt = <1500000>; +- regulator-always-on; +- }; +- +- vdd_3v3_reg: bperi { +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- regulator-always-on; +- }; +- +- vdd_buckmem_reg: bmem { +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- regulator-always-on; +- }; +- +- vdd_eth_reg: bio { +- regulator-min-microvolt = <1200000>; +- regulator-max-microvolt = <1200000>; +- regulator-always-on; +- }; +- +- vdd_eth_io_reg: ldo4 { +- regulator-min-microvolt = <2500000>; +- regulator-max-microvolt = <2500000>; +- regulator-always-on; +- }; +- +- vdd_mx6_snvs_reg: ldo5 { +- regulator-min-microvolt = <3000000>; +- regulator-max-microvolt = <3000000>; +- regulator-always-on; +- }; +- +- vdd_3v3_pmic_io_reg: ldo6 { +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- regulator-always-on; +- }; +- +- vdd_sd0_reg: ldo9 { +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- }; +- +- vdd_sd1_reg: ldo10 { +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- }; +- +- vdd_mx6_high_reg: ldo11 { +- regulator-min-microvolt = <3000000>; +- regulator-max-microvolt = <3000000>; +- regulator-always-on; +- }; +- }; +- }; +-}; +- +-&iomuxc { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hog>; +- +- hog { +- pinctrl_hog: hoggrp { +- fsl,pins = < +- MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 +- MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x80000000 /* SPI NOR chipselect */ +- MX6QDL_PAD_DI0_PIN15__GPIO4_IO17 0x80000000 /* PMIC interrupt */ +- >; +- }; +- }; +- +- pfla02 { +- pinctrl_usdhc3_pfla02: usdhc3grp-pfla02 { +- fsl,pins = < +- MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000 +- MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 +- >; +- }; +- }; +-}; +- +-&fec { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_enet_3>; +- phy-mode = "rgmii"; +- phy-reset-gpios = <&gpio3 23 0>; +- status = "disabled"; +-}; +- +-&uart4 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart4_1>; +- status = "disabled"; +-}; +- +-&usdhc2 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc2_2>; +- cd-gpios = <&gpio1 4 0>; +- wp-gpios = <&gpio1 2 0>; +- status = "disabled"; +-}; +- +-&usdhc3 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc3_2 +- &pinctrl_usdhc3_pfla02>; +- cd-gpios = <&gpio1 27 0>; +- wp-gpios = <&gpio1 29 0>; +- status = "disabled"; +-}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-pinfunc.h linux-openelec/arch/arm/boot/dts/imx6q-pinfunc.h +--- linux-3.14.36/arch/arm/boot/dts/imx6q-pinfunc.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-pinfunc.h 2015-05-06 12:05:43.000000000 -0500 +@@ -673,6 +673,7 @@ + #define MX6QDL_PAD_GPIO_3__USB_H1_OC 0x22c 0x5fc 0x948 0x6 0x1 + #define MX6QDL_PAD_GPIO_3__MLB_CLK 0x22c 0x5fc 0x900 0x7 0x1 + #define MX6QDL_PAD_GPIO_6__ESAI_TX_CLK 0x230 0x600 0x870 0x0 0x1 ++#define MX6QDL_PAD_GPIO_6__ENET_IRQ 0x230 0x600 0x03c 0x11 0xff000609 + #define MX6QDL_PAD_GPIO_6__I2C3_SDA 0x230 0x600 0x8ac 0x2 0x1 + #define MX6QDL_PAD_GPIO_6__GPIO1_IO06 0x230 0x600 0x000 0x5 0x0 + #define MX6QDL_PAD_GPIO_6__SD2_LCTL 0x230 0x600 0x000 0x6 0x0 +@@ -1024,6 +1025,7 @@ + #define MX6QDL_PAD_SD1_DAT2__WDOG1_RESET_B_DEB 0x34c 0x734 0x000 0x6 0x0 + #define MX6QDL_PAD_SD1_CLK__SD1_CLK 0x350 0x738 0x000 0x0 0x0 + #define MX6QDL_PAD_SD1_CLK__ECSPI5_SCLK 0x350 0x738 0x828 0x1 0x0 ++#define MX6QDL_PAD_SD1_CLK__OSC32K_32K_OUT 0x350 0x738 0x000 0x2 0x0 + #define MX6QDL_PAD_SD1_CLK__GPT_CLKIN 0x350 0x738 0x000 0x3 0x0 + #define MX6QDL_PAD_SD1_CLK__GPIO1_IO20 0x350 0x738 0x000 0x5 0x0 + #define MX6QDL_PAD_SD2_CLK__SD2_CLK 0x354 0x73c 0x000 0x0 0x0 +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-sabreauto.dts linux-openelec/arch/arm/boot/dts/imx6q-sabreauto.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-sabreauto.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-sabreauto.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -20,6 +20,22 @@ + compatible = "fsl,imx6q-sabreauto", "fsl,imx6q"; + }; + ++&mxcfb1 { ++ status = "okay"; ++}; ++ ++&mxcfb2 { ++ status = "okay"; ++}; ++ ++&mxcfb3 { ++ status = "okay"; ++}; ++ ++&mxcfb4 { ++ status = "okay"; ++}; ++ + &sata { + status = "okay"; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-sabrelite.dts linux-openelec/arch/arm/boot/dts/imx6q-sabrelite.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-sabrelite.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-sabrelite.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -12,189 +12,13 @@ + + /dts-v1/; + #include "imx6q.dtsi" ++#include "imx6qdl-sabrelite.dtsi" + + / { + model = "Freescale i.MX6 Quad SABRE Lite Board"; + compatible = "fsl,imx6q-sabrelite", "fsl,imx6q"; +- +- memory { +- reg = <0x10000000 0x40000000>; +- }; +- +- regulators { +- compatible = "simple-bus"; +- +- reg_2p5v: 2p5v { +- compatible = "regulator-fixed"; +- regulator-name = "2P5V"; +- regulator-min-microvolt = <2500000>; +- regulator-max-microvolt = <2500000>; +- regulator-always-on; +- }; +- +- reg_3p3v: 3p3v { +- compatible = "regulator-fixed"; +- regulator-name = "3P3V"; +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- regulator-always-on; +- }; +- +- reg_usb_otg_vbus: usb_otg_vbus { +- compatible = "regulator-fixed"; +- regulator-name = "usb_otg_vbus"; +- regulator-min-microvolt = <5000000>; +- regulator-max-microvolt = <5000000>; +- gpio = <&gpio3 22 0>; +- enable-active-high; +- }; +- }; +- +- sound { +- compatible = "fsl,imx6q-sabrelite-sgtl5000", +- "fsl,imx-audio-sgtl5000"; +- model = "imx6q-sabrelite-sgtl5000"; +- ssi-controller = <&ssi1>; +- audio-codec = <&codec>; +- audio-routing = +- "MIC_IN", "Mic Jack", +- "Mic Jack", "Mic Bias", +- "Headphone Jack", "HP_OUT"; +- mux-int-port = <1>; +- mux-ext-port = <4>; +- }; +-}; +- +-&audmux { +- status = "okay"; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_audmux_1>; +-}; +- +-&ecspi1 { +- fsl,spi-num-chipselects = <1>; +- cs-gpios = <&gpio3 19 0>; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_ecspi1_1>; +- status = "okay"; +- +- flash: m25p80@0 { +- compatible = "sst,sst25vf016b"; +- spi-max-frequency = <20000000>; +- reg = <0>; +- }; +-}; +- +-&fec { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_enet_1>; +- phy-mode = "rgmii"; +- phy-reset-gpios = <&gpio3 23 0>; +- status = "okay"; +-}; +- +-&i2c1 { +- status = "okay"; +- clock-frequency = <100000>; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_i2c1_1>; +- +- codec: sgtl5000@0a { +- compatible = "fsl,sgtl5000"; +- reg = <0x0a>; +- clocks = <&clks 201>; +- VDDA-supply = <®_2p5v>; +- VDDIO-supply = <®_3p3v>; +- }; +-}; +- +-&iomuxc { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_hog>; +- +- hog { +- pinctrl_hog: hoggrp { +- fsl,pins = < +- MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x80000000 +- MX6QDL_PAD_NANDF_D7__GPIO2_IO07 0x80000000 +- MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 +- MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 +- MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 +- MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 +- MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x1f0b0 +- MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x80000000 +- MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 +- >; +- }; +- }; +-}; +- +-&ldb { +- status = "okay"; +- +- lvds-channel@0 { +- fsl,data-mapping = "spwg"; +- fsl,data-width = <18>; +- status = "okay"; +- +- display-timings { +- native-mode = <&timing0>; +- timing0: hsd100pxn1 { +- clock-frequency = <65000000>; +- hactive = <1024>; +- vactive = <768>; +- hback-porch = <220>; +- hfront-porch = <40>; +- vback-porch = <21>; +- vfront-porch = <7>; +- hsync-len = <60>; +- vsync-len = <10>; +- }; +- }; +- }; + }; + + &sata { + status = "okay"; + }; +- +-&ssi1 { +- fsl,mode = "i2s-slave"; +- status = "okay"; +-}; +- +-&uart2 { +- status = "okay"; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart2_1>; +-}; +- +-&usbh1 { +- status = "okay"; +-}; +- +-&usbotg { +- vbus-supply = <®_usb_otg_vbus>; +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usbotg_1>; +- disable-over-current; +- status = "okay"; +-}; +- +-&usdhc3 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc3_2>; +- cd-gpios = <&gpio7 0 0>; +- wp-gpios = <&gpio7 1 0>; +- vmmc-supply = <®_3p3v>; +- status = "okay"; +-}; +- +-&usdhc4 { +- pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc4_2>; +- cd-gpios = <&gpio2 6 0>; +- wp-gpios = <&gpio2 7 0>; +- vmmc-supply = <®_3p3v>; +- status = "okay"; +-}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-sabresd.dts linux-openelec/arch/arm/boot/dts/imx6q-sabresd.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-sabresd.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-sabresd.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -23,3 +23,19 @@ + &sata { + status = "okay"; + }; ++ ++&mxcfb1 { ++ status = "okay"; ++}; ++ ++&mxcfb2 { ++ status = "okay"; ++}; ++ ++&mxcfb3 { ++ status = "okay"; ++}; ++ ++&mxcfb4 { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts linux-openelec/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,23 @@ ++/* ++ * Copyright 2012-2013 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include "imx6q-sabresd.dts" ++ ++&hdmi_video { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hdmi_hdcp>; ++ fsl,hdcp; ++}; ++ ++&i2c2 { ++ status = "disable"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-sbc6x.dts linux-openelec/arch/arm/boot/dts/imx6q-sbc6x.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-sbc6x.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-sbc6x.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -17,28 +17,78 @@ + }; + }; + ++ + &fec { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_enet_1>; ++ pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + status = "okay"; + }; + ++&iomuxc { ++ imx6q-sbc6x { ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg: usbotggrp { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ }; ++}; ++ + &uart1 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart1_1>; ++ pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; + }; + + &usbotg { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usbotg_1>; ++ pinctrl-0 = <&pinctrl_usbotg>; + disable-over-current; + status = "okay"; + }; + + &usdhc3 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc3_2>; ++ pinctrl-0 = <&pinctrl_usdhc3>; + status = "okay"; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-tbs2910.dts linux-openelec/arch/arm/boot/dts/imx6q-tbs2910.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-tbs2910.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6q-tbs2910.dts 2015-07-24 18:03:30.200842002 -0500 +@@ -0,0 +1,41 @@ ++/* ++ * Copyright 2012 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/dts-v1/; ++ ++#include "imx6q.dtsi" ++#include "imx6qdl-tbs2910.dtsi" ++ ++/ { ++ model = "TBS Matrix"; ++ compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; ++}; ++ ++&sata { ++ status = "okay"; ++}; ++ ++&mxcfb1 { ++ status = "okay"; ++}; ++ ++&mxcfb2 { ++ status = "okay"; ++}; ++ ++&mxcfb3 { ++ status = "okay"; ++}; ++ ++&mxcfb4 { ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6q-udoo.dts linux-openelec/arch/arm/boot/dts/imx6q-udoo.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6q-udoo.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6q-udoo.dts 2015-07-24 18:03:30.248842002 -0500 +@@ -2,6 +2,10 @@ + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Author: Fabio Estevam ++ * ++ * Copyright (C) 2014 Jasbir ++ * Copyright (C) 2014 udoo team ++ * Copyright (C) 2014 vpeter + * + * 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 +@@ -16,9 +20,395 @@ + model = "Udoo i.MX6 Quad Board"; + compatible = "udoo,imx6q-udoo", "fsl,imx6q"; + ++ chosen { ++ stdout-path = &uart1; ++ }; ++ ++ aliases { ++ mxcfb0 = &mxcfb1; ++ mxcfb1 = &mxcfb2; ++ mxcfb2 = &mxcfb3; ++ mxcfb3 = &mxcfb4; ++ ssi0 = &ssi1; ++ }; ++ + memory { + reg = <0x10000000 0x40000000>; + }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ ++ reg_2p5v: 2p5v { ++ compatible = "regulator-fixed"; ++ regulator-name = "2P5V"; ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ regulator-always-on; ++ }; ++ ++ reg_3p3v: 3p3v { ++ compatible = "regulator-fixed"; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ aux_5v: aux5v { ++ compatible = "regulator-fixed"; ++ regulator-name = "AUX_5V"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ gpio = <&gpio6 10 1>; ++ regulator-boot-on; ++ enable-active-high; ++ }; ++ ++ reg_sensor: sensor_supply { ++ compatible = "regulator-fixed"; ++ regulator-name = "sensor-SUPPLY"; ++ enable-active-high; ++ }; ++ ++ reg_usb_otg_vbus: usb_otg_vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ }; ++ ++ reg_usb_h1_vbus: usb_h1_vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ startup-delay-us = <2>; /* USB2415 requires a POR of 1 us minimum */ ++ gpio = <&gpio7 12 0>; ++ }; ++ }; ++ ++ mxcfb1: fb@0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <24>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "okay"; ++ }; ++ ++ mxcfb2: fb@1 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ default_bpp = <24>; ++ interface_pix_fmt = "RGB24"; ++ mode_str =""; ++ int_clk = <0>; ++ late_init = <1>; ++ status = "okay"; ++ }; ++ ++ mxcfb3: fb@2 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <24>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ mxcfb4: fb@3 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "hdmi"; ++ interface_pix_fmt = "RGB24"; ++ mode_str ="1920x1080M@60"; ++ default_bpp = <24>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "disabled"; ++ }; ++ ++ codec: vt1613 { ++ compatible = "via,vt1613"; ++ }; ++ ++ sound { ++ compatible = "udoo,imx-vt1613-audio"; ++ ssi-controller = <&ssi1>; ++ audio-codec = <&codec>; ++ mux-int-port = <1>; ++ mux-ext-port = <6>; ++ }; ++ ++ sound-hdmi { ++ compatible = "fsl,imx6q-audio-hdmi", ++ "fsl,imx-audio-hdmi"; ++ model = "imx-audio-hdmi"; ++ hdmi-controller = <&hdmi_audio>; ++ }; ++ ++ sound-spdif { ++ compatible = "fsl,imx-audio-spdif", ++ "fsl,imx-sabreauto-spdif"; ++ model = "imx-spdif"; ++ spdif-controller = <&spdif>; ++ spdif-in; ++ status = "disabled"; ++ }; ++ ++ v4l2_out { ++ compatible = "fsl,mxc_v4l2_output"; ++ status = "okay"; ++ }; ++ ++ poweroff { ++ compatible = "udoo,poweroff"; ++ sam3x_rst_gpio = <&gpio1 0 GPIO_ACTIVE_LOW>; ++ pwr_5v_gpio = <&gpio2 4 GPIO_ACTIVE_HIGH>; ++ arduino_mode = <0>; ++ }; ++}; ++ ++&ldb { ++ ipu_id = <1>; ++ disp_id = <0>; ++ ext_ref = <1>; ++ mode = "sep0"; ++ sec_ipu_id = <1>; ++ sec_disp_id = <1>; ++ status = "okay"; ++}; ++ ++&hdmi_audio { ++ status = "okay"; ++}; ++ ++&hdmi_core { ++ ipu_id = <0>; ++ disp_id = <0>; ++ status = "okay"; ++}; ++ ++&hdmi_video { ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1_2>; ++ status = "okay"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2_2>; ++ status = "okay"; ++ ++ hdmi: edid@50 { ++ compatible = "fsl,imx6-hdmi-i2c"; ++ reg = <0x50>; ++ }; ++}; ++ ++&i2c3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3_5>; ++ status = "okay"; ++ ++ touchscreen: st1232@55 { ++ compatible = "sitronix,st1232"; ++ reg = <0x55>; ++ interrupt-parent = <&gpio1>; ++ interrupts = <13 IRQ_TYPE_LEVEL_LOW>; ++ gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; ++ /* udoo poweroff driver */ ++ lcd_panel_on_gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>; ++ lcd_backlight_gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>; ++ }; ++}; ++ ++&fec { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_enet>; ++ phy-mode = "rgmii"; ++ status = "okay"; ++}; ++ ++&iomuxc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hog>; ++ ++ imx6q-udoo { ++ pinctrl_hog: hoggrp { ++ fsl,pins = < ++ MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x80000000 /* 5v enable */ ++ MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 /* Vtt suspend */ ++ MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x80000000 /* touch reset */ ++ MX6QDL_PAD_EIM_EB3__GPIO2_IO31 0x80000000 /* ethernet power */ ++ ++ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* usb hub reset */ ++ MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0 /* clk usb hub */ ++ MX6QDL_PAD_EIM_WAIT__GPIO5_IO00 0xb0b1 /* usb otg select */ ++ ++ MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x80000000 /* sdcard power */ ++ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 /* sd card detect */ ++ MX6QDL_PAD_DISP0_DAT5__GPIO4_IO26 0x80000000 /* select dbg uart*/ ++ MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x80000000 /* SAM3X reset */ ++ MX6QDL_PAD_DISP0_DAT0__GPIO4_IO21 0x30b1 /* SAM3X erase */ ++ MX6QDL_PAD_GPIO_16__GPIO7_IO11 0xb0b1 /* SAM3X vbus_en */ ++ MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 /* SAM3X usb host */ ++ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 /* panel on */ ++ MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 /* backlight on */ ++ MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x80000000 /* camera reset */ ++ MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x80000000 /* camera enable */ ++ MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x80000000 /* input mon serial*/ ++ MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x80000000 /* input mon serial*/ ++ MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x80000000 /* writeprotect spi*/ ++ MX6QDL_PAD_GPIO_3__GPIO1_IO03 0x30b1 /* arduino pinout */ ++ >; ++ }; ++ ++ pinctrl_i2c1_2: i2c1grp-2 { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1 ++ MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c2_2: i2c2grp-2 { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_i2c3_5: i2c3grp-5 { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_enet: enetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 ++ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 ++ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 ++ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 ++ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 ++ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 ++ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 ++ MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 /* reset */ ++ >; ++ }; ++ ++ pinctrl_uart2: uart2grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_uart4: uart4grp { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_i2c3_1: i2c3grp-1 { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_spdif_1: spdifgrp-1 { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0 ++ >; ++ }; ++ ++ /*pinctrl_hdmi_cec_1: hdmicecgrp-1 { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x1f8b0 ++ >; ++ };*/ ++ ++ ac97link_running: ac97link_runninggrp { ++ fsl,pins = < ++ MX6QDL_PAD_DI0_PIN2__AUD6_TXD 0x80000000 ++ MX6QDL_PAD_DI0_PIN3__AUD6_TXFS 0x80000000 ++ MX6QDL_PAD_DI0_PIN4__AUD6_RXD 0x80000000 ++ MX6QDL_PAD_DI0_PIN15__AUD6_TXC 0x80000000 ++ >; ++ }; ++ ++ ac97link_reset: ac97link_resetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x80000000 ++ MX6QDL_PAD_DI0_PIN3__GPIO4_IO19 0x80000000 ++ MX6QDL_PAD_DI0_PIN2__GPIO4_IO18 0x80000000 ++ >; ++ }; ++ ++ ac97link_warm_reset: ac97link_warm_resetgrp { ++ fsl,pins = < ++ MX6QDL_PAD_DI0_PIN3__GPIO4_IO19 0x80000000 ++ >; ++ }; ++ }; ++}; ++ ++&audmux { ++ status = "okay"; ++}; ++ ++&ssi1 { ++ fsl,mode = "ac97-slave"; ++ pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset"; ++ pinctrl-0 = <&ac97link_running>; ++ pinctrl-1 = <&ac97link_running>; ++ pinctrl-2 = <&ac97link_reset>; ++ pinctrl-3 = <&ac97link_warm_reset>; ++ /* sync, sdata (output), reset */ ++ ac97-gpios = <&gpio4 19 0 &gpio4 18 0 &gpio2 30 0>; ++ status = "okay"; ++}; ++ ++&spdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_spdif_1>; ++ status = "disabled"; + }; + + &sata { +@@ -27,13 +417,37 @@ + + &uart2 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart2_1>; ++ pinctrl-0 = <&pinctrl_uart2>; + status = "okay"; + }; + ++&uart4 { /* sam3x port */ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_uart4>; ++ status = "okay"; ++}; ++ + &usdhc3 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usdhc3_2>; ++ pinctrl-0 = <&pinctrl_usdhc3>; + non-removable; ++ keep-power-in-suspend; + status = "okay"; + }; ++ ++&usbotg { ++ status = "disabled"; ++}; ++ ++&usbh1 { ++ vbus-supply = <®_usb_h1_vbus>; ++ clocks = <&clks 201>; ++ clock-names = "phy"; ++ status = "okay"; ++}; ++ ++&hdmi_cec { ++ /*pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hdmi_cec_1>;*/ ++ status = "disabled"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6sl.dtsi linux-openelec/arch/arm/boot/dts/imx6sl.dtsi +--- linux-3.14.36/arch/arm/boot/dts/imx6sl.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6sl.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -7,12 +7,14 @@ + * + */ + ++#include + #include "skeleton.dtsi" + #include "imx6sl-pinfunc.h" + #include + + / { + aliases { ++ ethernet0 = &fec; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; +@@ -27,25 +29,46 @@ + spi1 = &ecspi2; + spi2 = &ecspi3; + spi3 = &ecspi4; ++ usbphy0 = &usbphy1; ++ usbphy1 = &usbphy2; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + +- cpu@0 { ++ cpu0: cpu@0 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <0x0>; + next-level-cache = <&L2>; ++ operating-points = < ++ /* kHz uV */ ++ 996000 1275000 ++ 792000 1175000 ++ 396000 975000 ++ >; ++ fsl,soc-operating-points = < ++ /* ARM kHz SOC-PU uV */ ++ 996000 1225000 ++ 792000 1175000 ++ 396000 1175000 ++ >; ++ clock-latency = <61036>; /* two CLK32 periods */ ++ clocks = <&clks IMX6SL_CLK_ARM>, <&clks IMX6SL_CLK_PLL2_PFD2>, ++ <&clks IMX6SL_CLK_STEP>, <&clks IMX6SL_CLK_PLL1_SW>, ++ <&clks IMX6SL_CLK_PLL1_SYS>; ++ clock-names = "arm", "pll2_pfd2_396m", "step", ++ "pll1_sw", "pll1_sys"; ++ arm-supply = <®_arm>; ++ pu-supply = <®_pu>; ++ soc-supply = <®_soc>; + }; + }; + + intc: interrupt-controller@00a01000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; +- #address-cells = <1>; +- #size-cells = <1>; + interrupt-controller; + reg = <0x00a01000 0x1000>, + <0x00a00100 0x100>; +@@ -57,15 +80,21 @@ + + ckil { + compatible = "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <32768>; + }; + + osc { + compatible = "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; + ++ pu_dummy: pudummy_reg { ++ compatible = "fsl,imx6-dummy-pureg"; /* only used in ldo-bypass */ ++ }; ++ + soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -73,19 +102,45 @@ + interrupt-parent = <&intc>; + ranges; + ++ ocram: sram@00900000 { ++ compatible = "mmio-sram"; ++ reg = <0x00900000 0x20000>; ++ clocks = <&clks IMX6SL_CLK_OCRAM>; ++ }; ++ ++ busfreq { /* BUSFREQ */ ++ compatible = "fsl,imx6_busfreq"; ++ clocks = <&clks IMX6SL_CLK_PLL2_BUS>, <&clks IMX6SL_CLK_PLL2_PFD2>, ++ <&clks IMX6SL_CLK_PLL2_198M>, <&clks IMX6SL_CLK_ARM>, ++ <&clks IMX6SL_CLK_PLL3_USB_OTG>, <&clks IMX6SL_CLK_PERIPH>, ++ <&clks IMX6SL_CLK_PRE_PERIPH_SEL>, <&clks IMX6SL_CLK_PERIPH_CLK2>, ++ <&clks IMX6SL_CLK_PERIPH_CLK2_SEL>, <&clks IMX6SL_CLK_OSC>, ++ <&clks IMX6SL_CLK_PLL1_SYS>, <&clks IMX6SL_CLK_PERIPH2>, ++ <&clks IMX6SL_CLK_AHB>, <&clks IMX6SL_CLK_OCRAM>, ++ <&clks IMX6SL_CLK_PLL1_SW>, <&clks IMX6SL_CLK_PRE_PERIPH2_SEL>, ++ <&clks IMX6SL_CLK_PERIPH2_CLK2_SEL>, <&clks IMX6SL_CLK_PERIPH2_CLK2>, ++ <&clks IMX6SL_CLK_STEP>; ++ clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", ++ "periph_pre", "periph_clk2", "periph_clk2_sel", "osc", "pll1_sys", "periph2", "ahb", "ocram", "pll1_sw", ++ "periph2_pre", "periph2_clk2_sel", "periph2_clk2", "step"; ++ fsl,max_ddr_freq = <400000000>; ++ }; ++ + L2: l2-cache@00a02000 { + compatible = "arm,pl310-cache"; + reg = <0x00a02000 0x1000>; +- interrupts = <0 92 0x04>; ++ interrupts = <0 92 IRQ_TYPE_LEVEL_HIGH>; + cache-unified; + cache-level = <2>; + arm,tag-latency = <4 2 3>; + arm,data-latency = <4 2 3>; ++ arm,dynamic-clk-gating; ++ arm,standby-mode; + }; + + pmu { + compatible = "arm,cortex-a9-pmu"; +- interrupts = <0 94 0x04>; ++ interrupts = <0 94 IRQ_TYPE_LEVEL_HIGH>; + }; + + aips1: aips-bus@02000000 { +@@ -104,7 +159,7 @@ + + spdif: spdif@02004000 { + reg = <0x02004000 0x4000>; +- interrupts = <0 52 0x04>; ++ interrupts = <0 52 IRQ_TYPE_LEVEL_HIGH>; + }; + + ecspi1: ecspi@02008000 { +@@ -112,7 +167,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x02008000 0x4000>; +- interrupts = <0 31 0x04>; ++ interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_ECSPI1>, + <&clks IMX6SL_CLK_ECSPI1>; + clock-names = "ipg", "per"; +@@ -124,7 +179,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x0200c000 0x4000>; +- interrupts = <0 32 0x04>; ++ interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_ECSPI2>, + <&clks IMX6SL_CLK_ECSPI2>; + clock-names = "ipg", "per"; +@@ -136,7 +191,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x02010000 0x4000>; +- interrupts = <0 33 0x04>; ++ interrupts = <0 33 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_ECSPI3>, + <&clks IMX6SL_CLK_ECSPI3>; + clock-names = "ipg", "per"; +@@ -148,7 +203,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x02014000 0x4000>; +- interrupts = <0 34 0x04>; ++ interrupts = <0 34 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_ECSPI4>, + <&clks IMX6SL_CLK_ECSPI4>; + clock-names = "ipg", "per"; +@@ -159,7 +214,7 @@ + compatible = "fsl,imx6sl-uart", + "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x02018000 0x4000>; +- interrupts = <0 30 0x04>; ++ interrupts = <0 30 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; +@@ -172,7 +227,7 @@ + compatible = "fsl,imx6sl-uart", + "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x02020000 0x4000>; +- interrupts = <0 26 0x04>; ++ interrupts = <0 26 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; +@@ -185,7 +240,7 @@ + compatible = "fsl,imx6sl-uart", + "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x02024000 0x4000>; +- interrupts = <0 27 0x04>; ++ interrupts = <0 27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; +@@ -195,9 +250,11 @@ + }; + + ssi1: ssi@02028000 { +- compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi"; ++ compatible = "fsl,imx6sl-ssi", ++ "fsl,imx51-ssi", ++ "fsl,imx21-ssi"; + reg = <0x02028000 0x4000>; +- interrupts = <0 46 0x04>; ++ interrupts = <0 46 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_SSI1>; + dmas = <&sdma 37 1 0>, + <&sdma 38 1 0>; +@@ -207,9 +264,11 @@ + }; + + ssi2: ssi@0202c000 { +- compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi"; ++ compatible = "fsl,imx6sl-ssi", ++ "fsl,imx51-ssi", ++ "fsl,imx21-ssi"; + reg = <0x0202c000 0x4000>; +- interrupts = <0 47 0x04>; ++ interrupts = <0 47 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_SSI2>; + dmas = <&sdma 41 1 0>, + <&sdma 42 1 0>; +@@ -219,9 +278,11 @@ + }; + + ssi3: ssi@02030000 { +- compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi"; ++ compatible = "fsl,imx6sl-ssi", ++ "fsl,imx51-ssi", ++ "fsl,imx21-ssi"; + reg = <0x02030000 0x4000>; +- interrupts = <0 48 0x04>; ++ interrupts = <0 48 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_SSI3>; + dmas = <&sdma 45 1 0>, + <&sdma 46 1 0>; +@@ -234,7 +295,7 @@ + compatible = "fsl,imx6sl-uart", + "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x02034000 0x4000>; +- interrupts = <0 28 0x04>; ++ interrupts = <0 28 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; +@@ -247,7 +308,7 @@ + compatible = "fsl,imx6sl-uart", + "fsl,imx6q-uart", "fsl,imx21-uart"; + reg = <0x02038000 0x4000>; +- interrupts = <0 29 0x04>; ++ interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; +@@ -261,7 +322,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x02080000 0x4000>; +- interrupts = <0 83 0x04>; ++ interrupts = <0 83 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_PWM1>, + <&clks IMX6SL_CLK_PWM1>; + clock-names = "ipg", "per"; +@@ -271,7 +332,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x02084000 0x4000>; +- interrupts = <0 84 0x04>; ++ interrupts = <0 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_PWM2>, + <&clks IMX6SL_CLK_PWM2>; + clock-names = "ipg", "per"; +@@ -281,7 +342,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x02088000 0x4000>; +- interrupts = <0 85 0x04>; ++ interrupts = <0 85 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_PWM3>, + <&clks IMX6SL_CLK_PWM3>; + clock-names = "ipg", "per"; +@@ -291,7 +352,7 @@ + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x0208c000 0x4000>; +- interrupts = <0 86 0x04>; ++ interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_PWM4>, + <&clks IMX6SL_CLK_PWM4>; + clock-names = "ipg", "per"; +@@ -300,7 +361,7 @@ + gpt: gpt@02098000 { + compatible = "fsl,imx6sl-gpt"; + reg = <0x02098000 0x4000>; +- interrupts = <0 55 0x04>; ++ interrupts = <0 55 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_GPT>, + <&clks IMX6SL_CLK_GPT_SERIAL>; + clock-names = "ipg", "per"; +@@ -309,7 +370,8 @@ + gpio1: gpio@0209c000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x0209c000 0x4000>; +- interrupts = <0 66 0x04 0 67 0x04>; ++ interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>, ++ <0 67 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -319,7 +381,8 @@ + gpio2: gpio@020a0000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020a0000 0x4000>; +- interrupts = <0 68 0x04 0 69 0x04>; ++ interrupts = <0 68 IRQ_TYPE_LEVEL_HIGH>, ++ <0 69 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -329,7 +392,8 @@ + gpio3: gpio@020a4000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020a4000 0x4000>; +- interrupts = <0 70 0x04 0 71 0x04>; ++ interrupts = <0 70 IRQ_TYPE_LEVEL_HIGH>, ++ <0 71 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -339,7 +403,8 @@ + gpio4: gpio@020a8000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020a8000 0x4000>; +- interrupts = <0 72 0x04 0 73 0x04>; ++ interrupts = <0 72 IRQ_TYPE_LEVEL_HIGH>, ++ <0 73 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -349,7 +414,8 @@ + gpio5: gpio@020ac000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020ac000 0x4000>; +- interrupts = <0 74 0x04 0 75 0x04>; ++ interrupts = <0 74 IRQ_TYPE_LEVEL_HIGH>, ++ <0 75 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; +@@ -357,21 +423,23 @@ + }; + + kpp: kpp@020b8000 { ++ compatible = "fsl,imx6sl-kpp", "fsl,imx21-kpp"; + reg = <0x020b8000 0x4000>; +- interrupts = <0 82 0x04>; ++ interrupts = <0 82 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks IMX6SL_CLK_DUMMY>; + }; + + wdog1: wdog@020bc000 { + compatible = "fsl,imx6sl-wdt", "fsl,imx21-wdt"; + reg = <0x020bc000 0x4000>; +- interrupts = <0 80 0x04>; ++ interrupts = <0 80 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_DUMMY>; + }; + + wdog2: wdog@020c0000 { + compatible = "fsl,imx6sl-wdt", "fsl,imx21-wdt"; + reg = <0x020c0000 0x4000>; +- interrupts = <0 81 0x04>; ++ interrupts = <0 81 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_DUMMY>; + status = "disabled"; + }; +@@ -379,7 +447,8 @@ + clks: ccm@020c4000 { + compatible = "fsl,imx6sl-ccm"; + reg = <0x020c4000 0x4000>; +- interrupts = <0 87 0x04 0 88 0x04>; ++ interrupts = <0 87 IRQ_TYPE_LEVEL_HIGH>, ++ <0 88 IRQ_TYPE_LEVEL_HIGH>; + #clock-cells = <1>; + }; + +@@ -388,7 +457,9 @@ + "fsl,imx6q-anatop", + "syscon", "simple-bus"; + reg = <0x020c8000 0x1000>; +- interrupts = <0 49 0x04 0 54 0x04 0 127 0x04>; ++ interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>, ++ <0 54 IRQ_TYPE_LEVEL_HIGH>, ++ <0 127 IRQ_TYPE_LEVEL_HIGH>; + + regulator-1p1@110 { + compatible = "fsl,anatop-regulator"; +@@ -434,7 +505,7 @@ + + reg_arm: regulator-vddcore@140 { + compatible = "fsl,anatop-regulator"; +- regulator-name = "cpu"; ++ regulator-name = "vddarm"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1450000>; + regulator-always-on; +@@ -454,7 +525,6 @@ + regulator-name = "vddpu"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1450000>; +- regulator-always-on; + anatop-reg-offset = <0x140>; + anatop-vol-bit-shift = <9>; + anatop-vol-bit-width = <5>; +@@ -484,18 +554,34 @@ + }; + }; + ++ tempmon: tempmon { ++ compatible = "fsl,imx6sl-tempmon", "fsl,imx6q-tempmon"; ++ interrupts = <0 49 0x04>; ++ fsl,tempmon = <&anatop>; ++ fsl,tempmon-data = <&ocotp>; ++ clocks = <&clks IMX6SL_CLK_PLL3_USB_OTG>; ++ }; ++ + usbphy1: usbphy@020c9000 { + compatible = "fsl,imx6sl-usbphy", "fsl,imx23-usbphy"; + reg = <0x020c9000 0x1000>; +- interrupts = <0 44 0x04>; ++ interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USBPHY1>; ++ fsl,anatop = <&anatop>; + }; + + usbphy2: usbphy@020ca000 { + compatible = "fsl,imx6sl-usbphy", "fsl,imx23-usbphy"; + reg = <0x020ca000 0x1000>; +- interrupts = <0 45 0x04>; ++ interrupts = <0 45 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USBPHY2>; ++ fsl,anatop = <&anatop>; ++ }; ++ ++ usbphy_nop1: usbphy_nop1 { ++ compatible = "usb-nop-xceiv"; ++ clocks = <&clks IMX6SL_CLK_USBPHY1>; ++ clock-names = "main_clk"; + }; + + snvs@020cc000 { +@@ -507,271 +593,165 @@ + snvs-rtc-lp@34 { + compatible = "fsl,sec-v4.0-mon-rtc-lp"; + reg = <0x34 0x58>; +- interrupts = <0 19 0x04 0 20 0x04>; ++ interrupts = <0 19 IRQ_TYPE_LEVEL_HIGH>, ++ <0 20 IRQ_TYPE_LEVEL_HIGH>; + }; +- }; +- +- epit1: epit@020d0000 { +- reg = <0x020d0000 0x4000>; +- interrupts = <0 56 0x04>; +- }; + +- epit2: epit@020d4000 { +- reg = <0x020d4000 0x4000>; +- interrupts = <0 57 0x04>; +- }; +- +- src: src@020d8000 { +- compatible = "fsl,imx6sl-src", "fsl,imx51-src"; +- reg = <0x020d8000 0x4000>; +- interrupts = <0 91 0x04 0 96 0x04>; +- #reset-cells = <1>; +- }; +- +- gpc: gpc@020dc000 { +- compatible = "fsl,imx6sl-gpc", "fsl,imx6q-gpc"; +- reg = <0x020dc000 0x4000>; +- interrupts = <0 89 0x04>; +- }; +- +- gpr: iomuxc-gpr@020e0000 { +- compatible = "fsl,imx6sl-iomuxc-gpr", +- "fsl,imx6q-iomuxc-gpr", "syscon"; +- reg = <0x020e0000 0x38>; +- }; +- +- iomuxc: iomuxc@020e0000 { +- compatible = "fsl,imx6sl-iomuxc"; +- reg = <0x020e0000 0x4000>; +- +- ecspi1 { +- pinctrl_ecspi1_1: ecspi1grp-1 { ++ csi { ++ pinctrl_csi_0: csigrp-0 { + fsl,pins = < +- MX6SL_PAD_ECSPI1_MISO__ECSPI1_MISO 0x100b1 +- MX6SL_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x100b1 +- MX6SL_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x100b1 ++ MX6SL_PAD_EPDC_GDRL__CSI_MCLK 0x110b0 ++ MX6SL_PAD_EPDC_GDCLK__CSI_PIXCLK 0x110b0 ++ MX6SL_PAD_EPDC_GDSP__CSI_VSYNC 0x110b0 ++ MX6SL_PAD_EPDC_GDOE__CSI_HSYNC 0x110b0 ++ MX6SL_PAD_EPDC_SDLE__CSI_DATA09 0x110b0 ++ MX6SL_PAD_EPDC_SDCLK__CSI_DATA08 0x110b0 ++ MX6SL_PAD_EPDC_D7__CSI_DATA07 0x110b0 ++ MX6SL_PAD_EPDC_D6__CSI_DATA06 0x110b0 ++ MX6SL_PAD_EPDC_D5__CSI_DATA05 0x110b0 ++ MX6SL_PAD_EPDC_D4__CSI_DATA04 0x110b0 ++ MX6SL_PAD_EPDC_D3__CSI_DATA03 0x110b0 ++ MX6SL_PAD_EPDC_D2__CSI_DATA02 0x110b0 ++ MX6SL_PAD_EPDC_D1__CSI_DATA01 0x110b0 ++ MX6SL_PAD_EPDC_D0__CSI_DATA00 0x110b0 ++ MX6SL_PAD_EPDC_SDSHR__GPIO1_IO26 0x80000000 ++ MX6SL_PAD_EPDC_SDOE__GPIO1_IO25 0x80000000 + >; + }; + }; + +- fec { +- pinctrl_fec_1: fecgrp-1 { ++ i2c1 { ++ pinctrl_i2c1_1: i2c1grp-1 { + fsl,pins = < +- MX6SL_PAD_FEC_MDC__FEC_MDC 0x1b0b0 +- MX6SL_PAD_FEC_MDIO__FEC_MDIO 0x1b0b0 +- MX6SL_PAD_FEC_CRS_DV__FEC_RX_DV 0x1b0b0 +- MX6SL_PAD_FEC_RXD0__FEC_RX_DATA0 0x1b0b0 +- MX6SL_PAD_FEC_RXD1__FEC_RX_DATA1 0x1b0b0 +- MX6SL_PAD_FEC_TX_EN__FEC_TX_EN 0x1b0b0 +- MX6SL_PAD_FEC_TXD0__FEC_TX_DATA0 0x1b0b0 +- MX6SL_PAD_FEC_TXD1__FEC_TX_DATA1 0x1b0b0 +- MX6SL_PAD_FEC_REF_CLK__FEC_REF_OUT 0x4001b0a8 ++ MX6SL_PAD_I2C1_SCL__I2C1_SCL 0x4001b8b1 ++ MX6SL_PAD_I2C1_SDA__I2C1_SDA 0x4001b8b1 + >; + }; + }; + +- uart1 { +- pinctrl_uart1_1: uart1grp-1 { ++ i2c2 { ++ pinctrl_i2c2_1: i2c2grp-1 { + fsl,pins = < +- MX6SL_PAD_UART1_RXD__UART1_RX_DATA 0x1b0b1 +- MX6SL_PAD_UART1_TXD__UART1_TX_DATA 0x1b0b1 ++ MX6SL_PAD_I2C2_SCL__I2C2_SCL 0x4001b8b1 ++ MX6SL_PAD_I2C2_SDA__I2C2_SDA 0x4001b8b1 + >; + }; + }; + +- usbotg1 { +- pinctrl_usbotg1_1: usbotg1grp-1 { +- fsl,pins = < +- MX6SL_PAD_EPDC_PWRCOM__USB_OTG1_ID 0x17059 +- >; +- }; +- +- pinctrl_usbotg1_2: usbotg1grp-2 { ++ i2c3 { ++ pinctrl_i2c3_1: i2c3grp-1 { + fsl,pins = < +- MX6SL_PAD_FEC_RXD0__USB_OTG1_ID 0x17059 +- >; +- }; +- +- pinctrl_usbotg1_3: usbotg1grp-3 { +- fsl,pins = < +- MX6SL_PAD_LCD_DAT1__USB_OTG1_ID 0x17059 +- >; +- }; +- +- pinctrl_usbotg1_4: usbotg1grp-4 { +- fsl,pins = < +- MX6SL_PAD_REF_CLK_32K__USB_OTG1_ID 0x17059 +- >; +- }; +- +- pinctrl_usbotg1_5: usbotg1grp-5 { +- fsl,pins = < +- MX6SL_PAD_SD3_DAT0__USB_OTG1_ID 0x17059 ++ MX6SL_PAD_EPDC_SDCE2__I2C3_SCL 0x4001b8b1 ++ MX6SL_PAD_EPDC_SDCE3__I2C3_SDA 0x4001b8b1 + >; + }; + }; + +- usbotg2 { +- pinctrl_usbotg2_1: usbotg2grp-1 { +- fsl,pins = < +- MX6SL_PAD_ECSPI1_SCLK__USB_OTG2_OC 0x17059 +- >; +- }; +- +- pinctrl_usbotg2_2: usbotg2grp-2 { ++ lcdif { ++ pinctrl_lcdif_dat_0: lcdifdatgrp-0 { + fsl,pins = < +- MX6SL_PAD_ECSPI2_SCLK__USB_OTG2_OC 0x17059 ++ MX6SL_PAD_LCD_DAT0__LCD_DATA00 0x1b0b0 ++ MX6SL_PAD_LCD_DAT1__LCD_DATA01 0x1b0b0 ++ MX6SL_PAD_LCD_DAT2__LCD_DATA02 0x1b0b0 ++ MX6SL_PAD_LCD_DAT3__LCD_DATA03 0x1b0b0 ++ MX6SL_PAD_LCD_DAT4__LCD_DATA04 0x1b0b0 ++ MX6SL_PAD_LCD_DAT5__LCD_DATA05 0x1b0b0 ++ MX6SL_PAD_LCD_DAT6__LCD_DATA06 0x1b0b0 ++ MX6SL_PAD_LCD_DAT7__LCD_DATA07 0x1b0b0 ++ MX6SL_PAD_LCD_DAT8__LCD_DATA08 0x1b0b0 ++ MX6SL_PAD_LCD_DAT9__LCD_DATA09 0x1b0b0 ++ MX6SL_PAD_LCD_DAT10__LCD_DATA10 0x1b0b0 ++ MX6SL_PAD_LCD_DAT11__LCD_DATA11 0x1b0b0 ++ MX6SL_PAD_LCD_DAT12__LCD_DATA12 0x1b0b0 ++ MX6SL_PAD_LCD_DAT13__LCD_DATA13 0x1b0b0 ++ MX6SL_PAD_LCD_DAT14__LCD_DATA14 0x1b0b0 ++ MX6SL_PAD_LCD_DAT15__LCD_DATA15 0x1b0b0 ++ MX6SL_PAD_LCD_DAT16__LCD_DATA16 0x1b0b0 ++ MX6SL_PAD_LCD_DAT17__LCD_DATA17 0x1b0b0 ++ MX6SL_PAD_LCD_DAT18__LCD_DATA18 0x1b0b0 ++ MX6SL_PAD_LCD_DAT19__LCD_DATA19 0x1b0b0 ++ MX6SL_PAD_LCD_DAT20__LCD_DATA20 0x1b0b0 ++ MX6SL_PAD_LCD_DAT21__LCD_DATA21 0x1b0b0 ++ MX6SL_PAD_LCD_DAT22__LCD_DATA22 0x1b0b0 ++ MX6SL_PAD_LCD_DAT23__LCD_DATA23 0x1b0b0 + >; + }; + +- pinctrl_usbotg2_3: usbotg2grp-3 { ++ pinctrl_lcdif_ctrl_0: lcdifctrlgrp-0 { + fsl,pins = < +- MX6SL_PAD_KEY_ROW5__USB_OTG2_OC 0x17059 +- >; +- }; +- +- pinctrl_usbotg2_4: usbotg2grp-4 { +- fsl,pins = < +- MX6SL_PAD_SD3_DAT2__USB_OTG2_OC 0x17059 ++ MX6SL_PAD_LCD_CLK__LCD_CLK 0x1b0b0 ++ MX6SL_PAD_LCD_ENABLE__LCD_ENABLE 0x1b0b0 ++ MX6SL_PAD_LCD_HSYNC__LCD_HSYNC 0x1b0b0 ++ MX6SL_PAD_LCD_VSYNC__LCD_VSYNC 0x1b0b0 ++ MX6SL_PAD_LCD_RESET__LCD_RESET 0x1b0b0 + >; + }; + }; + +- usdhc1 { +- pinctrl_usdhc1_1: usdhc1grp-1 { ++ pwm1 { ++ pinctrl_pwm1_0: pwm1grp-0 { + fsl,pins = < +- MX6SL_PAD_SD1_CMD__SD1_CMD 0x17059 +- MX6SL_PAD_SD1_CLK__SD1_CLK 0x10059 +- MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x17059 +- MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x17059 +- MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x17059 +- MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x17059 +- MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x17059 +- MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x17059 +- MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x17059 +- MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x17059 ++ MX6SL_PAD_PWM1__PWM1_OUT 0x110b0 + >; + }; +- +- pinctrl_usdhc1_1_100mhz: usdhc1grp-1-100mhz { +- fsl,pins = < +- MX6SL_PAD_SD1_CMD__SD1_CMD 0x170b9 +- MX6SL_PAD_SD1_CLK__SD1_CLK 0x100b9 +- MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170b9 +- MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170b9 +- MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170b9 +- MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170b9 +- MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170b9 +- MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170b9 +- MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170b9 +- MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170b9 +- >; +- }; +- +- pinctrl_usdhc1_1_200mhz: usdhc1grp-1-200mhz { +- fsl,pins = < +- MX6SL_PAD_SD1_CMD__SD1_CMD 0x170f9 +- MX6SL_PAD_SD1_CLK__SD1_CLK 0x100f9 +- MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170f9 +- MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170f9 +- MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170f9 +- MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170f9 +- MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170f9 +- MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170f9 +- MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170f9 +- MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170f9 +- >; +- }; +- +- + }; ++ }; + +- usdhc2 { +- pinctrl_usdhc2_1: usdhc2grp-1 { +- fsl,pins = < +- MX6SL_PAD_SD2_CMD__SD2_CMD 0x17059 +- MX6SL_PAD_SD2_CLK__SD2_CLK 0x10059 +- MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x17059 +- MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x17059 +- MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x17059 +- MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x17059 +- >; +- }; +- +- pinctrl_usdhc2_1_100mhz: usdhc2grp-1-100mhz { +- fsl,pins = < +- MX6SL_PAD_SD2_CMD__SD2_CMD 0x170b9 +- MX6SL_PAD_SD2_CLK__SD2_CLK 0x100b9 +- MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 +- MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 +- MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 +- MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170b9 +- >; +- }; ++ epit1: epit@020d0000 { ++ reg = <0x020d0000 0x4000>; ++ interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; ++ }; + +- pinctrl_usdhc2_1_200mhz: usdhc2grp-1-200mhz { +- fsl,pins = < +- MX6SL_PAD_SD2_CMD__SD2_CMD 0x170f9 +- MX6SL_PAD_SD2_CLK__SD2_CLK 0x100f9 +- MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 +- MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 +- MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 +- MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170f9 +- >; +- }; ++ epit2: epit@020d4000 { ++ reg = <0x020d4000 0x4000>; ++ interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; ++ }; + +- }; ++ src: src@020d8000 { ++ compatible = "fsl,imx6sl-src", "fsl,imx51-src"; ++ reg = <0x020d8000 0x4000>; ++ interrupts = <0 91 IRQ_TYPE_LEVEL_HIGH>, ++ <0 96 IRQ_TYPE_LEVEL_HIGH>; ++ #reset-cells = <1>; ++ }; + +- usdhc3 { +- pinctrl_usdhc3_1: usdhc3grp-1 { +- fsl,pins = < +- MX6SL_PAD_SD3_CMD__SD3_CMD 0x17059 +- MX6SL_PAD_SD3_CLK__SD3_CLK 0x10059 +- MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x17059 +- MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x17059 +- MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x17059 +- MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x17059 +- >; +- }; ++ gpc: gpc@020dc000 { ++ compatible = "fsl,imx6sl-gpc", "fsl,imx6q-gpc"; ++ reg = <0x020dc000 0x4000>; ++ interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks IMX6SL_CLK_GPU2D_PODF>, <&clks IMX6SL_CLK_GPU2D_OVG>, ++ <&clks IMX6SL_CLK_IPG>; ++ clock-names = "gpu2d_podf", "gpu2d_ovg", "ipg"; ++ pu-supply = <®_pu>; ++ }; + +- pinctrl_usdhc3_1_100mhz: usdhc3grp-1-100mhz { +- fsl,pins = < +- MX6SL_PAD_SD3_CMD__SD3_CMD 0x170b9 +- MX6SL_PAD_SD3_CLK__SD3_CLK 0x100b9 +- MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170b9 +- MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170b9 +- MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170b9 +- MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170b9 +- >; +- }; ++ gpr: iomuxc-gpr@020e0000 { ++ compatible = "fsl,imx6sl-iomuxc-gpr", ++ "fsl,imx6q-iomuxc-gpr", "syscon"; ++ reg = <0x020e0000 0x38>; ++ }; + +- pinctrl_usdhc3_1_200mhz: usdhc3grp-1-200mhz { +- fsl,pins = < +- MX6SL_PAD_SD3_CMD__SD3_CMD 0x170f9 +- MX6SL_PAD_SD3_CLK__SD3_CLK 0x100f9 +- MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170f9 +- MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170f9 +- MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170f9 +- MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170f9 +- >; +- }; +- }; ++ iomuxc: iomuxc@020e0000 { ++ compatible = "fsl,imx6sl-iomuxc"; ++ reg = <0x020e0000 0x4000>; + }; + + csi: csi@020e4000 { ++ compatible = "fsl,imx6sl-csi"; + reg = <0x020e4000 0x4000>; +- interrupts = <0 7 0x04>; ++ interrupts = <0 7 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; + }; + + spdc: spdc@020e8000 { + reg = <0x020e8000 0x4000>; +- interrupts = <0 6 0x04>; ++ interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>; + }; + + sdma: sdma@020ec000 { + compatible = "fsl,imx6sl-sdma", "fsl,imx35-sdma"; + reg = <0x020ec000 0x4000>; +- interrupts = <0 2 0x04>; ++ interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_SDMA>, + <&clks IMX6SL_CLK_SDMA>; + clock-names = "ipg", "ahb"; +@@ -781,23 +761,32 @@ + }; + + pxp: pxp@020f0000 { ++ compatible = "fsl,imx6sl-pxp-dma", "fsl,imx6dl-pxp-dma"; + reg = <0x020f0000 0x4000>; +- interrupts = <0 98 0x04>; ++ interrupts = <0 98 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks 111>; ++ clock-names = "pxp-axi"; ++ status = "disabled"; + }; + + epdc: epdc@020f4000 { + reg = <0x020f4000 0x4000>; +- interrupts = <0 97 0x04>; ++ interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>; + }; + + lcdif: lcdif@020f8000 { ++ compatible = "fsl,imx6sl-lcdif", "fsl,imx28-lcdif"; + reg = <0x020f8000 0x4000>; +- interrupts = <0 39 0x04>; ++ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks IMX6SL_CLK_LCDIF_PIX>, ++ <&clks IMX6SL_CLK_LCDIF_AXI>; ++ clock-names = "pix", "axi"; ++ status = "disabled"; + }; + + dcp: dcp@020fc000 { + reg = <0x020fc000 0x4000>; +- interrupts = <0 99 0x04>; ++ interrupts = <0 99 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + +@@ -811,7 +800,7 @@ + usbotg1: usb@02184000 { + compatible = "fsl,imx6sl-usb", "fsl,imx27-usb"; + reg = <0x02184000 0x200>; +- interrupts = <0 43 0x04>; ++ interrupts = <0 43 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USBOH3>; + fsl,usbphy = <&usbphy1>; + fsl,usbmisc = <&usbmisc 0>; +@@ -821,7 +810,7 @@ + usbotg2: usb@02184200 { + compatible = "fsl,imx6sl-usb", "fsl,imx27-usb"; + reg = <0x02184200 0x200>; +- interrupts = <0 42 0x04>; ++ interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USBOH3>; + fsl,usbphy = <&usbphy2>; + fsl,usbmisc = <&usbmisc 1>; +@@ -831,9 +820,12 @@ + usbh: usb@02184400 { + compatible = "fsl,imx6sl-usb", "fsl,imx27-usb"; + reg = <0x02184400 0x200>; +- interrupts = <0 40 0x04>; ++ interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USBOH3>; + fsl,usbmisc = <&usbmisc 2>; ++ phy_type = "hsic"; ++ fsl,usbphy = <&usbphy_nop1>; ++ fsl,anatop = <&anatop>; + status = "disabled"; + }; + +@@ -847,7 +839,7 @@ + fec: ethernet@02188000 { + compatible = "fsl,imx6sl-fec", "fsl,imx25-fec"; + reg = <0x02188000 0x4000>; +- interrupts = <0 114 0x04>; ++ interrupts = <0 114 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_ENET_REF>, + <&clks IMX6SL_CLK_ENET_REF>; + clock-names = "ipg", "ahb"; +@@ -857,7 +849,7 @@ + usdhc1: usdhc@02190000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x02190000 0x4000>; +- interrupts = <0 22 0x04>; ++ interrupts = <0 22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USDHC1>, + <&clks IMX6SL_CLK_USDHC1>, + <&clks IMX6SL_CLK_USDHC1>; +@@ -869,7 +861,7 @@ + usdhc2: usdhc@02194000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x02194000 0x4000>; +- interrupts = <0 23 0x04>; ++ interrupts = <0 23 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USDHC2>, + <&clks IMX6SL_CLK_USDHC2>, + <&clks IMX6SL_CLK_USDHC2>; +@@ -881,7 +873,7 @@ + usdhc3: usdhc@02198000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x02198000 0x4000>; +- interrupts = <0 24 0x04>; ++ interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USDHC3>, + <&clks IMX6SL_CLK_USDHC3>, + <&clks IMX6SL_CLK_USDHC3>; +@@ -893,7 +885,7 @@ + usdhc4: usdhc@0219c000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x0219c000 0x4000>; +- interrupts = <0 25 0x04>; ++ interrupts = <0 25 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_USDHC4>, + <&clks IMX6SL_CLK_USDHC4>, + <&clks IMX6SL_CLK_USDHC4>; +@@ -907,7 +899,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c"; + reg = <0x021a0000 0x4000>; +- interrupts = <0 36 0x04>; ++ interrupts = <0 36 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_I2C1>; + status = "disabled"; + }; +@@ -917,7 +909,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c"; + reg = <0x021a4000 0x4000>; +- interrupts = <0 37 0x04>; ++ interrupts = <0 37 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_I2C2>; + status = "disabled"; + }; +@@ -927,7 +919,7 @@ + #size-cells = <0>; + compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c"; + reg = <0x021a8000 0x4000>; +- interrupts = <0 38 0x04>; ++ interrupts = <0 38 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_I2C3>; + status = "disabled"; + }; +@@ -939,17 +931,23 @@ + + rngb: rngb@021b4000 { + reg = <0x021b4000 0x4000>; +- interrupts = <0 5 0x04>; ++ interrupts = <0 5 IRQ_TYPE_LEVEL_HIGH>; + }; + + weim: weim@021b8000 { + reg = <0x021b8000 0x4000>; +- interrupts = <0 14 0x04>; ++ interrupts = <0 14 IRQ_TYPE_LEVEL_HIGH>; ++ }; ++ ++ ocotp: ocotp-ctrl@021bc000 { ++ compatible = "syscon"; ++ reg = <0x021bc000 0x4000>; + }; + +- ocotp: ocotp@021bc000 { +- compatible = "fsl,imx6sl-ocotp"; ++ ocotp-fuse@021bc000 { ++ compatible = "fsl,imx6sl-ocotp", "fsl,imx6q-ocotp"; + reg = <0x021bc000 0x4000>; ++ clocks = <&clks IMX6SL_CLK_OCOTP>; + }; + + audmux: audmux@021d8000 { +@@ -957,6 +955,25 @@ + reg = <0x021d8000 0x4000>; + status = "disabled"; + }; ++ ++ gpu: gpu@02200000 { ++ compatible = "fsl,imx6sl-gpu", "fsl,imx6q-gpu"; ++ reg = <0x02200000 0x4000>, <0x02204000 0x4000>, ++ <0x80000000 0x0>; ++ reg-names = "iobase_2d", "iobase_vg", ++ "phys_baseaddr"; ++ interrupts = <0 10 0x04>, <0 11 0x04>; ++ interrupt-names = "irq_2d", "irq_vg"; ++ clocks = <&clks IMX6SL_CLK_MMDC_ROOT>, ++ <&clks IMX6SL_CLK_MMDC_ROOT>, ++ <&clks IMX6SL_CLK_GPU2D_OVG>; ++ clock-names = "gpu2d_axi_clk", "openvg_axi_clk", ++ "gpu2d_clk"; ++ resets = <&src 3>, <&src 3>; ++ reset-names = "gpu2d", "gpuvg"; ++ pu-supply = <®_pu>; ++ }; ++ + }; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6sl-evk-csi.dts linux-openelec/arch/arm/boot/dts/imx6sl-evk-csi.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6sl-evk-csi.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/imx6sl-evk-csi.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (C) 2013 Freescale Semiconductor, Inc. ++ * ++ * 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 "imx6sl-evk.dts" ++ ++/ { ++ csi_v4l2_cap { ++ status = "okay"; ++ }; ++}; ++ ++&csi { ++ status = "okay"; ++}; ++ ++&i2c3 { ++ status = "okay"; ++}; ++ ++&epdc { ++ status = "disabled"; ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/imx6sl-evk.dts linux-openelec/arch/arm/boot/dts/imx6sl-evk.dts +--- linux-3.14.36/arch/arm/boot/dts/imx6sl-evk.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/imx6sl-evk.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -8,6 +8,8 @@ + + /dts-v1/; + ++#include ++#include + #include "imx6sl.dtsi" + + / { +@@ -18,11 +20,26 @@ + reg = <0x80000000 0x40000000>; + }; + ++ leds { ++ compatible = "gpio-leds"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_led>; ++ ++ user { ++ label = "debug"; ++ gpios = <&gpio3 20 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ }; ++ + regulators { + compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; + +- reg_usb_otg1_vbus: usb_otg1_vbus { ++ reg_usb_otg1_vbus: regulator@0 { + compatible = "regulator-fixed"; ++ reg = <0>; + regulator-name = "usb_otg1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; +@@ -30,22 +47,63 @@ + enable-active-high; + }; + +- reg_usb_otg2_vbus: usb_otg2_vbus { ++ reg_usb_otg2_vbus: regulator@1 { + compatible = "regulator-fixed"; ++ reg = <1>; + regulator-name = "usb_otg2_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio4 2 0>; + enable-active-high; + }; ++ ++ reg_aud3v: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "wm8962-supply-3v15"; ++ regulator-min-microvolt = <3150000>; ++ regulator-max-microvolt = <3150000>; ++ regulator-boot-on; ++ }; ++ ++ reg_aud4v: regulator@3 { ++ compatible = "regulator-fixed"; ++ reg = <3>; ++ regulator-name = "wm8962-supply-4v2"; ++ regulator-min-microvolt = <4325000>; ++ regulator-max-microvolt = <4325000>; ++ regulator-boot-on; ++ }; + }; ++ ++ sound { ++ compatible = "fsl,imx6sl-evk-wm8962", "fsl,imx-audio-wm8962"; ++ model = "wm8962-audio"; ++ ssi-controller = <&ssi2>; ++ audio-codec = <&codec>; ++ audio-routing = ++ "Headphone Jack", "HPOUTL", ++ "Headphone Jack", "HPOUTR", ++ "Ext Spk", "SPKOUTL", ++ "Ext Spk", "SPKOUTR", ++ "AMIC", "MICBIAS", ++ "IN3R", "AMIC"; ++ mux-int-port = <2>; ++ mux-ext-port = <3>; ++ }; ++}; ++ ++&audmux { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_audmux3>; ++ status = "okay"; + }; + + &ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio4 11 0>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_ecspi1_1>; ++ pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { +@@ -57,18 +115,326 @@ + }; + }; + ++&csi { ++ status = "okay"; ++}; ++ ++&cpu0 { ++ arm-supply = <&sw1a_reg>; ++ soc-supply = <&sw1c_reg>; ++ pu-supply = <&pu_dummy>; /* use pu_dummy if VDDSOC share with VDDPU */ ++}; ++ + &fec { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_fec_1>; ++ pinctrl-0 = <&pinctrl_fec>; + phy-mode = "rmii"; + status = "okay"; + }; + ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1>; ++ status = "okay"; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ regulator-always-on; ++ }; ++ ++ vgen2_reg: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen3_reg: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ ++ reg_lcd_3v3: lcd-3v3 { ++ compatible = "regulator-fixed"; ++ regulator-name = "lcd-3v3"; ++ gpio = <&gpio4 3 0>; ++ enable-active-high; ++ }; ++ }; ++ ++ backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm1 0 5000000>; ++ brightness-levels = <0 4 8 16 32 64 128 255>; ++ default-brightness-level = <6>; ++ }; ++ ++ csi_v4l2_cap { ++ compatible = "fsl,imx6sl-csi-v4l2"; ++ status = "okay"; ++ }; ++ ++ pxp_v4l2_out { ++ compatible = "fsl,imx6sl-pxp-v4l2"; ++ status = "okay"; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2>; ++ status = "okay"; ++ ++ codec: wm8962@1a { ++ compatible = "wlf,wm8962"; ++ reg = <0x1a>; ++ clocks = <&clks IMX6SL_CLK_EXTERN_AUDIO>; ++ DCVDD-supply = <&vgen3_reg>; ++ DBVDD-supply = <®_aud3v>; ++ AVDD-supply = <&vgen3_reg>; ++ CPVDD-supply = <&vgen3_reg>; ++ MICVDD-supply = <®_aud3v>; ++ PLLVDD-supply = <&vgen3_reg>; ++ SPKVDD1-supply = <®_aud4v>; ++ SPKVDD2-supply = <®_aud4v>; ++ }; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c1_1>; ++ status = "okay"; ++ ++ pmic: pfuze100@08 { ++ compatible = "fsl,pfuze100"; ++ reg = <0x08>; ++ ++ regulators { ++ sw1a_reg: sw1ab { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw1c_reg: sw1c { ++ regulator-min-microvolt = <300000>; ++ regulator-max-microvolt = <1875000>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-ramp-delay = <6250>; ++ }; ++ ++ sw2_reg: sw2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3a_reg: sw3a { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw3b_reg: sw3b { ++ regulator-min-microvolt = <400000>; ++ regulator-max-microvolt = <1975000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ sw4_reg: sw4 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ swbst_reg: swbst { ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5150000>; ++ }; ++ ++ snvs_reg: vsnvs { ++ regulator-min-microvolt = <1000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vref_reg: vrefddr { ++ regulator-boot-on; ++ regulator-always-on; ++ }; ++ ++ vgen1_reg: vgen1 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen2_reg: vgen2 { ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1550000>; ++ }; ++ ++ vgen3_reg: vgen3 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen4_reg: vgen4 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen5_reg: vgen5 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vgen6_reg: vgen6 { ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ }; ++ }; ++ ++ mma8450@1c { ++ compatible = "fsl,mma8450"; ++ reg = <0x1c>; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c2_1>; ++ status = "okay"; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_i2c3_1>; ++ status = "okay"; ++ ++ ov564x: ov564x@3c { ++ compatible = "ovti,ov564x"; ++ reg = <0x3c>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_csi_0>; ++ clocks = <&clks IMX6SL_CLK_CSI>; ++ clock-names = "csi_mclk"; ++ AVDD-supply = <&vgen6_reg>; /* 2.8v */ ++ DVDD-supply = <&vgen2_reg>; /* 1.5v*/ ++ pwn-gpios = <&gpio1 25 1>; ++ rst-gpios = <&gpio1 26 0>; ++ csi_id = <0>; ++ mclk = <24000000>; ++ mclk_source = <0>; ++ }; ++}; ++ + &iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + +- hog { ++ imx6sl-evk { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6SL_PAD_KEY_ROW7__GPIO4_IO07 0x17059 +@@ -78,21 +444,270 @@ + MX6SL_PAD_REF_CLK_32K__GPIO3_IO22 0x17059 + MX6SL_PAD_KEY_COL4__GPIO4_IO00 0x80000000 + MX6SL_PAD_KEY_COL5__GPIO4_IO02 0x80000000 ++ MX6SL_PAD_AUD_MCLK__AUDIO_CLK_OUT 0x4130b0 ++ >; ++ }; ++ ++ pinctrl_audmux3: audmux3grp { ++ fsl,pins = < ++ MX6SL_PAD_AUD_RXD__AUD3_RXD 0x4130b0 ++ MX6SL_PAD_AUD_TXC__AUD3_TXC 0x4130b0 ++ MX6SL_PAD_AUD_TXD__AUD3_TXD 0x4110b0 ++ MX6SL_PAD_AUD_TXFS__AUD3_TXFS 0x4130b0 ++ >; ++ }; ++ ++ pinctrl_ecspi1: ecspi1grp { ++ fsl,pins = < ++ MX6SL_PAD_ECSPI1_MISO__ECSPI1_MISO 0x100b1 ++ MX6SL_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x100b1 ++ MX6SL_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x100b1 ++ MX6SL_PAD_ECSPI1_SS0__GPIO4_IO11 0x80000000 ++ >; ++ }; ++ ++ pinctrl_fec: fecgrp { ++ fsl,pins = < ++ MX6SL_PAD_FEC_MDC__FEC_MDC 0x1b0b0 ++ MX6SL_PAD_FEC_MDIO__FEC_MDIO 0x1b0b0 ++ MX6SL_PAD_FEC_CRS_DV__FEC_RX_DV 0x1b0b0 ++ MX6SL_PAD_FEC_RXD0__FEC_RX_DATA0 0x1b0b0 ++ MX6SL_PAD_FEC_RXD1__FEC_RX_DATA1 0x1b0b0 ++ MX6SL_PAD_FEC_TX_EN__FEC_TX_EN 0x1b0b0 ++ MX6SL_PAD_FEC_TXD0__FEC_TX_DATA0 0x1b0b0 ++ MX6SL_PAD_FEC_TXD1__FEC_TX_DATA1 0x1b0b0 ++ MX6SL_PAD_FEC_REF_CLK__FEC_REF_OUT 0x4001b0a8 ++ >; ++ }; ++ ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6SL_PAD_I2C1_SCL__I2C1_SCL 0x4001b8b1 ++ MX6SL_PAD_I2C1_SDA__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ ++ pinctrl_i2c2: i2c2grp { ++ fsl,pins = < ++ MX6SL_PAD_I2C2_SCL__I2C2_SCL 0x4001b8b1 ++ MX6SL_PAD_I2C2_SDA__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_led: ledgrp { ++ fsl,pins = < ++ MX6SL_PAD_HSIC_STROBE__GPIO3_IO20 0x17059 ++ >; ++ }; ++ ++ pinctrl_kpp: kppgrp { ++ fsl,pins = < ++ MX6SL_PAD_KEY_ROW0__KEY_ROW0 0x1b010 ++ MX6SL_PAD_KEY_ROW1__KEY_ROW1 0x1b010 ++ MX6SL_PAD_KEY_ROW2__KEY_ROW2 0x1b0b0 ++ MX6SL_PAD_KEY_COL0__KEY_COL0 0x110b0 ++ MX6SL_PAD_KEY_COL1__KEY_COL1 0x110b0 ++ MX6SL_PAD_KEY_COL2__KEY_COL2 0x110b0 ++ >; ++ }; ++ ++ pinctrl_uart1: uart1grp { ++ fsl,pins = < ++ MX6SL_PAD_UART1_RXD__UART1_RX_DATA 0x1b0b1 ++ MX6SL_PAD_UART1_TXD__UART1_TX_DATA 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_usbotg1: usbotg1grp { ++ fsl,pins = < ++ MX6SL_PAD_EPDC_PWRCOM__USB_OTG1_ID 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc1: usdhc1grp { ++ fsl,pins = < ++ MX6SL_PAD_SD1_CMD__SD1_CMD 0x17059 ++ MX6SL_PAD_SD1_CLK__SD1_CLK 0x10059 ++ MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x17059 ++ MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x17059 ++ MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x17059 ++ MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x17059 ++ MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x17059 ++ MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x17059 ++ MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x17059 ++ MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc1_100mhz: usdhc1grp100mhz { ++ fsl,pins = < ++ MX6SL_PAD_SD1_CMD__SD1_CMD 0x170b9 ++ MX6SL_PAD_SD1_CLK__SD1_CLK 0x100b9 ++ MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170b9 ++ MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170b9 ++ MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170b9 ++ MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170b9 ++ MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170b9 ++ MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170b9 ++ MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170b9 ++ MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170b9 ++ >; ++ }; ++ ++ pinctrl_usdhc1_200mhz: usdhc1grp200mhz { ++ fsl,pins = < ++ MX6SL_PAD_SD1_CMD__SD1_CMD 0x170f9 ++ MX6SL_PAD_SD1_CLK__SD1_CLK 0x100f9 ++ MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170f9 ++ MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170f9 ++ MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170f9 ++ MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170f9 ++ MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170f9 ++ MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170f9 ++ MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170f9 ++ MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170f9 ++ >; ++ }; ++ ++ pinctrl_usdhc2: usdhc2grp { ++ fsl,pins = < ++ MX6SL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6SL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc2_100mhz: usdhc2grp100mhz { ++ fsl,pins = < ++ MX6SL_PAD_SD2_CMD__SD2_CMD 0x170b9 ++ MX6SL_PAD_SD2_CLK__SD2_CLK 0x100b9 ++ MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 ++ MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 ++ MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 ++ MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170b9 ++ >; ++ }; ++ ++ pinctrl_usdhc2_200mhz: usdhc2grp200mhz { ++ fsl,pins = < ++ MX6SL_PAD_SD2_CMD__SD2_CMD 0x170f9 ++ MX6SL_PAD_SD2_CLK__SD2_CLK 0x100f9 ++ MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 ++ MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 ++ MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 ++ MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170f9 ++ >; ++ }; ++ ++ pinctrl_usdhc3: usdhc3grp { ++ fsl,pins = < ++ MX6SL_PAD_SD3_CMD__SD3_CMD 0x17059 ++ MX6SL_PAD_SD3_CLK__SD3_CLK 0x10059 ++ MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x17059 ++ MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x17059 ++ MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x17059 ++ MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x17059 ++ >; ++ }; ++ ++ pinctrl_usdhc3_100mhz: usdhc3grp100mhz { ++ fsl,pins = < ++ MX6SL_PAD_SD3_CMD__SD3_CMD 0x170b9 ++ MX6SL_PAD_SD3_CLK__SD3_CLK 0x100b9 ++ MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170b9 ++ MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170b9 ++ MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170b9 ++ MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170b9 ++ >; ++ }; ++ ++ pinctrl_usdhc3_200mhz: usdhc3grp200mhz { ++ fsl,pins = < ++ MX6SL_PAD_SD3_CMD__SD3_CMD 0x170f9 ++ MX6SL_PAD_SD3_CLK__SD3_CLK 0x100f9 ++ MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170f9 ++ MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170f9 ++ MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170f9 ++ MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170f9 + >; + }; + }; + }; + ++&kpp { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_kpp>; ++ linux,keymap = < ++ MATRIX_KEY(0x0, 0x0, KEY_UP) /* ROW0, COL0 */ ++ MATRIX_KEY(0x0, 0x1, KEY_DOWN) /* ROW0, COL1 */ ++ MATRIX_KEY(0x0, 0x2, KEY_ENTER) /* ROW0, COL2 */ ++ MATRIX_KEY(0x1, 0x0, KEY_HOME) /* ROW1, COL0 */ ++ MATRIX_KEY(0x1, 0x1, KEY_RIGHT) /* ROW1, COL1 */ ++ MATRIX_KEY(0x1, 0x2, KEY_LEFT) /* ROW1, COL2 */ ++ MATRIX_KEY(0x2, 0x0, KEY_VOLUMEDOWN) /* ROW2, COL0 */ ++ MATRIX_KEY(0x2, 0x1, KEY_VOLUMEUP) /* ROW2, COL1 */ ++ >; ++ status = "okay"; ++}; ++ ++&ssi2 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ ++&lcdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_lcdif_dat_0 ++ &pinctrl_lcdif_ctrl_0>; ++ lcd-supply = <®_lcd_3v3>; ++ display = <&display>; ++ status = "okay"; ++ ++ display: display { ++ bits-per-pixel = <16>; ++ bus-width = <24>; ++ ++ display-timings { ++ native-mode = <&timing0>; ++ timing0: timing0 { ++ clock-frequency = <33500000>; ++ hactive = <800>; ++ vactive = <480>; ++ hback-porch = <89>; ++ hfront-porch = <164>; ++ vback-porch = <23>; ++ vfront-porch = <10>; ++ hsync-len = <10>; ++ vsync-len = <10>; ++ hsync-active = <0>; ++ vsync-active = <0>; ++ de-active = <1>; ++ pixelclk-active = <0>; ++ }; ++ }; ++ }; ++}; ++ ++&pwm1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_pwm1_0>; ++ status = "okay"; ++}; ++ + &uart1 { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_uart1_1>; ++ pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; + }; + + &usbotg1 { + vbus-supply = <®_usb_otg1_vbus>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usbotg1_1>; ++ pinctrl-0 = <&pinctrl_usbotg1>; + disable-over-current; + status = "okay"; + }; +@@ -106,9 +721,9 @@ + + &usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; +- pinctrl-0 = <&pinctrl_usdhc1_1>; +- pinctrl-1 = <&pinctrl_usdhc1_1_100mhz>; +- pinctrl-2 = <&pinctrl_usdhc1_1_200mhz>; ++ pinctrl-0 = <&pinctrl_usdhc1>; ++ pinctrl-1 = <&pinctrl_usdhc1_100mhz>; ++ pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + bus-width = <8>; + cd-gpios = <&gpio4 7 0>; + wp-gpios = <&gpio4 6 0>; +@@ -117,9 +732,9 @@ + + &usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; +- pinctrl-0 = <&pinctrl_usdhc2_1>; +- pinctrl-1 = <&pinctrl_usdhc2_1_100mhz>; +- pinctrl-2 = <&pinctrl_usdhc2_1_200mhz>; ++ pinctrl-0 = <&pinctrl_usdhc2>; ++ pinctrl-1 = <&pinctrl_usdhc2_100mhz>; ++ pinctrl-2 = <&pinctrl_usdhc2_200mhz>; + cd-gpios = <&gpio5 0 0>; + wp-gpios = <&gpio4 29 0>; + status = "okay"; +@@ -127,9 +742,26 @@ + + &usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; +- pinctrl-0 = <&pinctrl_usdhc3_1>; +- pinctrl-1 = <&pinctrl_usdhc3_1_100mhz>; +- pinctrl-2 = <&pinctrl_usdhc3_1_200mhz>; ++ pinctrl-0 = <&pinctrl_usdhc3>; ++ pinctrl-1 = <&pinctrl_usdhc3_100mhz>; ++ pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + cd-gpios = <&gpio3 22 0>; + status = "okay"; + }; ++ ++&pxp { ++ status = "okay"; ++}; ++ ++&gpc { ++ fsl,cpu_pupscr_sw2iso = <0xf>; ++ fsl,cpu_pupscr_sw = <0xf>; ++ fsl,cpu_pdnscr_iso2sw = <0x1>; ++ fsl,cpu_pdnscr_iso = <0x1>; ++ fsl,ldo-bypass; /* use ldo-bypass, u-boot will check it and configure */ ++ pu-supply = <&pu_dummy>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ ++}; ++ ++&gpu { ++ pu-supply = <&pu_dummy>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ ++}; +diff -Nur linux-3.14.36/arch/arm/boot/dts/Makefile linux-openelec/arch/arm/boot/dts/Makefile +--- linux-3.14.36/arch/arm/boot/dts/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/Makefile 2015-07-24 18:03:30.200842002 -0500 +@@ -154,16 +154,38 @@ + imx53-qsb.dtb \ + imx53-smd.dtb \ + imx6dl-cubox-i.dtb \ ++ imx6dl-dfi-fs700-m60.dtb \ ++ imx6dl-gw51xx.dtb \ ++ imx6dl-gw52xx.dtb \ ++ imx6dl-gw53xx.dtb \ ++ imx6dl-gw54xx.dtb \ + imx6dl-hummingboard.dtb \ ++ imx6dl-nitrogen6x.dtb \ ++ imx6dl-phytec-pbab01.dtb \ + imx6dl-sabreauto.dtb \ ++ imx6dl-sabrelite.dtb \ + imx6dl-sabresd.dtb \ ++ imx6dl-sabresd-hdcp.dtb \ + imx6dl-wandboard.dtb \ + imx6q-arm2.dtb \ ++ imx6q-cm-fx6.dtb \ + imx6q-cubox-i.dtb \ ++ imx6q-hummingboard.dtb \ ++ imx6q-dfi-fs700-m60.dtb \ ++ imx6q-dmo-edmqmx6.dtb \ ++ imx6q-gk802.dtb \ ++ imx6q-gw51xx.dtb \ ++ imx6q-gw52xx.dtb \ ++ imx6q-gw53xx.dtb \ ++ imx6q-gw5400-a.dtb \ ++ imx6q-gw54xx.dtb \ ++ imx6q-nitrogen6x.dtb \ + imx6q-phytec-pbab01.dtb \ + imx6q-sabreauto.dtb \ + imx6q-sabrelite.dtb \ + imx6q-sabresd.dtb \ ++ imx6q-sabresd-hdcp.dtb \ ++ imx6q-tbs2910.dtb \ + imx6q-sbc6x.dtb \ + imx6q-udoo.dtb \ + imx6q-wandboard.dtb \ +@@ -312,7 +334,14 @@ + dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \ + vexpress-v2p-ca9.dtb \ + vexpress-v2p-ca15-tc1.dtb \ +- vexpress-v2p-ca15_a7.dtb ++ vexpress-v2p-ca15_a7.dtb \ ++ rtsm_ve-cortex_a9x2.dtb \ ++ rtsm_ve-cortex_a9x4.dtb \ ++ rtsm_ve-cortex_a15x1.dtb \ ++ rtsm_ve-cortex_a15x2.dtb \ ++ rtsm_ve-cortex_a15x4.dtb \ ++ rtsm_ve-v2p-ca15x1-ca7x1.dtb \ ++ rtsm_ve-v2p-ca15x4-ca7x4.dtb + dtb-$(CONFIG_ARCH_VIRT) += xenvm-4.2.dtb + dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \ + wm8505-ref.dtb \ +diff -Nur linux-3.14.36/arch/arm/boot/dts/marco.dtsi linux-openelec/arch/arm/boot/dts/marco.dtsi +--- linux-3.14.36/arch/arm/boot/dts/marco.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/marco.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -36,7 +36,7 @@ + ranges = <0x40000000 0x40000000 0xa0000000>; + + l2-cache-controller@c0030000 { +- compatible = "sirf,marco-pl310-cache", "arm,pl310-cache"; ++ compatible = "arm,pl310-cache"; + reg = <0xc0030000 0x1000>; + interrupts = <0 59 0>; + arm,tag-latency = <1 1 1>; +diff -Nur linux-3.14.36/arch/arm/boot/dts/prima2.dtsi linux-openelec/arch/arm/boot/dts/prima2.dtsi +--- linux-3.14.36/arch/arm/boot/dts/prima2.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/prima2.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -48,7 +48,7 @@ + ranges = <0x40000000 0x40000000 0x80000000>; + + l2-cache-controller@80040000 { +- compatible = "arm,pl310-cache", "sirf,prima2-pl310-cache"; ++ compatible = "arm,pl310-cache"; + reg = <0x80040000 0x1000>; + interrupts = <59>; + arm,tag-latency = <1 1 1>; +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,159 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * ARMCortexA15x1CT ++ * ++ * RTSM_VE_Cortex_A15x1.lisa ++ */ ++ ++/dts-v1/; ++ ++/ { ++ model = "RTSM_VE_CortexA15x1"; ++ arm,vexpress,site = <0xf>; ++ compatible = "arm,rtsm_ve,cortex_a15x1", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <0>; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0 0x80000000 0 0x80000000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0 0x2c001000 0 0x1000>, ++ <0 0x2c002000 0 0x1000>, ++ <0 0x2c004000 0 0x2000>, ++ <0 0x2c006000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ }; ++ ++ dcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ osc@0 { ++ /* ACLK clock to the AXI master port on the test chip */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 0>; ++ freq-range = <30000000 50000000>; ++ #clock-cells = <0>; ++ clock-output-names = "extsaxiclk"; ++ }; ++ ++ oscclk1: osc@1 { ++ /* Reference clock for the CLCD */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <10000000 80000000>; ++ #clock-cells = <0>; ++ clock-output-names = "clcdclk"; ++ }; ++ ++ smbclk: oscclk2: osc@2 { ++ /* Reference clock for the test chip internal PLLs */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 2>; ++ freq-range = <33000000 100000000>; ++ #clock-cells = <0>; ++ clock-output-names = "tcrefclk"; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,165 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * ARMCortexA15x2CT ++ * ++ * RTSM_VE_Cortex_A15x2.lisa ++ */ ++ ++/dts-v1/; ++ ++/ { ++ model = "RTSM_VE_CortexA15x2"; ++ arm,vexpress,site = <0xf>; ++ compatible = "arm,rtsm_ve,cortex_a15x2", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <1>; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0 0x80000000 0 0x80000000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0 0x2c001000 0 0x1000>, ++ <0 0x2c002000 0 0x1000>, ++ <0 0x2c004000 0 0x2000>, ++ <0 0x2c006000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ }; ++ ++ dcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ osc@0 { ++ /* ACLK clock to the AXI master port on the test chip */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 0>; ++ freq-range = <30000000 50000000>; ++ #clock-cells = <0>; ++ clock-output-names = "extsaxiclk"; ++ }; ++ ++ oscclk1: osc@1 { ++ /* Reference clock for the CLCD */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <10000000 80000000>; ++ #clock-cells = <0>; ++ clock-output-names = "clcdclk"; ++ }; ++ ++ smbclk: oscclk2: osc@2 { ++ /* Reference clock for the test chip internal PLLs */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 2>; ++ freq-range = <33000000 100000000>; ++ #clock-cells = <0>; ++ clock-output-names = "tcrefclk"; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,177 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * ARMCortexA15x4CT ++ * ++ * RTSM_VE_Cortex_A15x4.lisa ++ */ ++ ++/dts-v1/; ++ ++/ { ++ model = "RTSM_VE_CortexA15x4"; ++ arm,vexpress,site = <0xf>; ++ compatible = "arm,rtsm_ve,cortex_a15x4", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <1>; ++ }; ++ ++ cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <2>; ++ }; ++ ++ cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <3>; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0 0x80000000 0 0x80000000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0 0x2c001000 0 0x1000>, ++ <0 0x2c002000 0 0x1000>, ++ <0 0x2c004000 0 0x2000>, ++ <0 0x2c006000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ }; ++ ++ dcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ osc@0 { ++ /* ACLK clock to the AXI master port on the test chip */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 0>; ++ freq-range = <30000000 50000000>; ++ #clock-cells = <0>; ++ clock-output-names = "extsaxiclk"; ++ }; ++ ++ oscclk1: osc@1 { ++ /* Reference clock for the CLCD */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <10000000 80000000>; ++ #clock-cells = <0>; ++ clock-output-names = "clcdclk"; ++ }; ++ ++ smbclk: oscclk2: osc@2 { ++ /* Reference clock for the test chip internal PLLs */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 2>; ++ freq-range = <33000000 100000000>; ++ #clock-cells = <0>; ++ clock-output-names = "tcrefclk"; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,171 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * ARMCortexA9MPx2CT ++ * ++ * RTSM_VE_Cortex_A9x2.lisa ++ */ ++ ++/dts-v1/; ++ ++/ { ++ model = "RTSM_VE_CortexA9x2"; ++ arm,vexpress,site = <0xf>; ++ compatible = "arm,rtsm_ve,cortex_a9x2", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a9"; ++ reg = <0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a9"; ++ reg = <1>; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x80000000 0x80000000>; ++ }; ++ ++ scu@2c000000 { ++ compatible = "arm,cortex-a9-scu"; ++ reg = <0x2c000000 0x58>; ++ }; ++ ++ timer@2c000600 { ++ compatible = "arm,cortex-a9-twd-timer"; ++ reg = <0x2c000600 0x20>; ++ interrupts = <1 13 0xf04>; ++ }; ++ ++ watchdog@2c000620 { ++ compatible = "arm,cortex-a9-twd-wdt"; ++ reg = <0x2c000620 0x20>; ++ interrupts = <1 14 0xf04>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0x2c001000 0x1000>, ++ <0x2c000100 0x100>; ++ }; ++ ++ dcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ osc@0 { ++ /* ACLK clock to the AXI master port on the test chip */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 0>; ++ freq-range = <30000000 50000000>; ++ #clock-cells = <0>; ++ clock-output-names = "extsaxiclk"; ++ }; ++ ++ oscclk1: osc@1 { ++ /* Reference clock for the CLCD */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <10000000 80000000>; ++ #clock-cells = <0>; ++ clock-output-names = "clcdclk"; ++ }; ++ ++ smbclk: oscclk2: osc@2 { ++ /* Reference clock for the test chip internal PLLs */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 2>; ++ freq-range = <33000000 100000000>; ++ #clock-cells = <0>; ++ clock-output-names = "tcrefclk"; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0x08000000 0x04000000>, ++ <1 0 0x14000000 0x04000000>, ++ <2 0 0x18000000 0x04000000>, ++ <3 0 0x1c000000 0x04000000>, ++ <4 0 0x0c000000 0x04000000>, ++ <5 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,183 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * ARMCortexA9MPx4CT ++ * ++ * RTSM_VE_Cortex_A9x4.lisa ++ */ ++ ++/dts-v1/; ++ ++/ { ++ model = "RTSM_VE_CortexA9x4"; ++ arm,vexpress,site = <0xf>; ++ compatible = "arm,rtsm_ve,cortex_a9x4", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a9"; ++ reg = <0>; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a9"; ++ reg = <1>; ++ }; ++ ++ cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a9"; ++ reg = <2>; ++ }; ++ ++ cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a9"; ++ reg = <3>; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x80000000 0x80000000>; ++ }; ++ ++ scu@2c000000 { ++ compatible = "arm,cortex-a9-scu"; ++ reg = <0x2c000000 0x58>; ++ }; ++ ++ timer@2c000600 { ++ compatible = "arm,cortex-a9-twd-timer"; ++ reg = <0x2c000600 0x20>; ++ interrupts = <1 13 0xf04>; ++ }; ++ ++ watchdog@2c000620 { ++ compatible = "arm,cortex-a9-twd-wdt"; ++ reg = <0x2c000620 0x20>; ++ interrupts = <1 14 0xf04>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0x2c001000 0x1000>, ++ <0x2c000100 0x100>; ++ }; ++ ++ dcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ osc@0 { ++ /* ACLK clock to the AXI master port on the test chip */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 0>; ++ freq-range = <30000000 50000000>; ++ #clock-cells = <0>; ++ clock-output-names = "extsaxiclk"; ++ }; ++ ++ oscclk1: osc@1 { ++ /* Reference clock for the CLCD */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <10000000 80000000>; ++ #clock-cells = <0>; ++ clock-output-names = "clcdclk"; ++ }; ++ ++ smbclk: oscclk2: osc@2 { ++ /* Reference clock for the test chip internal PLLs */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 2>; ++ freq-range = <33000000 100000000>; ++ #clock-cells = <0>; ++ clock-output-names = "tcrefclk"; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0x08000000 0x04000000>, ++ <1 0 0x14000000 0x04000000>, ++ <2 0 0x18000000 0x04000000>, ++ <3 0 0x1c000000 0x04000000>, ++ <4 0 0x0c000000 0x04000000>, ++ <5 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi linux-openelec/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,231 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * Motherboard component ++ * ++ * VEMotherBoard.lisa ++ */ ++ ++ motherboard { ++ compatible = "arm,vexpress,v2m-p1", "simple-bus"; ++ arm,hbi = <0x190>; ++ arm,vexpress,site = <0>; ++ arm,v2m-memory-map = "rs1"; ++ #address-cells = <2>; /* SMB chipselect number and offset */ ++ #size-cells = <1>; ++ #interrupt-cells = <1>; ++ ranges; ++ ++ flash@0,00000000 { ++ compatible = "arm,vexpress-flash", "cfi-flash"; ++ reg = <0 0x00000000 0x04000000>, ++ <4 0x00000000 0x04000000>; ++ bank-width = <4>; ++ }; ++ ++ vram@2,00000000 { ++ compatible = "arm,vexpress-vram"; ++ reg = <2 0x00000000 0x00800000>; ++ }; ++ ++ ethernet@2,02000000 { ++ compatible = "smsc,lan91c111"; ++ reg = <2 0x02000000 0x10000>; ++ interrupts = <15>; ++ }; ++ ++ iofpga@3,00000000 { ++ compatible = "arm,amba-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 3 0 0x200000>; ++ ++ v2m_sysreg: sysreg@010000 { ++ compatible = "arm,vexpress-sysreg"; ++ reg = <0x010000 0x1000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ v2m_sysctl: sysctl@020000 { ++ compatible = "arm,sp810", "arm,primecell"; ++ reg = <0x020000 0x1000>; ++ clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&smbclk>; ++ clock-names = "refclk", "timclk", "apb_pclk"; ++ #clock-cells = <1>; ++ clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3"; ++ }; ++ ++ aaci@040000 { ++ compatible = "arm,pl041", "arm,primecell"; ++ reg = <0x040000 0x1000>; ++ interrupts = <11>; ++ clocks = <&smbclk>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ mmci@050000 { ++ compatible = "arm,pl180", "arm,primecell"; ++ reg = <0x050000 0x1000>; ++ interrupts = <9 10>; ++ cd-gpios = <&v2m_sysreg 0 0>; ++ wp-gpios = <&v2m_sysreg 1 0>; ++ max-frequency = <12000000>; ++ vmmc-supply = <&v2m_fixed_3v3>; ++ clocks = <&v2m_clk24mhz>, <&smbclk>; ++ clock-names = "mclk", "apb_pclk"; ++ }; ++ ++ kmi@060000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x060000 0x1000>; ++ interrupts = <12>; ++ clocks = <&v2m_clk24mhz>, <&smbclk>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ kmi@070000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x070000 0x1000>; ++ interrupts = <13>; ++ clocks = <&v2m_clk24mhz>, <&smbclk>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ v2m_serial0: uart@090000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x090000 0x1000>; ++ interrupts = <5>; ++ clocks = <&v2m_clk24mhz>, <&smbclk>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial1: uart@0a0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0a0000 0x1000>; ++ interrupts = <6>; ++ clocks = <&v2m_clk24mhz>, <&smbclk>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial2: uart@0b0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0b0000 0x1000>; ++ interrupts = <7>; ++ clocks = <&v2m_clk24mhz>, <&smbclk>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial3: uart@0c0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0c0000 0x1000>; ++ interrupts = <8>; ++ clocks = <&v2m_clk24mhz>, <&smbclk>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ wdt@0f0000 { ++ compatible = "arm,sp805", "arm,primecell"; ++ reg = <0x0f0000 0x1000>; ++ interrupts = <0>; ++ clocks = <&v2m_refclk32khz>, <&smbclk>; ++ clock-names = "wdogclk", "apb_pclk"; ++ }; ++ ++ v2m_timer01: timer@110000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x110000 0x1000>; ++ interrupts = <2>; ++ clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&smbclk>; ++ clock-names = "timclken1", "timclken2", "apb_pclk"; ++ }; ++ ++ v2m_timer23: timer@120000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x120000 0x1000>; ++ interrupts = <3>; ++ clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&smbclk>; ++ clock-names = "timclken1", "timclken2", "apb_pclk"; ++ }; ++ ++ rtc@170000 { ++ compatible = "arm,pl031", "arm,primecell"; ++ reg = <0x170000 0x1000>; ++ interrupts = <4>; ++ clocks = <&smbclk>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ clcd@1f0000 { ++ compatible = "arm,pl111", "arm,primecell"; ++ reg = <0x1f0000 0x1000>; ++ interrupts = <14>; ++ clocks = <&v2m_oscclk1>, <&smbclk>; ++ clock-names = "v2m:oscclk1", "apb_pclk"; ++ mode = "VGA"; ++ use_dma = <0>; ++ framebuffer = <0x18000000 0x00180000>; ++ }; ++ ++ virtio_block@0130000 { ++ compatible = "virtio,mmio"; ++ reg = <0x130000 0x200>; ++ interrupts = <42>; ++ }; ++ ++ }; ++ ++ v2m_fixed_3v3: fixedregulator@0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "3V3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ v2m_clk24mhz: clk24mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ clock-output-names = "v2m:clk24mhz"; ++ }; ++ ++ v2m_refclk1mhz: refclk1mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <1000000>; ++ clock-output-names = "v2m:refclk1mhz"; ++ }; ++ ++ v2m_refclk32khz: refclk32khz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <32768>; ++ clock-output-names = "v2m:refclk32khz"; ++ }; ++ ++ mcc { ++ compatible = "simple-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ v2m_oscclk1: osc@1 { ++ /* CLCD clock */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <23750000 63500000>; ++ #clock-cells = <0>; ++ clock-output-names = "v2m:oscclk1"; ++ }; ++ ++ muxfpga@0 { ++ compatible = "arm,vexpress-muxfpga"; ++ arm,vexpress-sysreg,func = <7 0>; ++ }; ++ ++ shutdown@0 { ++ compatible = "arm,vexpress-shutdown"; ++ arm,vexpress-sysreg,func = <8 0>; ++ }; ++ }; ++ }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts linux-openelec/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,233 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * ARMCortexA15x4CT ++ * ARMCortexA7x4CT ++ * RTSM_VE_Cortex_A15x1_A7x1.lisa ++ */ ++ ++/dts-v1/; ++ ++/memreserve/ 0xff000000 0x01000000; ++ ++/ { ++ model = "RTSM_VE_CortexA15x1-A7x1"; ++ arm,vexpress,site = <0xf>; ++ compatible = "arm,rtsm_ve,cortex_a15x1_a7x1", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ clusters { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cluster0: cluster@0 { ++ reg = <0>; ++// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>; ++ cores { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ core0: core@0 { ++ reg = <0>; ++ }; ++ ++ }; ++ }; ++ ++ cluster1: cluster@1 { ++ reg = <1>; ++// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>; ++ cores { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ core1: core@0 { ++ reg = <0>; ++ }; ++ ++ }; ++ }; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <0>; ++ cluster = <&cluster0>; ++ core = <&core0>; ++// clock-frequency = <1000000000>; ++ cci-control-port = <&cci_control1>; ++ }; ++ ++ cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x100>; ++ cluster = <&cluster1>; ++ core = <&core1>; ++// clock-frequency = <800000000>; ++ cci-control-port = <&cci_control2>; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0 0x80000000 0 0x80000000>; ++ }; ++ ++ cci@2c090000 { ++ compatible = "arm,cci-400", "arm,cci"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0 0x2c090000 0 0x1000>; ++ ranges = <0x0 0x0 0x2c090000 0x10000>; ++ ++ cci_control1: slave-if@4000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x4000 0x1000>; ++ }; ++ ++ cci_control2: slave-if@5000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x5000 0x1000>; ++ }; ++ }; ++ ++ dcscb@60000000 { ++ compatible = "arm,rtsm,dcscb"; ++ reg = <0 0x60000000 0 0x1000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0 0x2c001000 0 0x1000>, ++ <0 0x2c002000 0 0x1000>, ++ <0 0x2c004000 0 0x2000>, ++ <0 0x2c006000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ }; ++ ++ dcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ osc@0 { ++ /* ACLK clock to the AXI master port on the test chip */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 0>; ++ freq-range = <30000000 50000000>; ++ #clock-cells = <0>; ++ clock-output-names = "extsaxiclk"; ++ }; ++ ++ oscclk1: osc@1 { ++ /* Reference clock for the CLCD */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <10000000 80000000>; ++ #clock-cells = <0>; ++ clock-output-names = "clcdclk"; ++ }; ++ ++ smbclk: oscclk2: osc@2 { ++ /* Reference clock for the test chip internal PLLs */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 2>; ++ freq-range = <33000000 100000000>; ++ #clock-cells = <0>; ++ clock-output-names = "tcrefclk"; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts linux-openelec/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts +--- linux-3.14.36/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,317 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * ARMCortexA15x4CT ++ * ARMCortexA7x4CT ++ * RTSM_VE_Cortex_A15x4_A7x4.lisa ++ */ ++ ++/dts-v1/; ++ ++/memreserve/ 0xff000000 0x01000000; ++ ++/ { ++ model = "RTSM_VE_CortexA15x4-A7x4"; ++ arm,vexpress,site = <0xf>; ++ compatible = "arm,rtsm_ve,cortex_a15x4_a7x4", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ clusters { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cluster0: cluster@0 { ++ reg = <0>; ++// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>; ++ cores { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ core0: core@0 { ++ reg = <0>; ++ }; ++ ++ core1: core@1 { ++ reg = <1>; ++ }; ++ ++ core2: core@2 { ++ reg = <2>; ++ }; ++ ++ core3: core@3 { ++ reg = <3>; ++ }; ++ ++ }; ++ }; ++ ++ cluster1: cluster@1 { ++ reg = <1>; ++// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>; ++ cores { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ core4: core@0 { ++ reg = <0>; ++ }; ++ ++ core5: core@1 { ++ reg = <1>; ++ }; ++ ++ core6: core@2 { ++ reg = <2>; ++ }; ++ ++ core7: core@3 { ++ reg = <3>; ++ }; ++ ++ }; ++ }; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <0>; ++ cluster = <&cluster0>; ++ core = <&core0>; ++// clock-frequency = <1000000000>; ++ cci-control-port = <&cci_control1>; ++ }; ++ ++ cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <1>; ++ cluster = <&cluster0>; ++ core = <&core1>; ++// clock-frequency = <1000000000>; ++ cci-control-port = <&cci_control1>; ++ }; ++ ++ cpu2: cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <2>; ++ cluster = <&cluster0>; ++ core = <&core2>; ++// clock-frequency = <1000000000>; ++ cci-control-port = <&cci_control1>; ++ }; ++ ++ cpu3: cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <3>; ++ cluster = <&cluster0>; ++ core = <&core3>; ++// clock-frequency = <1000000000>; ++ cci-control-port = <&cci_control1>; ++ }; ++ ++ cpu4: cpu@4 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x100>; ++ cluster = <&cluster1>; ++ core = <&core4>; ++// clock-frequency = <800000000>; ++ cci-control-port = <&cci_control2>; ++ }; ++ ++ cpu5: cpu@5 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x101>; ++ cluster = <&cluster1>; ++ core = <&core5>; ++// clock-frequency = <800000000>; ++ cci-control-port = <&cci_control2>; ++ }; ++ ++ cpu6: cpu@6 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x102>; ++ cluster = <&cluster1>; ++ core = <&core6>; ++// clock-frequency = <800000000>; ++ cci-control-port = <&cci_control2>; ++ }; ++ ++ cpu7: cpu@7 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x103>; ++ cluster = <&cluster1>; ++ core = <&core7>; ++// clock-frequency = <800000000>; ++ cci-control-port = <&cci_control2>; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0 0x80000000 0 0x80000000>; ++ }; ++ ++ cci@2c090000 { ++ compatible = "arm,cci-400", "arm,cci"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0 0x2c090000 0 0x1000>; ++ ranges = <0x0 0x0 0x2c090000 0x10000>; ++ ++ cci_control1: slave-if@4000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x4000 0x1000>; ++ }; ++ ++ cci_control2: slave-if@5000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x5000 0x1000>; ++ }; ++ }; ++ ++ dcscb@60000000 { ++ compatible = "arm,rtsm,dcscb"; ++ reg = <0 0x60000000 0 0x1000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0 0x2c001000 0 0x1000>, ++ <0 0x2c002000 0 0x1000>, ++ <0 0x2c004000 0 0x2000>, ++ <0 0x2c006000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ }; ++ ++ dcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ osc@0 { ++ /* ACLK clock to the AXI master port on the test chip */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 0>; ++ freq-range = <30000000 50000000>; ++ #clock-cells = <0>; ++ clock-output-names = "extsaxiclk"; ++ }; ++ ++ oscclk1: osc@1 { ++ /* Reference clock for the CLCD */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <10000000 80000000>; ++ #clock-cells = <0>; ++ clock-output-names = "clcdclk"; ++ }; ++ ++ smbclk: oscclk2: osc@2 { ++ /* Reference clock for the test chip internal PLLs */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 2>; ++ freq-range = <33000000 100000000>; ++ #clock-cells = <0>; ++ clock-output-names = "tcrefclk"; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm/boot/dts/vexpress-v2m.dtsi linux-openelec/arch/arm/boot/dts/vexpress-v2m.dtsi +--- linux-3.14.36/arch/arm/boot/dts/vexpress-v2m.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vexpress-v2m.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -227,6 +227,7 @@ + }; + + clcd@1f000 { ++ status = "disabled"; + compatible = "arm,pl111", "arm,primecell"; + reg = <0x1f000 0x1000>; + interrupts = <14>; +diff -Nur linux-3.14.36/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi linux-openelec/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi +--- linux-3.14.36/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -228,6 +228,7 @@ + }; + + clcd@1f0000 { ++ status = "disabled"; + compatible = "arm,pl111", "arm,primecell"; + reg = <0x1f0000 0x1000>; + interrupts = <14>; +diff -Nur linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +--- linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -9,6 +9,8 @@ + + /dts-v1/; + ++/memreserve/ 0xff000000 0x01000000; ++ + / { + model = "V2P-CA15_CA7"; + arm,hbi = <0x249>; +@@ -29,29 +31,60 @@ + i2c1 = &v2m_i2c_pcie; + }; + +- cpus { ++ clusters { + #address-cells = <1>; + #size-cells = <0>; + +- cpu0: cpu@0 { +- device_type = "cpu"; +- compatible = "arm,cortex-a15"; ++ cluster0: cluster@0 { + reg = <0>; +- cci-control-port = <&cci_control1>; ++ cores { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ core0: core@0 { ++ reg = <0>; ++ }; ++ ++ core1: core@1 { ++ reg = <1>; ++ }; ++ ++ }; + }; + +- cpu1: cpu@1 { +- device_type = "cpu"; +- compatible = "arm,cortex-a15"; ++ cluster1: cluster@1 { + reg = <1>; +- cci-control-port = <&cci_control1>; ++ cores { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ core2: core@0 { ++ reg = <0>; ++ }; ++ ++ core3: core@1 { ++ reg = <1>; ++ }; ++ ++ core4: core@2 { ++ reg = <2>; ++ }; ++ }; + }; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x100>; + cci-control-port = <&cci_control2>; ++ cluster = <&cluster1>; ++ core = <&core2>; ++ clock-frequency = <800000000>; + }; + + cpu3: cpu@3 { +@@ -59,6 +92,9 @@ + compatible = "arm,cortex-a7"; + reg = <0x101>; + cci-control-port = <&cci_control2>; ++ cluster = <&cluster1>; ++ core = <&core3>; ++ clock-frequency = <800000000>; + }; + + cpu4: cpu@4 { +@@ -66,12 +102,35 @@ + compatible = "arm,cortex-a7"; + reg = <0x102>; + cci-control-port = <&cci_control2>; ++ cluster = <&cluster1>; ++ core = <&core4>; ++ clock-frequency = <800000000>; ++ }; ++ ++ cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <0>; ++ cci-control-port = <&cci_control1>; ++ cluster = <&cluster0>; ++ core = <&core0>; ++ clock-frequency = <1000000000>; ++ }; ++ ++ cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a15"; ++ reg = <1>; ++ cci-control-port = <&cci_control1>; ++ cluster = <&cluster0>; ++ core = <&core1>; ++ clock-frequency = <1000000000>; + }; + }; + + memory@80000000 { + device_type = "memory"; +- reg = <0 0x80000000 0 0x40000000>; ++ reg = <0 0x80000000 0 0x80000000>; + }; + + wdt@2a490000 { +@@ -86,6 +145,8 @@ + compatible = "arm,hdlcd"; + reg = <0 0x2b000000 0 0x1000>; + interrupts = <0 85 4>; ++ mode = "1024x768-16@60"; ++ framebuffer = <0 0xff000000 0 0x01000000>; + clocks = <&oscclk5>; + clock-names = "pxlclk"; + }; +@@ -127,6 +188,16 @@ + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; ++ ++ pmu@9000 { ++ compatible = "arm,cci-400-pmu"; ++ reg = <0x9000 0x5000>; ++ interrupts = <0 101 4>, ++ <0 102 4>, ++ <0 103 4>, ++ <0 104 4>, ++ <0 105 4>; ++ }; + }; + + memory-controller@7ffd0000 { +@@ -164,12 +235,21 @@ + <1 10 0xf08>; + }; + +- pmu { ++ pmu_a15 { + compatible = "arm,cortex-a15-pmu"; ++ cluster = <&cluster0>; + interrupts = <0 68 4>, + <0 69 4>; + }; + ++ pmu_a7 { ++ compatible = "arm,cortex-a7-pmu"; ++ cluster = <&cluster1>; ++ interrupts = <0 128 4>, ++ <0 129 4>, ++ <0 130 4>; ++ }; ++ + oscclk6a: oscclk6a { + /* Reference 24MHz clock */ + compatible = "fixed-clock"; +@@ -178,6 +258,19 @@ + clock-output-names = "oscclk6a"; + }; + ++/* PSCI requires support from firmware and is not present in the normal TC2 ++ * distribution, so this node is commented out by default... ++ ++ psci { ++ compatible = "arm,psci"; ++ method = "smc"; ++ cpu_suspend = <0x80100001>; ++ cpu_off = <0x80100002>; ++ cpu_on = <0x80100003>; ++ migrate = <0x80100004>; ++ }; ++*/ ++ + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; +diff -Nur linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts +--- linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -9,6 +9,8 @@ + + /dts-v1/; + ++/memreserve/ 0xbf000000 0x01000000; ++ + / { + model = "V2P-CA15"; + arm,hbi = <0x237>; +@@ -57,6 +59,8 @@ + interrupts = <0 85 4>; + clocks = <&oscclk5>; + clock-names = "pxlclk"; ++ mode = "1024x768-16@60"; ++ framebuffer = <0 0xbf000000 0 0x01000000>; + }; + + memory-controller@2b0a0000 { +diff -Nur linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca5s.dts linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca5s.dts +--- linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca5s.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca5s.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -9,6 +9,8 @@ + + /dts-v1/; + ++/memreserve/ 0xbf000000 0x01000000; ++ + / { + model = "V2P-CA5s"; + arm,hbi = <0x225>; +@@ -59,6 +61,8 @@ + interrupts = <0 85 4>; + clocks = <&oscclk3>; + clock-names = "pxlclk"; ++ mode = "640x480-16@60"; ++ framebuffer = <0xbf000000 0x01000000>; + }; + + memory-controller@2a150000 { +diff -Nur linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca9.dts linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca9.dts +--- linux-3.14.36/arch/arm/boot/dts/vexpress-v2p-ca9.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vexpress-v2p-ca9.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -9,6 +9,8 @@ + + /dts-v1/; + ++/include/ "clcd-panels.dtsi" ++ + / { + model = "V2P-CA9"; + arm,hbi = <0x191>; +@@ -73,6 +75,8 @@ + interrupts = <0 44 4>; + clocks = <&oscclk1>, <&oscclk2>; + clock-names = "clcdclk", "apb_pclk"; ++ mode = "XVGA"; ++ use_dma = <1>; + }; + + memory-controller@100e0000 { +diff -Nur linux-3.14.36/arch/arm/boot/dts/vf610.dtsi linux-openelec/arch/arm/boot/dts/vf610.dtsi +--- linux-3.14.36/arch/arm/boot/dts/vf610.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vf610.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -44,11 +44,13 @@ + + sxosc { + compatible = "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <32768>; + }; + + fxosc { + compatible = "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <24000000>; + }; + }; +diff -Nur linux-3.14.36/arch/arm/boot/dts/vf610-twr.dts linux-openelec/arch/arm/boot/dts/vf610-twr.dts +--- linux-3.14.36/arch/arm/boot/dts/vf610-twr.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/boot/dts/vf610-twr.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -25,11 +25,13 @@ + clocks { + audio_ext { + compatible = "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <24576000>; + }; + + enet_ext { + compatible = "fixed-clock"; ++ #clock-cells = <0>; + clock-frequency = <50000000>; + }; + }; +diff -Nur linux-3.14.36/arch/arm/common/Makefile linux-openelec/arch/arm/common/Makefile +--- linux-3.14.36/arch/arm/common/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/common/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -13,6 +13,7 @@ + obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o + obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o + obj-$(CONFIG_MCPM) += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o ++CFLAGS_REMOVE_mcpm_entry.o = -pg + AFLAGS_mcpm_head.o := -march=armv7-a + AFLAGS_vlock.o := -march=armv7-a + obj-$(CONFIG_TI_PRIV_EDMA) += edma.o +diff -Nur linux-3.14.36/arch/arm/configs/imx_v6_v7_defconfig linux-openelec/arch/arm/configs/imx_v6_v7_defconfig +--- linux-3.14.36/arch/arm/configs/imx_v6_v7_defconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/configs/imx_v6_v7_defconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -45,6 +45,9 @@ + CONFIG_AEABI=y + CONFIG_HIGHMEM=y + CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" ++CONFIG_CPU_FREQ=y ++CONFIG_ARM_IMX6Q_CPUFREQ=y ++CONFIG_CPU_IDLE=y + CONFIG_VFP=y + CONFIG_NEON=y + CONFIG_BINFMT_MISC=m +@@ -70,6 +73,8 @@ + CONFIG_DEVTMPFS=y + CONFIG_DEVTMPFS_MOUNT=y + # CONFIG_STANDALONE is not set ++CONFIG_CMA=y ++CONFIG_CMA_SIZE_MBYTES=256 + CONFIG_IMX_WEIM=y + CONFIG_CONNECTOR=y + CONFIG_MTD=y +@@ -154,7 +159,12 @@ + CONFIG_SPI_IMX=y + CONFIG_GPIO_SYSFS=y + CONFIG_GPIO_MC9S08DZ60=y ++CONFIG_GPIO_PCA953X=y + # CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_CPU_THERMAL=y ++CONFIG_IMX_THERMAL=y ++CONFIG_DEVICE_THERMAL=y + CONFIG_WATCHDOG=y + CONFIG_IMX2_WDT=y + CONFIG_MFD_DA9052_I2C=y +@@ -170,32 +180,44 @@ + CONFIG_REGULATOR_PFUZE100=y + CONFIG_MEDIA_SUPPORT=y + CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=m + CONFIG_MEDIA_RC_SUPPORT=y + CONFIG_RC_DEVICES=y + CONFIG_IR_GPIO_CIR=y + CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_MXC_OUTPUT=y ++CONFIG_VIDEO_MXC_IPU_OUTPUT=y + CONFIG_SOC_CAMERA=y + CONFIG_VIDEO_MX3=y + CONFIG_V4L_MEM2MEM_DRIVERS=y + CONFIG_VIDEO_CODA=y + CONFIG_SOC_CAMERA_OV2640=y + CONFIG_DRM=y ++CONFIG_DRM_VIVANTE=y + CONFIG_BACKLIGHT_LCD_SUPPORT=y + CONFIG_LCD_CLASS_DEVICE=y + CONFIG_LCD_L4F00242T03=y + CONFIG_LCD_PLATFORM=y + CONFIG_BACKLIGHT_CLASS_DEVICE=y + CONFIG_BACKLIGHT_PWM=y ++CONFIG_FB_MXC_SYNC_PANEL=y ++CONFIG_FB_MXC_LDB=y ++CONFIG_FB_MXC_HDMI=y ++CONFIG_FB_MXC_MIPI_DSI=y ++CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y + CONFIG_FRAMEBUFFER_CONSOLE=y + CONFIG_LOGO=y + CONFIG_SOUND=y + CONFIG_SND=y ++CONFIG_SND_USB_AUDIO=m + CONFIG_SND_SOC=y + CONFIG_SND_IMX_SOC=y + CONFIG_SND_SOC_PHYCORE_AC97=y + CONFIG_SND_SOC_EUKREA_TLV320=y + CONFIG_SND_SOC_IMX_WM8962=y + CONFIG_SND_SOC_IMX_SGTL5000=y ++CONFIG_SND_SOC_IMX_CS42888=y + CONFIG_SND_SOC_IMX_SPDIF=y + CONFIG_SND_SOC_IMX_MC13783=y + CONFIG_USB=y +@@ -208,12 +230,18 @@ + CONFIG_NOP_USB_XCEIV=y + CONFIG_USB_MXS_PHY=y + CONFIG_USB_GADGET=y ++CONFIG_USB_ZERO=m + CONFIG_USB_ETH=m + CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m + CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y + CONFIG_MMC_SDHCI=y + CONFIG_MMC_SDHCI_PLTFM=y + CONFIG_MMC_SDHCI_ESDHC_IMX=y ++CONFIG_MXC_IPU=y ++CONFIG_MXC_GPU_VIV=y ++CONFIG_MXC_ASRC=y + CONFIG_NEW_LEDS=y + CONFIG_LEDS_CLASS=y + CONFIG_LEDS_GPIO=y +@@ -229,16 +257,10 @@ + CONFIG_RTC_DRV_MXC=y + CONFIG_RTC_DRV_SNVS=y + CONFIG_DMADEVICES=y ++CONFIG_MXC_PXP_V2=y + CONFIG_IMX_SDMA=y + CONFIG_MXS_DMA=y + CONFIG_STAGING=y +-CONFIG_DRM_IMX=y +-CONFIG_DRM_IMX_FB_HELPER=y +-CONFIG_DRM_IMX_PARALLEL_DISPLAY=y +-CONFIG_DRM_IMX_TVE=y +-CONFIG_DRM_IMX_LDB=y +-CONFIG_DRM_IMX_IPUV3_CORE=y +-CONFIG_DRM_IMX_IPUV3=y + CONFIG_COMMON_CLK_DEBUG=y + # CONFIG_IOMMU_SUPPORT is not set + CONFIG_PWM=y +diff -Nur linux-3.14.36/arch/arm/configs/imx_v7_cbi_hb_base_defconfig linux-openelec/arch/arm/configs/imx_v7_cbi_hb_base_defconfig +--- linux-3.14.36/arch/arm/configs/imx_v7_cbi_hb_base_defconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/configs/imx_v7_cbi_hb_base_defconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,367 @@ ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_KERNEL_LZO=y ++CONFIG_SYSVIPC=y ++CONFIG_FHANDLE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_LOG_BUF_SHIFT=18 ++CONFIG_CGROUPS=y ++CONFIG_RELAY=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EXPERT=y ++CONFIG_PERF_EVENTS=y ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_ZSWAP=y ++CONFIG_ZSMALLOC=y ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++# CONFIG_BLK_DEV_BSG is not set ++CONFIG_GPIO_PCA953X=y ++CONFIG_ARCH_MXC=y ++CONFIG_MXC_DEBUG_BOARD=y ++CONFIG_SOC_IMX6Q=y ++CONFIG_SOC_IMX6SL=y ++# CONFIG_SWP_EMULATE is not set ++CONFIG_PCI=y ++CONFIG_PCIE_DW=y ++CONFIG_PCI_IMX6=y ++CONFIG_SMP=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_PREEMPT_VOLUNTARY=y ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++CONFIG_HIGHMEM=y ++CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_ARM_IMX6_CPUFREQ=y ++CONFIG_CPU_IDLE=y ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++CONFIG_BINFMT_MISC=m ++CONFIG_PM_RUNTIME=y ++CONFIG_PM_DEBUG=y ++CONFIG_PM_TEST_SUSPEND=y ++CONFIG_IOSCHED_BFQ=y ++CONFIG_CGROUP_BFQIO=y ++CONFIG_DEFAULT_BFQ=y ++CONFIG_DEFAULT_IOSCHED="bfq" ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++CONFIG_IPV6=y ++CONFIG_NETFILTER=y ++CONFIG_VLAN_8021Q=y ++CONFIG_WIRELESS=y ++CONFIG_WIRELESS_EXT=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_WEXT_SPY=y ++CONFIG_WEXT_PRIV=y ++CONFIG_CFG80211=y ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_NET_VENDOR_FARADAY ++# CONFIG_NET_VENDOR_INTEL ++# CONFIG_NET_VENDOR_I825XX ++# CONFIG_NET_VENDOR_MARVELL ++# CONFIG_NET_VENDOR_MICROCHIP ++# CONFIG_NET_VENDOR_MICROCHIP=y ++# CONFIG_ENC28J60 is not set ++# CONFIG_NET_VENDOR_NATSEMI=y ++# CONFIG_NET_VENDOR_8390=y ++# CONFIG_AX88796 is not set ++# CONFIG_ETHOC is not set ++# CONFIG_SH_ETH is not set ++# CONFIG_NET_VENDOR_SEEQ=y ++# CONFIG_NET_VENDOR_SMSC=y ++# CONFIG_SMC91X is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SMSC911X is not set ++# CONFIG_NET_VENDOR_STMICRO=y ++# CONFIG_STMMAC_ETH is not set ++# CONFIG_NET_VENDOR_VIA=y ++# CONFIG_VIA_VELOCITY is not set ++# CONFIG_NET_VENDOR_WIZNET=y ++CONFIG_NET_VENDOR_FREESCALE=y ++CONFIG_FEC=y ++CONFIG_PHYLIB=y ++CONFIG_AT803X_PHY=y ++CONFIG_WLAN=y ++CONFIG_BRCMUTIL=m ++CONFIG_BRCMFMAC=m ++CONFIG_BRCMFMAC_SDIO=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++# CONFIG_STANDALONE is not set ++CONFIG_DMA_CMA=y ++CONFIG_CMA=y ++CONFIG_CMA_SIZE_MBYTES=256 ++CONFIG_CONNECTOR=y ++# CONFIG_MTD is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_SCSI_MULTI_LUN=y ++CONFIG_SCSI_CONSTANTS=y ++CONFIG_SCSI_LOGGING=y ++CONFIG_SCSI_SCAN_ASYNC=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_ATA=y ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_AHCI_IMX=y ++CONFIG_NETDEVICES=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_IMX=y ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++# CONFIG_KEYBOARD_ATKBD is not set ++# CONFIG_MOUSE_PS2 is not set ++CONFIG_INPUT_MISC=y ++CONFIG_SERIO_SERPORT=m ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_IMX=y ++CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_FSL_LPUART=y ++CONFIG_SERIAL_FSL_LPUART_CONSOLE=y ++CONFIG_FSL_OTP=y ++CONFIG_GPIO_MXC=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_ALGOPCF=m ++CONFIG_I2C_ALGOPCA=m ++CONFIG_I2C_IMX=y ++CONFIG_SPI=y ++CONFIG_SPI_IMX=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_THERMAL=y ++CONFIG_CPU_THERMAL=y ++CONFIG_IMX_THERMAL=y ++CONFIG_DEVICE_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_IMX2_WDT=y ++CONFIG_MFD_DA9052_I2C=y ++CONFIG_MFD_MC13XXX_SPI=y ++CONFIG_MFD_MC13XXX_I2C=y ++CONFIG_MFD_SI476X_CORE=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_ANATOP=y ++CONFIG_REGULATOR_PFUZE100=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++CONFIG_VIDEO_V4L2_INT_DEVICE=y ++# CONFIG_MEDIA_USB_SUPPORT isnot set ++# CONFIG_USB_VIDEO_CLASS is not set ++# CONFIG_RADIO_ADAPTERS is not set ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_MXC_OUTPUT=y ++CONFIG_VIDEO_MXC_CAPTURE=m ++CONFIG_VIDEO_MXC_CSI_CAMERA=m ++CONFIG_MXC_CAMERA_OV5640=m ++CONFIG_MXC_CAMERA_OV5642=m ++CONFIG_MXC_CAMERA_OV5640_MIPI=m ++CONFIG_MXC_TVIN_ADV7180=m ++CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m ++CONFIG_VIDEO_MXC_IPU_OUTPUT=y ++CONFIG_VIDEO_MXC_PXP_V4L2=y ++CONFIG_SOC_CAMERA=y ++CONFIG_SOC_CAMERA_OV2640=y ++CONFIG_DRM=y ++CONFIG_DRM_VIVANTE=y ++CONFIG_FB=y ++# CONFIG_FB_MX3 is not set ++CONFIG_FB_MXC_SYNC_PANEL=y ++CONFIG_FB_MXC_LDB=y ++CONFIG_FB_MXC_HDMI=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_LOGO=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_SOC=y ++CONFIG_SND_IMX_SOC=y ++CONFIG_SND_SOC_IMX_SGTL5000=y ++CONFIG_SND_SOC_IMX_SPDIF=y ++CONFIG_SND_SOC_IMX_HDMI=y ++CONFIG_USB=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_CHIPIDEA=y ++CONFIG_USB_CHIPIDEA_UDC=y ++CONFIG_USB_CHIPIDEA_HOST=y ++CONFIG_USB_PHY=y ++CONFIG_NOP_USB_XCEIV=y ++CONFIG_USB_MXS_PHY=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_ZERO=m ++CONFIG_USB_ETH=m ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_ESDHC_IMX=y ++CONFIG_MXC_IPU=y ++CONFIG_MXC_GPU_VIV=y ++CONFIG_MXC_ASRC=y ++CONFIG_MXC_HDMI_CEC=y ++CONFIG_MXC_MIPI_CSI2=y ++CONFIG_MXC_MLB150=m ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGERS=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_DRV_MXC=y ++CONFIG_RTC_DRV_SNVS=y ++CONFIG_RTC_DRV_PCF8523=y ++CONFIG_DMADEVICES=y ++CONFIG_MXC_PXP_V2=y ++CONFIG_IMX_SDMA=y ++CONFIG_MXS_DMA=y ++CONFIG_SRAM=y ++CONFIG_STAGING=y ++CONFIG_COMMON_CLK_DEBUG=y ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_PWM=y ++CONFIG_PWM_SYSFS=y ++CONFIG_PWM_IMX=y ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++CONFIG_RESET_GPIO=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++CONFIG_EXT4_FS_XATTR=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_QUOTA=y ++CONFIG_QUOTA_NETLINK_INTERFACE=y ++# CONFIG_PRINT_QUOTA_WARNING is not set ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=m ++CONFIG_VFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_UBIFS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_NLS_DEFAULT="cp437" ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_UTF8=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_FTRACE is not set ++CONFIG_SECURITYFS=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_TEST=m ++CONFIG_CRYPTO_CCM=y ++CONFIG_CRYPTO_GCM=y ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=y ++CONFIG_CRYPTO_ECB=y ++CONFIG_CRYPTO_LRW=y ++CONFIG_CRYPTO_XTS=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_MICHAEL_MIC=y ++CONFIG_CRYPTO_RMD128=y ++CONFIG_CRYPTO_RMD160=y ++CONFIG_CRYPTO_RMD256=y ++CONFIG_CRYPTO_RMD320=y ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=y ++CONFIG_CRYPTO_SHA512=y ++CONFIG_CRYPTO_TGR192=y ++CONFIG_CRYPTO_WP512=y ++CONFIG_CRYPTO_BLOWFISH=y ++CONFIG_CRYPTO_CAMELLIA=y ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_TWOFISH=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++CONFIG_CRYPTO_DEV_FSL_CAAM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y ++CONFIG_CRYPTO_AES_ARM_BS=y ++CONFIG_CRC_CCITT=m ++CONFIG_CRC_T10DIF=y ++CONFIG_CRC7=m ++CONFIG_LIBCRC32C=m ++# CONFIG_MXC_MMA8451 is not set ++CONFIG_RC_CORE=m ++CONFIG_RC_DECODERS=y ++CONFIG_LIRC=m ++CONFIG_RC_LOOPBACK=m ++CONFIG_RC_MAP=m ++CONFIG_RC_DEVICES=y ++CONFIG_RC_ATI_REMOTE=m ++CONFIG_IR_NEC_DECODER=m ++CONFIG_IR_RC5_DECODER=m ++CONFIG_IR_RC6_DECODER=m ++CONFIG_IR_JVC_DECODER=m ++CONFIG_IR_SONY_DECODER=m ++CONFIG_IR_RC5_SZ_DECODER=m ++CONFIG_IR_SANYO_DECODER=m ++CONFIG_IR_MCE_KBD_DECODER=m ++CONFIG_IR_LIRC_CODEC=m ++CONFIG_IR_IMON=m ++CONFIG_IR_MCEUSB=m ++CONFIG_IR_ITE_CIR=m ++CONFIG_IR_NUVOTON=m ++CONFIG_IR_FINTEK=m ++CONFIG_IR_REDRAT3=m ++CONFIG_IR_ENE=m ++CONFIG_IR_STREAMZAP=m ++CONFIG_IR_WINBOND_CIR=m ++CONFIG_IR_IGUANA=m ++CONFIG_IR_TTUSBIR=m ++CONFIG_IR_GPIO_CIR=m +diff -Nur linux-3.14.36/arch/arm/configs/imx_v7_cbi_hb_defconfig linux-openelec/arch/arm/configs/imx_v7_cbi_hb_defconfig +--- linux-3.14.36/arch/arm/configs/imx_v7_cbi_hb_defconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/configs/imx_v7_cbi_hb_defconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,5138 @@ ++# ++# Automatically generated make config: don't edit ++# ++CONFIG_MMU=y ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set ++# CONFIG_DEBUG_HOTPLUG_CPU0 is not set ++CONFIG_LOCALVERSION="" ++CONFIG_CROSS_COMPILE="" ++CONFIG_DEFAULT_HOSTNAME="(none)" ++ ++# ++# Code maturity level options ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_HOTPLUG=y ++CONFIG_UEVENT_HELPER_PATH="" ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++ ++CONFIG_BUILD_DOCSRC=y ++ ++# ++# General setup ++# ++CONFIG_KERNEL_LZO=y ++# CONFIG_KERNEL_BZIP2 is not set ++# CONFIG_KERNEL_LZMA is not set ++CONFIG_SWAP=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++# CONFIG_COMPILE_TEST is not set ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_SYSCTL=y ++# CONFIG_IKCONFIG is not set ++# CONFIG_EMBEDDED is not set ++CONFIG_KALLSYMS=y ++CONFIG_KALLSYMS_ALL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_CFQ_GROUP_IOSCHED=y ++CONFIG_IOSCHED_BFQ=y ++CONFIG_CGROUP_BFQIO=y ++CONFIG_DEFAULT_BFQ=y ++CONFIG_DEFAULT_IOSCHED="bfq" ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_PID_NS=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++CONFIG_NET_NS=y ++CONFIG_USER_NS=y ++# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set ++CONFIG_SYSVIPC=y ++CONFIG_FHANDLE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_LOG_BUF_SHIFT=18 ++CONFIG_CGROUPS=y ++CONFIG_RELAY=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EXPERT=y ++CONFIG_PERF_EVENTS=y ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++ ++CONFIG_POSIX_MQUEUE=y ++CONFIG_PREEMPT_VOLUNTARY=y ++ ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_SLUB_STATS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++ ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_ATMEL_PWM is not set ++# CONFIG_IWMC3200TOP is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# MX6 specific kernel configuration ++CONFIG_GPIO_PCA953X=y ++CONFIG_ARCH_MXC=y ++CONFIG_MXC_DEBUG_BOARD=y ++CONFIG_SOC_IMX6Q=y ++CONFIG_SOC_IMX6SL=y ++# CONFIG_SWP_EMULATE is not set ++CONFIG_PCI=y ++CONFIG_PCIE_DW=y ++CONFIG_PCI_IMX6=y ++CONFIG_SMP=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++CONFIG_HIGHMEM=y ++CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_ARM_IMX6_CPUFREQ=y ++CONFIG_CPU_IDLE=y ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++CONFIG_BINFMT_MISC=m ++CONFIG_PM_RUNTIME=y ++CONFIG_PM_DEBUG=y ++CONFIG_PM_TEST_SUSPEND=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++CONFIG_IPV6=y ++CONFIG_NETFILTER=y ++CONFIG_VLAN_8021Q=y ++CONFIG_WIRELESS=y ++CONFIG_WIRELESS_EXT=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_WEXT_SPY=y ++CONFIG_WEXT_PRIV=y ++CONFIG_CFG80211=y ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_NET_VENDOR_FARADAY ++# CONFIG_NET_VENDOR_INTEL ++# CONFIG_NET_VENDOR_I825XX ++# CONFIG_NET_VENDOR_MARVELL ++# CONFIG_NET_VENDOR_MICROCHIP ++# CONFIG_NET_VENDOR_MICROCHIP=y ++# CONFIG_ENC28J60 is not set ++# CONFIG_NET_VENDOR_NATSEMI=y ++# CONFIG_NET_VENDOR_8390=y ++# CONFIG_AX88796 is not set ++# CONFIG_ETHOC is not set ++# CONFIG_SH_ETH is not set ++# CONFIG_NET_VENDOR_SEEQ=y ++# CONFIG_NET_VENDOR_SMSC=y ++# CONFIG_SMC91X is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SMSC911X is not set ++# CONFIG_NET_VENDOR_STMICRO=y ++# CONFIG_STMMAC_ETH is not set ++# CONFIG_NET_VENDOR_VIA=y ++# CONFIG_VIA_VELOCITY is not set ++# CONFIG_NET_VENDOR_WIZNET=y ++CONFIG_NET_VENDOR_FREESCALE=y ++CONFIG_FEC=y ++CONFIG_PHYLIB=y ++CONFIG_AT803X_PHY=y ++CONFIG_WLAN=y ++CONFIG_BRCMUTIL=m ++CONFIG_BRCMFMAC=m ++CONFIG_BRCMFMAC_SDIO=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++# CONFIG_STANDALONE is not set ++CONFIG_DMA_CMA=y ++CONFIG_CMA=y ++CONFIG_CMA_SIZE_MBYTES=256 ++CONFIG_CONNECTOR=y ++# CONFIG_MTD is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_SCSI_MULTI_LUN=y ++CONFIG_SCSI_CONSTANTS=y ++CONFIG_SCSI_LOGGING=y ++CONFIG_SCSI_SCAN_ASYNC=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_ATA=y ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_AHCI_IMX=y ++CONFIG_NETDEVICES=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_IMX=y ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++# CONFIG_KEYBOARD_ATKBD is not set ++# CONFIG_MOUSE_PS2 is not set ++CONFIG_INPUT_MISC=y ++CONFIG_SERIO_SERPORT=m ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_IMX=y ++CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_FSL_LPUART=y ++CONFIG_SERIAL_FSL_LPUART_CONSOLE=y ++CONFIG_FSL_OTP=y ++CONFIG_GPIO_MXC=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_ALGOPCF=m ++CONFIG_I2C_ALGOPCA=m ++CONFIG_I2C_IMX=y ++CONFIG_SPI=y ++CONFIG_SPI_IMX=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_THERMAL=y ++CONFIG_CPU_THERMAL=y ++CONFIG_IMX_THERMAL=y ++CONFIG_DEVICE_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_IMX2_WDT=y ++CONFIG_MFD_DA9052_I2C=y ++CONFIG_MFD_MC13XXX_SPI=y ++CONFIG_MFD_MC13XXX_I2C=y ++CONFIG_MFD_SI476X_CORE=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_ANATOP=y ++CONFIG_REGULATOR_PFUZE100=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++CONFIG_VIDEO_V4L2_INT_DEVICE=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=m ++# CONFIG_RADIO_ADAPTERS is not set ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_MXC_OUTPUT=y ++CONFIG_VIDEO_MXC_CAPTURE=m ++CONFIG_VIDEO_MXC_CSI_CAMERA=m ++CONFIG_MXC_CAMERA_OV5640=m ++CONFIG_MXC_CAMERA_OV5642=m ++CONFIG_MXC_CAMERA_OV5640_MIPI=m ++CONFIG_MXC_TVIN_ADV7180=m ++CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m ++CONFIG_VIDEO_MXC_IPU_OUTPUT=y ++CONFIG_VIDEO_MXC_PXP_V4L2=y ++CONFIG_SOC_CAMERA=y ++CONFIG_SOC_CAMERA_OV2640=y ++CONFIG_DRM=y ++CONFIG_DRM_VIVANTE=y ++CONFIG_FB=y ++# CONFIG_FB_MX3 is not set ++CONFIG_FB_MXC_SYNC_PANEL=y ++CONFIG_FB_MXC_LDB=y ++CONFIG_FB_MXC_HDMI=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_LOGO=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_SOC=y ++CONFIG_SND_IMX_SOC=y ++CONFIG_SND_SOC_IMX_SGTL5000=y ++CONFIG_SND_SOC_IMX_SPDIF=y ++CONFIG_SND_SOC_IMX_HDMI=y ++CONFIG_USB=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_CHIPIDEA=y ++CONFIG_USB_CHIPIDEA_UDC=y ++CONFIG_USB_CHIPIDEA_HOST=y ++CONFIG_USB_PHY=y ++CONFIG_NOP_USB_XCEIV=y ++CONFIG_USB_MXS_PHY=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_ZERO=m ++CONFIG_USB_ETH=m ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_ESDHC_IMX=y ++CONFIG_MXC_IPU=y ++CONFIG_MXC_GPU_VIV=y ++CONFIG_MXC_ASRC=y ++CONFIG_MXC_HDMI_CEC=y ++CONFIG_MXC_MIPI_CSI2=y ++CONFIG_MXC_MLB150=m ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGERS=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_DRV_MXC=y ++CONFIG_RTC_DRV_SNVS=y ++CONFIG_RTC_DRV_PCF8523=y ++CONFIG_DMADEVICES=y ++CONFIG_MXC_PXP_V2=y ++CONFIG_IMX_SDMA=y ++CONFIG_MXS_DMA=y ++CONFIG_SRAM=y ++CONFIG_STAGING=y ++CONFIG_COMMON_CLK_DEBUG=y ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_PWM=y ++CONFIG_PWM_SYSFS=y ++CONFIG_PWM_IMX=y ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++CONFIG_RESET_GPIO=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++CONFIG_EXT4_FS_XATTR=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_QUOTA=y ++CONFIG_QUOTA_NETLINK_INTERFACE=y ++# CONFIG_PRINT_QUOTA_WARNING is not set ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=m ++CONFIG_VFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_UBIFS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_NLS_DEFAULT="cp437" ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_UTF8=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_FTRACE is not set ++CONFIG_SECURITYFS=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_TEST=m ++CONFIG_CRYPTO_CCM=y ++CONFIG_CRYPTO_GCM=y ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=y ++CONFIG_CRYPTO_ECB=y ++CONFIG_CRYPTO_LRW=y ++CONFIG_CRYPTO_XTS=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_MICHAEL_MIC=y ++CONFIG_CRYPTO_RMD128=y ++CONFIG_CRYPTO_RMD160=y ++CONFIG_CRYPTO_RMD256=y ++CONFIG_CRYPTO_RMD320=y ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=y ++CONFIG_CRYPTO_SHA512=y ++CONFIG_CRYPTO_TGR192=y ++CONFIG_CRYPTO_WP512=y ++CONFIG_CRYPTO_BLOWFISH=y ++CONFIG_CRYPTO_CAMELLIA=y ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_TWOFISH=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++CONFIG_CRYPTO_DEV_FSL_CAAM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y ++CONFIG_CRYPTO_AES_ARM_BS=y ++CONFIG_CRC_CCITT=m ++CONFIG_CRC_T10DIF=y ++CONFIG_CRC7=m ++CONFIG_LIBCRC32C=m ++# CONFIG_MXC_MMA8451 is not set ++ ++# ++# Loadable module support ++# ++# CONFIG_MODULE_FORCE_LOAD is not set ++# -- MODULE_FORCE_UNLOAD is controlled by config-debug/nodebug ++ ++# CONFIG_PCI_DEBUG is not set ++CONFIG_PCI_STUB=y ++CONFIG_PCI_IOV=y ++CONFIG_PCI_PRI=y ++CONFIG_PCI_PASID=y ++CONFIG_HT_IRQ=y ++CONFIG_PCI_MSI=y ++# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set ++CONFIG_PCIEPORTBUS=y ++CONFIG_PCIEAER=y ++CONFIG_PCIEASPM=y ++# CONFIG_PCIEASPM_DEBUG is not set ++CONFIG_PCIE_ECRC=y ++CONFIG_PCIEAER_INJECT=m ++CONFIG_HOTPLUG_PCI_PCIE=y ++CONFIG_HOTPLUG_PCI_FAKE=m ++ ++# CONFIG_SGI_IOC4 is not set ++ ++# CONFIG_ISA is not set ++# CONFIG_SCx200 is not set ++ ++# ++# PCMCIA/CardBus support ++# FIXME: Deprecate Cardbus ? ++# ++CONFIG_PCMCIA=y ++CONFIG_PCMCIA_LOAD_CIS=y ++# CONFIG_PCMCIA_DEBUG is not set ++CONFIG_YENTA=m ++CONFIG_CARDBUS=y ++CONFIG_I82092=m ++CONFIG_PD6729=m ++ ++CONFIG_PCCARD=y ++CONFIG_SDIO_UART=m ++# CONFIG_MMC_TEST is not set ++# CONFIG_MMC_DEBUG is not set ++# https://lists.fedoraproject.org/pipermail/kernel/2014-February/004889.html ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++CONFIG_MMC_SDHCI_PCI=m ++CONFIG_MMC_SDHCI_ACPI=m ++CONFIG_MMC_SDRICOH_CS=m ++CONFIG_MMC_TIFM_SD=m ++CONFIG_MMC_WBSD=m ++CONFIG_MMC_VIA_SDMMC=m ++CONFIG_MMC_CB710=m ++CONFIG_MMC_RICOH_MMC=y ++CONFIG_MMC_USHC=m ++CONFIG_MMC_REALTEK_PCI=m ++CONFIG_MMC_VUB300=m ++# CONFIG_MMC_SDHCI_PXAV2 is not set ++# CONFIG_MMC_SDHCI_PXAV3 is not set ++# CONFIG_MMC_SDHCI_OF_ARASAN is not set ++ ++ ++CONFIG_CB710_CORE=m ++# CONFIG_CB710_DEBUG is not set ++ ++CONFIG_INFINIBAND=m ++CONFIG_INFINIBAND_MTHCA=m ++# CONFIG_INFINIBAND_MTHCA_DEBUG is not set ++CONFIG_INFINIBAND_IPOIB=m ++CONFIG_INFINIBAND_IPOIB_DEBUG=y ++CONFIG_INFINIBAND_IPOIB_DEBUG_DATA=y ++CONFIG_INFINIBAND_IPOIB_CM=y ++CONFIG_INFINIBAND_SRP=m ++CONFIG_INFINIBAND_SRPT=m ++CONFIG_INFINIBAND_USER_MAD=m ++CONFIG_INFINIBAND_USER_ACCESS=m ++# CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING is not set #staging ++CONFIG_INFINIBAND_IPATH=m ++CONFIG_INFINIBAND_ISER=m ++CONFIG_INFINIBAND_ISERT=m ++CONFIG_INFINIBAND_AMSO1100=m ++# CONFIG_INFINIBAND_AMSO1100_DEBUG is not set ++CONFIG_INFINIBAND_CXGB3=m ++CONFIG_INFINIBAND_CXGB4=m ++CONFIG_SCSI_CXGB3_ISCSI=m ++CONFIG_SCSI_CXGB4_ISCSI=m ++# CONFIG_INFINIBAND_CXGB3_DEBUG is not set ++CONFIG_MLX4_INFINIBAND=m ++CONFIG_MLX5_INFINIBAND=m ++CONFIG_INFINIBAND_NES=m ++# CONFIG_INFINIBAND_NES_DEBUG is not set ++CONFIG_INFINIBAND_QIB=m ++CONFIG_INFINIBAND_QIB_DCA=y ++# CONFIG_INFINIBAND_OCRDMA is not set ++# CONFIG_INFINIBAND_USNIC is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++# CONFIG_BINFMT_AOUT is not set ++CONFIG_BINFMT_SCRIPT=y ++ ++# ++# Device Drivers ++# ++ ++# CONFIG_COMMON_CLK_SI5351 is not set ++ ++# ++# Generic Driver Options ++# ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++ ++# Give this a try in rawhide for now ++# CONFIG_FW_LOADER_USER_HELPER is not set ++ ++ ++ ++# ++# Memory Technology Devices (MTD) ++# ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_AR7_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++# CONFIG_MTD_CHAR is not set ++# CONFIG_MTD_BLKDEVS is not set ++# CONFIG_MTD_BLOCK is not set ++# CONFIG_MTD_BLOCK_RO is not set ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_TS5500 is not set ++# CONFIG_MTD_INTEL_VR_NOR is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# Self-contained MTD device drivers ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_LPDDR is not set ++CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++ ++# ++# Parallel port support ++# ++CONFIG_PARPORT=m ++CONFIG_PARPORT_PC=m ++CONFIG_PARPORT_SERIAL=m ++# CONFIG_PARPORT_PC_FIFO is not set ++# CONFIG_PARPORT_PC_SUPERIO is not set ++CONFIG_PARPORT_PC_PCMCIA=m ++CONFIG_PARPORT_1284=y ++# CONFIG_PARPORT_AX88796 is not set ++ ++CONFIG_ACPI_PCI_SLOT=y ++CONFIG_HOTPLUG_PCI_ACPI=y ++CONFIG_HOTPLUG_PCI_ACPI_IBM=m ++ ++# ++# Block devices ++# ++CONFIG_BLK_DEV=y ++CONFIG_BLK_DEV_NULL_BLK=m ++CONFIG_BLK_DEV_FD=m ++# CONFIG_PARIDE is not set ++CONFIG_ZRAM=m ++# CONFIG_ZRAM_DEBUG is not set ++CONFIG_ENHANCEIO=m ++ ++CONFIG_BLK_CPQ_DA=m ++CONFIG_BLK_CPQ_CISS_DA=m ++CONFIG_CISS_SCSI_TAPE=y ++CONFIG_BLK_DEV_DAC960=m ++# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set ++CONFIG_BLK_DEV_DRBD=m ++CONFIG_BLK_DEV_UMEM=m ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=0 ++# Fedora 18 util-linux is the last release that supports cryptoloop devices ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++CONFIG_BLK_DEV_NBD=m ++CONFIG_BLK_DEV_NVME=m ++CONFIG_BLK_DEV_SKD=m # 64-bit only but easier to put here ++CONFIG_BLK_DEV_OSD=m ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_IO_TRACE=y ++ ++CONFIG_BLK_DEV_BSGLIB=y ++CONFIG_BLK_DEV_INTEGRITY=y ++CONFIG_BLK_DEV_THROTTLING=y ++# CONFIG_BLK_CMDLINE_PARSER is not set ++ ++ ++# ++# ATA/ATAPI/MFM/RLL support ++# ++# CONFIG_IDE is not set ++ ++# CONFIG_BLK_DEV_HD is not set ++# CONFIG_BLK_DEV_RSXX is not set ++ ++CONFIG_SCSI_VIRTIO=m ++CONFIG_VIRTIO_BLK=m ++CONFIG_VIRTIO_PCI=m ++CONFIG_VIRTIO_BALLOON=m ++CONFIG_VIRTIO_MMIO=m ++# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set ++CONFIG_VIRTIO_NET=m ++CONFIG_HW_RANDOM_VIRTIO=m ++CONFIG_VIRTIO_CONSOLE=m ++CONFIG_VHOST_NET=m ++CONFIG_TCM_VHOST=m ++CONFIG_VHOST_SCSI=m ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI=y ++ ++CONFIG_SCSI_ENCLOSURE=m ++CONFIG_SCSI_SRP=m ++CONFIG_SCSI_SRP_ATTRS=m ++CONFIG_SCSI_TGT=m ++CONFIG_SCSI_ISCI=m ++CONFIG_SCSI_CHELSIO_FCOE=m ++ ++CONFIG_SCSI_DH=y ++CONFIG_SCSI_DH_RDAC=m ++CONFIG_SCSI_DH_HP_SW=m ++CONFIG_SCSI_DH_EMC=m ++CONFIG_SCSI_DH_ALUA=m ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_CHR_DEV_ST=m ++CONFIG_CHR_DEV_OSST=m ++CONFIG_BLK_DEV_SR=y ++CONFIG_BLK_DEV_SR_VENDOR=y ++CONFIG_CHR_DEV_SG=y ++CONFIG_CHR_DEV_SCH=m ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++CONFIG_SCSI_SPI_ATTRS=m ++CONFIG_SCSI_FC_ATTRS=m ++CONFIG_SCSI_FC_TGT_ATTRS=y ++CONFIG_SCSI_ISCSI_ATTRS=m ++CONFIG_SCSI_SAS_ATTRS=m ++CONFIG_SCSI_SRP_TGT_ATTRS=y ++CONFIG_SCSI_SAS_LIBSAS=m ++CONFIG_SCSI_SAS_ATA=y ++CONFIG_SCSI_SAS_HOST_SMP=y ++CONFIG_RAID_ATTRS=m ++ ++CONFIG_ISCSI_TCP=m ++CONFIG_ISCSI_BOOT_SYSFS=m ++ ++# ++# SCSI low-level drivers ++# ++CONFIG_BLK_DEV_3W_XXXX_RAID=m ++CONFIG_SCSI_3W_9XXX=m ++CONFIG_SCSI_ACARD=m ++CONFIG_SCSI_AACRAID=m ++CONFIG_SCSI_AIC7XXX=m ++# http://lists.fedoraproject.org/pipermail/kernel/2013-February/004102.html ++# CONFIG_SCSI_AIC7XXX_OLD is not set ++CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 ++CONFIG_AIC7XXX_RESET_DELAY_MS=15000 ++# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set ++# CONFIG_AIC7XXX_DEBUG_ENABLE is not set ++CONFIG_AIC7XXX_DEBUG_MASK=0 ++# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set ++CONFIG_SCSI_AIC79XX=m ++CONFIG_AIC79XX_CMDS_PER_DEVICE=4 ++CONFIG_AIC79XX_RESET_DELAY_MS=15000 ++# CONFIG_AIC79XX_BUILD_FIRMWARE is not set ++# CONFIG_AIC79XX_DEBUG_ENABLE is not set ++CONFIG_AIC79XX_DEBUG_MASK=0 ++# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set ++CONFIG_SCSI_AIC94XX=m ++# CONFIG_AIC94XX_DEBUG is not set ++# CONFIG_SCSI_ADVANSYS is not set ++CONFIG_SCSI_BFA_FC=m ++CONFIG_MEGARAID_NEWGEN=y ++CONFIG_MEGARAID_MM=m ++CONFIG_MEGARAID_MAILBOX=m ++CONFIG_MEGARAID_LEGACY=m ++CONFIG_MEGARAID_SAS=m ++CONFIG_SCSI_ESAS2R=m ++CONFIG_SCSI_MVSAS=m ++# CONFIG_SCSI_MVSAS_DEBUG is not set ++CONFIG_SCSI_MVSAS_TASKLET=y ++CONFIG_SCSI_MPT2SAS=m ++CONFIG_SCSI_MPT2SAS_MAX_SGE=128 ++CONFIG_SCSI_MPT2SAS_LOGGING=y ++CONFIG_SCSI_MPT3SAS=m ++CONFIG_SCSI_MPT3SAS_MAX_SGE=128 ++CONFIG_SCSI_MPT3SAS_LOGGING=y ++ ++CONFIG_SCSI_UFSHCD=m ++CONFIG_SCSI_UFSHCD_PCI=m ++# CONFIG_SCSI_UFSHCD_PLATFORM is not set ++ ++CONFIG_SCSI_MVUMI=m ++ ++CONFIG_SCSI_OSD_INITIATOR=m ++CONFIG_SCSI_OSD_ULD=m ++CONFIG_SCSI_OSD_DPRINT_SENSE=1 ++# CONFIG_SCSI_OSD_DEBUG is not set ++ ++CONFIG_SCSI_BNX2_ISCSI=m ++CONFIG_SCSI_BNX2X_FCOE=m ++CONFIG_BE2ISCSI=m ++CONFIG_SCSI_PMCRAID=m ++ ++CONFIG_SCSI_HPSA=m ++CONFIG_SCSI_3W_SAS=m ++CONFIG_SCSI_PM8001=m ++CONFIG_VMWARE_PVSCSI=m ++CONFIG_VMWARE_BALLOON=m ++ ++CONFIG_SCSI_ARCMSR=m ++CONFIG_SCSI_BUSLOGIC=m ++CONFIG_SCSI_INITIO=m ++CONFIG_SCSI_FLASHPOINT=y ++CONFIG_SCSI_DMX3191D=m ++# CONFIG_SCSI_EATA is not set ++# CONFIG_SCSI_EATA_PIO is not set ++# CONFIG_SCSI_FUTURE_DOMAIN is not set ++CONFIG_SCSI_GDTH=m ++CONFIG_SCSI_HPTIOP=m ++CONFIG_SCSI_IPS=m ++CONFIG_SCSI_INIA100=m ++# CONFIG_SCSI_PPA is not set ++# CONFIG_SCSI_IMM is not set ++# CONFIG_SCSI_IZIP_EPP16 is not set ++# CONFIG_SCSI_IZIP_SLOW_CTR is not set ++CONFIG_SCSI_STEX=m ++CONFIG_SCSI_SYM53C8XX_2=m ++CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 ++CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 ++CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 ++CONFIG_SCSI_SYM53C8XX_MMIO=y ++CONFIG_SCSI_QLOGIC_1280=m ++CONFIG_SCSI_DC395x=m ++# CONFIG_SCSI_NSP32 is not set ++CONFIG_SCSI_DEBUG=m ++CONFIG_SCSI_DC390T=m ++CONFIG_SCSI_QLA_FC=m ++CONFIG_TCM_QLA2XXX=m ++CONFIG_SCSI_QLA_ISCSI=m ++CONFIG_SCSI_IPR=m ++CONFIG_SCSI_IPR_TRACE=y ++CONFIG_SCSI_IPR_DUMP=y ++# CONFIG_SCSI_DPT_I2O is not set ++CONFIG_SCSI_LPFC=m ++# CONFIG_SCSI_LPFC_DEBUG_FS is not set ++ ++# PCMCIA SCSI adapter support ++# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set ++ ++CONFIG_ATA_BMDMA=y ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_ATA_SFF=y ++CONFIG_ATA_PIIX=y ++# CONFIG_SATA_HIGHBANK is not set ++CONFIG_ATA_ACPI=y ++CONFIG_BLK_DEV_SX8=m ++CONFIG_PDC_ADMA=m ++CONFIG_SATA_AHCI=y ++CONFIG_SATA_INIC162X=m ++CONFIG_SATA_MV=m ++CONFIG_SATA_NV=m ++CONFIG_SATA_PMP=y ++CONFIG_SATA_PROMISE=m ++CONFIG_SATA_QSTOR=m ++CONFIG_SATA_RCAR=m ++CONFIG_SATA_SIL=m ++CONFIG_SATA_SIL24=m ++CONFIG_SATA_SIS=m ++CONFIG_SATA_SVW=m ++CONFIG_SATA_SX4=m ++CONFIG_SATA_ULI=m ++CONFIG_SATA_VIA=m ++CONFIG_SATA_VITESSE=m ++# CONFIG_SATA_ZPODD is not set ++CONFIG_SATA_ACARD_AHCI=m ++ ++# CONFIG_PATA_LEGACY is not set ++CONFIG_PATA_ACPI=m ++CONFIG_PATA_ALI=m ++CONFIG_PATA_AMD=m ++CONFIG_PATA_ARASAN_CF=m ++CONFIG_PATA_ARTOP=m ++CONFIG_PATA_ATIIXP=m ++CONFIG_PATA_CMD640_PCI=m ++CONFIG_PATA_CMD64X=m ++CONFIG_PATA_CS5520=m ++CONFIG_PATA_CS5530=m ++CONFIG_PATA_CS5535=m ++CONFIG_PATA_CS5536=m ++CONFIG_PATA_CYPRESS=m ++CONFIG_PATA_EFAR=m ++CONFIG_ATA_GENERIC=m ++CONFIG_PATA_HPT366=m ++CONFIG_PATA_HPT37X=m ++CONFIG_PATA_HPT3X2N=m ++CONFIG_PATA_HPT3X3=m ++# CONFIG_PATA_HPT3X3_DMA is not set ++CONFIG_PATA_IT821X=m ++CONFIG_PATA_IT8213=m ++CONFIG_PATA_JMICRON=m ++CONFIG_PATA_NINJA32=m ++CONFIG_PATA_MARVELL=m ++CONFIG_PATA_MPIIX=m ++CONFIG_PATA_NETCELL=m ++CONFIG_PATA_NS87410=m ++CONFIG_PATA_NS87415=m ++CONFIG_PATA_OLDPIIX=m ++CONFIG_PATA_OPTI=m ++CONFIG_PATA_OPTIDMA=m ++CONFIG_PATA_PCMCIA=m ++CONFIG_PATA_PDC_OLD=m ++# CONFIG_PATA_RADISYS is not set ++CONFIG_PATA_RDC=m ++# CONFIG_PATA_RZ1000 is not set ++# CONFIG_PATA_SC1200 is not set ++CONFIG_PATA_SERVERWORKS=m ++CONFIG_PATA_PDC2027X=m ++CONFIG_PATA_SCH=m ++CONFIG_PATA_SIL680=m ++CONFIG_PATA_SIS=m ++CONFIG_PATA_TOSHIBA=m ++CONFIG_PATA_TRIFLEX=m ++CONFIG_PATA_VIA=m ++CONFIG_PATA_WINBOND=m ++CONFIG_PATA_ATP867X=m ++ ++ ++# ++# Multi-device support (RAID and LVM) ++# ++CONFIG_MD=y ++CONFIG_BLK_DEV_MD=y ++CONFIG_MD_AUTODETECT=y ++CONFIG_MD_FAULTY=m ++CONFIG_MD_LINEAR=m ++CONFIG_MD_MULTIPATH=m ++CONFIG_MD_RAID0=m ++CONFIG_MD_RAID1=m ++CONFIG_MD_RAID10=m ++CONFIG_MD_RAID456=m ++ ++CONFIG_BCACHE=m ++# CONFIG_BCACHE_DEBUG is not set ++# CONFIG_BCACHE_EDEBUG is not set ++# CONFIG_BCACHE_CLOSURES_DEBUG is not set ++ ++# CONFIG_MULTICORE_RAID456 is not set ++CONFIG_ASYNC_RAID6_TEST=m ++CONFIG_BLK_DEV_DM=y ++CONFIG_DM_CRYPT=m ++CONFIG_DM_DEBUG=y ++CONFIG_DM_DELAY=m ++CONFIG_DM_MIRROR=y ++CONFIG_DM_MULTIPATH=m ++CONFIG_DM_SNAPSHOT=y ++CONFIG_DM_THIN_PROVISIONING=m ++CONFIG_DM_CACHE=m ++CONFIG_DM_CACHE_MQ=m ++CONFIG_DM_CACHE_CLEANER=m ++# CONFIG_DM_DEBUG_BLOCK_STACK_TRACING is not set ++# CONFIG_DM_DEBUG_SPACE_MAPS is not set ++CONFIG_DM_UEVENT=y ++CONFIG_DM_ZERO=y ++CONFIG_DM_LOG_USERSPACE=m ++CONFIG_DM_MULTIPATH_QL=m ++CONFIG_DM_MULTIPATH_ST=m ++CONFIG_DM_RAID=m ++CONFIG_DM_FLAKEY=m ++CONFIG_DM_VERITY=m ++CONFIG_DM_SWITCH=m ++ ++# ++# Fusion MPT device support ++# ++CONFIG_FUSION=y ++CONFIG_FUSION_SPI=m ++CONFIG_FUSION_FC=m ++CONFIG_FUSION_MAX_SGE=40 ++CONFIG_FUSION_CTL=m ++CONFIG_FUSION_LAN=m ++CONFIG_FUSION_SAS=m ++CONFIG_FUSION_LOGGING=y ++ ++# ++# IEEE 1394 (FireWire) support (JUJU alternative stack) ++# ++CONFIG_FIREWIRE=m ++CONFIG_FIREWIRE_OHCI=m ++CONFIG_FIREWIRE_SBP2=m ++CONFIG_FIREWIRE_NET=m ++CONFIG_FIREWIRE_OHCI_DEBUG=y ++CONFIG_FIREWIRE_NOSY=m ++# CONFIG_FIREWIRE_SERIAL is not set ++# CONFIG_FIREWIRE_OHCI_REMOTE_DMA is not set ++ ++# ++# IEEE 1394 (FireWire) support ++# ++ ++# ++# I2O device support ++# ++# CONFIG_I2O is not set ++# CONFIG_I2O_LCT_NOTIFY_ON_CHANGES is not set ++ ++# ++# Virtualization support drivers ++# ++# CONFIG_VIRT_DRIVERS is not set ++ ++# Networking support ++# ++ ++CONFIG_NET_DMA=y ++ ++CONFIG_NETLINK_MMAP=y ++CONFIG_NETLINK_DIAG=m ++ ++CONFIG_TCP_CONG_ADVANCED=y ++CONFIG_TCP_CONG_BIC=m ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_TCP_CONG_HTCP=m ++CONFIG_TCP_CONG_HSTCP=m ++CONFIG_TCP_CONG_HYBLA=m ++CONFIG_TCP_CONG_ILLINOIS=m ++CONFIG_TCP_CONG_LP=m ++CONFIG_TCP_CONG_SCALABLE=m ++CONFIG_TCP_CONG_VEGAS=m ++CONFIG_TCP_CONG_VENO=m ++CONFIG_TCP_CONG_WESTWOOD=m ++CONFIG_TCP_CONG_YEAH=m ++ ++CONFIG_TCP_MD5SIG=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET_DIAG=m ++CONFIG_UNIX_DIAG=m ++CONFIG_NET_KEY=m ++CONFIG_NET_KEY_MIGRATE=y ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_DIAG=m ++CONFIG_INET_UDP_DIAG=m ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_FIB_TRIE_STATS=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_NF_SECURITY=m ++CONFIG_NET_IPIP=m ++CONFIG_NET_IPGRE_DEMUX=m ++CONFIG_NET_IPGRE=m ++CONFIG_NET_IPGRE_BROADCAST=y ++CONFIG_IP_MROUTE=y ++CONFIG_IP_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IP_PIMSM_V1=y ++CONFIG_IP_PIMSM_V2=y ++CONFIG_ARPD=y ++CONFIG_SYN_COOKIES=y ++CONFIG_NET_IPVTI=m ++CONFIG_INET_AH=m ++CONFIG_INET_ESP=m ++CONFIG_INET_IPCOMP=m ++CONFIG_NETCONSOLE=m ++CONFIG_NETCONSOLE_DYNAMIC=y ++CONFIG_NETPOLL_TRAP=y ++CONFIG_NET_POLL_CONTROLLER=y ++ ++# ++# IP: Virtual Server Configuration ++# ++CONFIG_IP_VS=m ++# CONFIG_IP_VS_DEBUG is not set ++CONFIG_IP_VS_TAB_BITS=12 ++CONFIG_IP_VS_PROTO_TCP=y ++CONFIG_IP_VS_PROTO_UDP=y ++CONFIG_IP_VS_PROTO_ESP=y ++CONFIG_IP_VS_PROTO_AH=y ++CONFIG_IP_VS_PROTO_SCTP=y ++CONFIG_IP_VS_IPV6=y ++CONFIG_IP_VS_RR=m ++CONFIG_IP_VS_WRR=m ++CONFIG_IP_VS_LC=m ++CONFIG_IP_VS_WLC=m ++CONFIG_IP_VS_LBLC=m ++CONFIG_IP_VS_LBLCR=m ++CONFIG_IP_VS_DH=m ++CONFIG_IP_VS_SH=m ++CONFIG_IP_VS_SED=m ++CONFIG_IP_VS_NQ=m ++ ++# ++# IPVS SH scheduler ++# ++CONFIG_IP_VS_SH_TAB_BITS=8 ++ ++CONFIG_IP_VS_FTP=m ++CONFIG_IP_VS_PE_SIP=m ++ ++CONFIG_IPV6_PRIVACY=y ++CONFIG_IPV6_ROUTER_PREF=y ++CONFIG_IPV6_ROUTE_INFO=y ++CONFIG_IPV6_OPTIMISTIC_DAD=y ++CONFIG_INET6_AH=m ++CONFIG_INET6_ESP=m ++CONFIG_INET6_IPCOMP=m ++CONFIG_IPV6_MIP6=y ++CONFIG_IPV6_VTI=m ++CONFIG_IPV6_SIT=m ++CONFIG_IPV6_SIT_6RD=y ++CONFIG_IPV6_TUNNEL=m ++# CONFIG_IPV6_GRE is not set ++CONFIG_IPV6_SUBTREES=y ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IPV6_PIMSM_V2=y ++ ++CONFIG_RDS=m ++# CONFIG_RDS_DEBUG is not set ++CONFIG_RDS_RDMA=m ++CONFIG_RDS_TCP=m ++ ++CONFIG_NET_9P=m ++CONFIG_NET_9P_VIRTIO=m ++# CONFIG_NET_9P_DEBUG is not set ++CONFIG_NET_9P_RDMA=m ++ ++# CONFIG_DECNET is not set ++CONFIG_BRIDGE=m ++CONFIG_BRIDGE_IGMP_SNOOPING=y ++CONFIG_BRIDGE_VLAN_FILTERING=y ++ ++# PHY timestamping adds overhead ++CONFIG_NETWORK_PHY_TIMESTAMPING=y ++ ++CONFIG_NETFILTER_ADVANCED=y ++CONFIG_NF_CONNTRACK=m ++CONFIG_NETFILTER_NETLINK=m ++CONFIG_NETFILTER_NETLINK_ACCT=m ++CONFIG_NETFILTER_NETLINK_QUEUE=m ++CONFIG_NETFILTER_NETLINK_QUEUE_CT=y ++CONFIG_NETFILTER_NETLINK_LOG=m ++CONFIG_NETFILTER_TPROXY=m ++CONFIG_NETFILTER_XTABLES=y ++CONFIG_NETFILTER_XT_SET=m ++CONFIG_NETFILTER_XT_MARK=m ++CONFIG_NETFILTER_XT_CONNMARK=m ++ ++CONFIG_NETFILTER_XT_TARGET_AUDIT=m ++CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m ++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m ++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m ++CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m ++CONFIG_NETFILTER_XT_TARGET_CT=m ++CONFIG_NETFILTER_XT_TARGET_DSCP=m ++CONFIG_NETFILTER_XT_TARGET_HMARK=m ++CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m ++CONFIG_NETFILTER_XT_TARGET_LED=m ++CONFIG_NETFILTER_XT_TARGET_LOG=m ++CONFIG_NETFILTER_XT_TARGET_MARK=m ++CONFIG_NETFILTER_XT_TARGET_NFLOG=m ++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m ++CONFIG_NETFILTER_XT_TARGET_NOTRACK=m ++CONFIG_NETFILTER_XT_TARGET_RATEEST=m ++CONFIG_NETFILTER_XT_TARGET_SECMARK=m ++CONFIG_NETFILTER_XT_TARGET_TCPMSS=m ++CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m ++CONFIG_NETFILTER_XT_TARGET_TRACE=m ++CONFIG_NETFILTER_XT_TARGET_TEE=m ++CONFIG_NETFILTER_XT_TARGET_TPROXY=m ++ ++CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m ++CONFIG_NETFILTER_XT_MATCH_BPF=m ++CONFIG_NETFILTER_XT_MATCH_CGROUP=m ++CONFIG_NETFILTER_XT_MATCH_CLUSTER=m ++CONFIG_NETFILTER_XT_MATCH_COMMENT=m ++CONFIG_NETFILTER_XT_MATCH_CPU=m ++CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m ++CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m ++CONFIG_NETFILTER_XT_MATCH_CONNMARK=m ++CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m ++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y ++CONFIG_NETFILTER_XT_MATCH_DCCP=m ++CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m ++CONFIG_NETFILTER_XT_MATCH_DSCP=m ++CONFIG_NETFILTER_XT_MATCH_ECN=m ++CONFIG_NETFILTER_XT_MATCH_ESP=m ++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m ++CONFIG_NETFILTER_XT_MATCH_HELPER=m ++CONFIG_NETFILTER_XT_MATCH_HL=m ++CONFIG_NETFILTER_XT_MATCH_IPCOMP=m ++CONFIG_NETFILTER_XT_MATCH_IPRANGE=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=m ++CONFIG_NETFILTER_XT_MATCH_L2TP=m ++CONFIG_NETFILTER_XT_MATCH_LENGTH=m ++CONFIG_NETFILTER_XT_MATCH_LIMIT=m ++CONFIG_NETFILTER_XT_MATCH_MAC=m ++CONFIG_NETFILTER_XT_MATCH_MARK=m ++CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m ++CONFIG_NETFILTER_XT_MATCH_NFACCT=m ++CONFIG_NETFILTER_XT_MATCH_OSF=m ++CONFIG_NETFILTER_XT_MATCH_OWNER=m ++CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m ++CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m ++CONFIG_NETFILTER_XT_MATCH_POLICY=m ++CONFIG_NETFILTER_XT_MATCH_QUOTA=m ++CONFIG_NETFILTER_XT_MATCH_RATEEST=m ++CONFIG_NETFILTER_XT_MATCH_REALM=m ++CONFIG_NETFILTER_XT_MATCH_RECENT=m ++CONFIG_NETFILTER_XT_MATCH_SCTP=m ++CONFIG_NETFILTER_XT_MATCH_SOCKET=m ++CONFIG_NETFILTER_XT_MATCH_STATE=y ++CONFIG_NETFILTER_XT_MATCH_STATISTIC=m ++CONFIG_NETFILTER_XT_MATCH_STRING=m ++CONFIG_NETFILTER_XT_MATCH_TCPMSS=m ++CONFIG_NETFILTER_XT_MATCH_TIME=m ++CONFIG_NETFILTER_XT_MATCH_U32=m ++ ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_BRIDGE_NETFILTER=y ++ ++# ++# IP: Netfilter Configuration ++# ++ ++CONFIG_NF_CONNTRACK_MARK=y ++CONFIG_NF_CONNTRACK_SECMARK=y ++CONFIG_NF_CONNTRACK_EVENTS=y ++CONFIG_NF_CONNTRACK_ZONES=y ++CONFIG_NF_CONNTRACK_PROCFS=y # check if contrack(8) in f17 supports netlink ++# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set ++CONFIG_NF_CONNTRACK_AMANDA=m ++CONFIG_NF_CONNTRACK_FTP=m ++CONFIG_NF_CONNTRACK_H323=m ++CONFIG_NF_CONNTRACK_IRC=m ++CONFIG_NF_CONNTRACK_NETBIOS_NS=m ++CONFIG_NF_CONNTRACK_PPTP=m ++CONFIG_NF_CONNTRACK_SANE=m ++CONFIG_NF_CONNTRACK_SIP=m ++CONFIG_NF_CONNTRACK_TFTP=m ++CONFIG_NF_CONNTRACK_IPV4=y ++CONFIG_NF_CONNTRACK_IPV6=y ++# CONFIG_NF_CONNTRACK_TIMEOUT is not set ++CONFIG_NF_CONNTRACK_TIMESTAMP=y ++CONFIG_NF_CONNTRACK_SNMP=m ++CONFIG_NF_NAT=m ++CONFIG_NF_NAT_SNMP_BASIC=m ++CONFIG_NF_CT_PROTO_DCCP=m ++CONFIG_NF_CT_PROTO_SCTP=m ++CONFIG_NF_CT_NETLINK=m ++# CONFIG_NF_CT_NETLINK_TIMEOUT is not set ++CONFIG_NF_CT_NETLINK_HELPER=m ++CONFIG_NF_CT_PROTO_UDPLITE=m ++ ++CONFIG_IP_NF_MATCH_AH=m ++CONFIG_IP_NF_MATCH_ECN=m ++CONFIG_IP_NF_MATCH_RPFILTER=m ++CONFIG_IP_NF_MATCH_TTL=m ++CONFIG_IP_NF_TARGET_CLUSTERIP=m ++CONFIG_IP_NF_TARGET_REDIRECT=m ++CONFIG_IP_NF_TARGET_NETMAP=m ++CONFIG_IP_NF_TARGET_ECN=m ++CONFIG_IP_NF_TARGET_LOG=m ++CONFIG_IP_NF_TARGET_ULOG=m ++CONFIG_IP_NF_TARGET_REJECT=y ++CONFIG_IP_NF_TARGET_SYNPROXY=m ++CONFIG_IP_NF_TARGET_TTL=m ++CONFIG_NF_NAT_IPV4=m ++CONFIG_IP_NF_TARGET_MASQUERADE=m ++CONFIG_IP_NF_MANGLE=m ++CONFIG_IP_NF_ARPTABLES=m ++CONFIG_IP_NF_ARPFILTER=m ++CONFIG_IP_NF_ARP_MANGLE=m ++CONFIG_IP_NF_QUEUE=m ++CONFIG_IP_NF_RAW=m ++ ++CONFIG_IP_NF_IPTABLES=y ++CONFIG_IP_NF_FILTER=y ++ ++# ++# IPv6: Netfilter Configuration ++# ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_IPTABLES=m ++CONFIG_IP6_NF_MANGLE=m ++CONFIG_IP6_NF_MATCH_AH=m ++CONFIG_IP6_NF_MATCH_EUI64=m ++CONFIG_IP6_NF_MATCH_FRAG=m ++CONFIG_IP6_NF_MATCH_HL=m ++CONFIG_IP6_NF_MATCH_IPV6HEADER=m ++CONFIG_IP6_NF_MATCH_MH=m ++CONFIG_IP6_NF_MATCH_RPFILTER=m ++CONFIG_IP6_NF_MATCH_OPTS=m ++CONFIG_IP6_NF_MATCH_RT=m ++CONFIG_IP6_NF_QUEUE=m ++CONFIG_IP6_NF_RAW=m ++CONFIG_IP6_NF_SECURITY=m ++CONFIG_IP6_NF_TARGET_LOG=m ++CONFIG_IP6_NF_TARGET_REJECT=m ++CONFIG_IP6_NF_TARGET_SYNPROXY=m ++CONFIG_IP6_NF_TARGET_HL=m ++CONFIG_NF_NAT_IPV6=m ++CONFIG_IP6_NF_TARGET_MASQUERADE=m ++# CONFIG_IP6_NF_TARGET_NPT is not set ++ ++# nf_tables support ++CONFIG_NF_TABLES=m ++CONFIG_NF_TABLES_INET=m ++CONFIG_NFT_EXTHDR=m ++CONFIG_NFT_META=m ++CONFIG_NFT_CT=m ++CONFIG_NFT_RBTREE=m ++CONFIG_NFT_HASH=m ++CONFIG_NFT_COUNTER=m ++CONFIG_NFT_LOG=m ++CONFIG_NFT_LIMIT=m ++CONFIG_NFT_NAT=m ++CONFIG_NFT_QUEUE=m ++CONFIG_NFT_REJECT=m ++CONFIG_NFT_COMPAT=m ++ ++CONFIG_NF_TABLES_IPV4=m ++CONFIG_NFT_REJECT_IPV4=m ++CONFIG_NFT_CHAIN_ROUTE_IPV4=m ++CONFIG_NFT_CHAIN_NAT_IPV4=m ++CONFIG_NF_TABLES_ARP=m ++ ++CONFIG_NF_TABLES_IPV6=m ++CONFIG_NFT_CHAIN_ROUTE_IPV6=m ++CONFIG_NFT_CHAIN_NAT_IPV6=m ++ ++CONFIG_NF_TABLES_BRIDGE=m ++# ++# Bridge: Netfilter Configuration ++# ++CONFIG_BRIDGE_NF_EBTABLES=m ++CONFIG_BRIDGE_EBT_802_3=m ++CONFIG_BRIDGE_EBT_AMONG=m ++CONFIG_BRIDGE_EBT_ARP=m ++CONFIG_BRIDGE_EBT_ARPREPLY=m ++CONFIG_BRIDGE_EBT_BROUTE=m ++CONFIG_BRIDGE_EBT_DNAT=m ++CONFIG_BRIDGE_EBT_IP=m ++CONFIG_BRIDGE_EBT_IP6=m ++CONFIG_BRIDGE_EBT_LIMIT=m ++CONFIG_BRIDGE_EBT_LOG=m ++CONFIG_BRIDGE_EBT_MARK=m ++CONFIG_BRIDGE_EBT_MARK_T=m ++CONFIG_BRIDGE_EBT_NFLOG=m ++CONFIG_BRIDGE_EBT_PKTTYPE=m ++CONFIG_BRIDGE_EBT_REDIRECT=m ++CONFIG_BRIDGE_EBT_SNAT=m ++CONFIG_BRIDGE_EBT_STP=m ++CONFIG_BRIDGE_EBT_T_FILTER=m ++CONFIG_BRIDGE_EBT_T_NAT=m ++CONFIG_BRIDGE_EBT_ULOG=m ++CONFIG_BRIDGE_EBT_VLAN=m ++CONFIG_XFRM=y ++CONFIG_XFRM_MIGRATE=y ++CONFIG_XFRM_SUB_POLICY=y ++CONFIG_XFRM_STATISTICS=y ++CONFIG_XFRM_USER=y ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++ ++CONFIG_IP_SET=m ++CONFIG_IP_SET_MAX=256 ++CONFIG_IP_SET_BITMAP_IP=m ++CONFIG_IP_SET_BITMAP_IPMAC=m ++CONFIG_IP_SET_BITMAP_PORT=m ++CONFIG_IP_SET_HASH_IP=m ++CONFIG_IP_SET_HASH_IPPORT=m ++CONFIG_IP_SET_HASH_IPPORTIP=m ++CONFIG_IP_SET_HASH_IPPORTNET=m ++CONFIG_IP_SET_HASH_NETPORTNET=m ++CONFIG_IP_SET_HASH_NET=m ++CONFIG_IP_SET_HASH_NETNET=m ++CONFIG_IP_SET_HASH_NETPORT=m ++CONFIG_IP_SET_HASH_NETIFACE=m ++CONFIG_IP_SET_LIST_SET=m ++ ++# ++# SCTP Configuration (EXPERIMENTAL) ++# ++CONFIG_IP_SCTP=m ++CONFIG_NET_SCTPPROBE=m ++# CONFIG_SCTP_DBG_MSG is not set ++# CONFIG_SCTP_DBG_OBJCNT is not set ++CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1=y ++# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5 is not set ++# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE is not set ++CONFIG_SCTP_COOKIE_HMAC_MD5=y ++CONFIG_SCTP_COOKIE_HMAC_SHA1=y ++CONFIG_ATM=m ++CONFIG_VLAN_8021Q_GVRP=y ++CONFIG_VLAN_8021Q_MVRP=y ++CONFIG_LLC=m ++# CONFIG_LLC2 is not set ++CONFIG_IPX=m ++# CONFIG_IPX_INTERN is not set ++CONFIG_ATALK=m ++CONFIG_DEV_APPLETALK=m ++CONFIG_IPDDP=m ++CONFIG_IPDDP_ENCAP=y ++CONFIG_IPDDP_DECAP=y ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++CONFIG_WAN_ROUTER=m ++CONFIG_IP_DCCP=m ++CONFIG_IP_DCCP_CCID2=m ++# CONFIG_IP_DCCP_CCID2_DEBUG is not set ++CONFIG_IP_DCCP_CCID3=y ++# CONFIG_IP_DCCP_CCID3_DEBUG is not set ++# CONFIG_IP_DCCP_DEBUG is not set ++# CONFIG_NET_DCCPPROBE is not set ++ ++# ++# TIPC Configuration (EXPERIMENTAL) ++# ++CONFIG_TIPC=m ++CONFIG_TIPC_PORTS=8192 ++# CONFIG_TIPC_MEDIA_IB is not set ++# CONFIG_TIPC_ADVANCED is not set ++# CONFIG_TIPC_DEBUG is not set ++ ++CONFIG_NETLABEL=y ++ ++# ++# QoS and/or fair queueing ++# ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_CBQ=m ++CONFIG_NET_SCH_DSMARK=m ++CONFIG_NET_SCH_DRR=m ++CONFIG_NET_SCH_GRED=m ++CONFIG_NET_SCH_HFSC=m ++CONFIG_NET_SCH_HTB=m ++CONFIG_NET_SCH_INGRESS=m ++CONFIG_NET_SCH_NETEM=m ++CONFIG_NET_SCH_PRIO=m ++CONFIG_NET_SCH_RED=m ++CONFIG_NET_SCH_SFQ=m ++CONFIG_NET_SCH_TBF=m ++CONFIG_NET_SCH_TEQL=m ++CONFIG_NET_SCH_SFB=m ++CONFIG_NET_SCH_MQPRIO=m ++CONFIG_NET_SCH_MULTIQ=m ++CONFIG_NET_SCH_CHOKE=m ++CONFIG_NET_SCH_QFQ=m ++CONFIG_NET_SCH_CODEL=m ++CONFIG_NET_SCH_FQ_CODEL=m ++CONFIG_NET_SCH_FQ=m ++CONFIG_NET_SCH_HHF=m ++CONFIG_NET_SCH_PIE=m ++CONFIG_NET_SCH_PLUG=m ++CONFIG_NET_CLS=y ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_CLS_BASIC=m ++CONFIG_NET_CLS_CGROUP=y ++CONFIG_NET_CLS_BPF=m ++CONFIG_NET_CLS_FLOW=m ++CONFIG_NET_CLS_FW=m ++CONFIG_NET_CLS_IND=y ++CONFIG_NET_CLS_ROUTE4=m ++CONFIG_NET_CLS_ROUTE=y ++CONFIG_NET_CLS_RSVP=m ++CONFIG_NET_CLS_RSVP6=m ++CONFIG_NET_CLS_TCINDEX=m ++CONFIG_NET_CLS_U32=m ++CONFIG_CLS_U32_MARK=y ++CONFIG_CLS_U32_PERF=y ++CONFIG_NET_EMATCH=y ++CONFIG_NET_EMATCH_CMP=m ++CONFIG_NET_EMATCH_META=m ++CONFIG_NET_EMATCH_NBYTE=m ++CONFIG_NET_EMATCH_STACK=32 ++CONFIG_NET_EMATCH_TEXT=m ++CONFIG_NET_EMATCH_IPSET=m ++CONFIG_NET_EMATCH_U32=m ++ ++CONFIG_NET_ACT_CSUM=m ++CONFIG_NET_ACT_GACT=m ++CONFIG_GACT_PROB=y ++CONFIG_NET_ACT_IPT=m ++CONFIG_NET_ACT_MIRRED=m ++CONFIG_NET_ACT_NAT=m ++CONFIG_NET_ACT_PEDIT=m ++CONFIG_NET_ACT_POLICE=m ++CONFIG_NET_ACT_SIMP=m ++CONFIG_NET_ACT_SKBEDIT=m ++ ++CONFIG_DCB=y ++CONFIG_DNS_RESOLVER=m ++CONFIG_BATMAN_ADV=m ++CONFIG_BATMAN_ADV_BLA=y ++CONFIG_BATMAN_ADV_DAT=y ++CONFIG_BATMAN_ADV_NC=y ++ ++# CONFIG_BATMAN_ADV_DEBUG is not set ++CONFIG_OPENVSWITCH=m ++CONFIG_OPENVSWITCH_GRE=y ++CONFIG_OPENVSWITCH_VXLAN=y ++CONFIG_VSOCKETS=m ++ ++ ++# ++# Network testing ++# ++CONFIG_NET_PKTGEN=m ++# CONFIG_NET_TCPPROBE is not set ++CONFIG_NET_DROP_MONITOR=y ++ ++# disable later --kyle ++ ++# ++# ARCnet devices ++# ++# CONFIG_ARCNET is not set ++CONFIG_IFB=m ++CONFIG_NET_TEAM=m ++CONFIG_NET_TEAM_MODE_ROUNDROBIN=m ++CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m ++CONFIG_NET_TEAM_MODE_LOADBALANCE=m ++CONFIG_NET_TEAM_MODE_BROADCAST=m ++CONFIG_NET_TEAM_MODE_RANDOM=m ++CONFIG_DUMMY=m ++CONFIG_BONDING=m ++CONFIG_MACVLAN=m ++CONFIG_MACVTAP=m ++CONFIG_VXLAN=m ++CONFIG_EQUALIZER=m ++CONFIG_TUN=m ++CONFIG_VETH=m ++CONFIG_NLMON=m ++ ++# ++# ATM ++# ++CONFIG_ATM_DRIVERS=y ++# CONFIG_ATM_DUMMY is not set ++CONFIG_ATM_CLIP=m ++CONFIG_ATM_LANE=m ++CONFIG_ATM_BR2684=m ++CONFIG_NET_SCH_ATM=m ++CONFIG_ATM_TCP=m ++# CONFIG_ATM_LANAI is not set ++CONFIG_ATM_ENI=m ++CONFIG_ATM_FIRESTREAM=m ++# CONFIG_ATM_ZATM is not set ++# CONFIG_ATM_IDT77252 is not set ++# CONFIG_ATM_AMBASSADOR is not set ++# CONFIG_ATM_HORIZON is not set ++# CONFIG_ATM_FORE200E is not set ++# CONFIG_ATM_FORE200E_USE_TASKLET is not set ++CONFIG_ATM_FORE200E_TX_RETRY=16 ++CONFIG_ATM_FORE200E_DEBUG=0 ++ ++CONFIG_ATM_HE=m ++CONFIG_PPTP=m ++CONFIG_PPPOATM=m ++CONFIG_PPPOL2TP=m ++CONFIG_ATM_NICSTAR=m ++# CONFIG_ATM_IA is not set ++# CONFIG_ATM_CLIP_NO_ICMP is not set ++# CONFIG_ATM_MPOA is not set ++# CONFIG_ATM_BR2684_IPFILTER is not set ++# CONFIG_ATM_ENI_DEBUG is not set ++# CONFIG_ATM_ENI_TUNE_BURST is not set ++# CONFIG_ATM_ZATM_DEBUG is not set ++# CONFIG_ATM_IDT77252_DEBUG is not set ++# CONFIG_ATM_IDT77252_RCV_ALL is not set ++# CONFIG_ATM_AMBASSADOR_DEBUG is not set ++# CONFIG_ATM_HORIZON_DEBUG is not set ++# CONFIG_ATM_HE_USE_SUNI is not set ++# CONFIG_ATM_NICSTAR_USE_SUNI is not set ++# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set ++# CONFIG_ATM_IA_DEBUG is not set ++CONFIG_ATM_SOLOS=m ++ ++CONFIG_L2TP=m ++CONFIG_L2TP_V3=y ++CONFIG_L2TP_IP=m ++CONFIG_L2TP_ETH=m ++ ++# CONFIG_CAIF is not set ++ ++CONFIG_RFKILL=m ++CONFIG_RFKILL_GPIO=m ++CONFIG_RFKILL_INPUT=y ++ ++ ++# ++# Ethernet (10 or 100Mbit) ++# ++ ++CONFIG_NET_VENDOR_ADAPTEC=y ++CONFIG_ADAPTEC_STARFIRE=m ++ ++CONFIG_NET_VENDOR_ALTEON=y ++CONFIG_ACENIC=m ++# CONFIG_ACENIC_OMIT_TIGON_I is not set ++ ++CONFIG_NET_VENDOR_AMD=y ++CONFIG_PCNET32=m ++CONFIG_AMD8111_ETH=m ++CONFIG_PCMCIA_NMCLAN=m ++ ++CONFIG_NET_VENDOR_ARC=y ++CONFIG_ARC_EMAC=m ++ ++CONFIG_NET_VENDOR_ATHEROS=y ++CONFIG_ALX=m ++CONFIG_ATL2=m ++CONFIG_ATL1=m ++CONFIG_ATL1C=m ++CONFIG_ATL1E=m ++CONFIG_NET_CADENCE=y ++CONFIG_ARM_AT91_ETHER=m ++CONFIG_MACB=m ++ ++CONFIG_NET_VENDOR_BROCADE=y ++CONFIG_BNA=m ++CONFIG_NET_CALXEDA_XGMAC=m ++ ++CONFIG_NET_VENDOR_CHELSIO=y ++CONFIG_CHELSIO_T1=m ++CONFIG_CHELSIO_T1_1G=y ++CONFIG_CHELSIO_T3=m ++CONFIG_CHELSIO_T4=m ++CONFIG_CHELSIO_T4VF=m ++ ++CONFIG_NET_VENDOR_CISCO=y ++CONFIG_ENIC=m ++ ++CONFIG_NET_VENDOR_DEC=y ++# ++# Tulip family network device support ++# ++CONFIG_NET_TULIP=y ++CONFIG_DE2104X=m ++CONFIG_DE2104X_DSL=0 ++CONFIG_TULIP=m ++# CONFIG_TULIP_NAPI is not set ++# CONFIG_TULIP_MWI is not set ++CONFIG_TULIP_MMIO=y ++# CONFIG_NI5010 is not set ++CONFIG_DE4X5=m ++CONFIG_WINBOND_840=m ++CONFIG_DM9102=m ++CONFIG_PCMCIA_XIRCOM=m ++CONFIG_ULI526X=m ++ ++CONFIG_NET_VENDOR_DLINK=y ++CONFIG_DE600=m ++CONFIG_DE620=m ++CONFIG_DL2K=m ++CONFIG_SUNDANCE=m ++# CONFIG_SUNDANCE_MMIO is not set ++ ++CONFIG_NET_VENDOR_EMULEX=y ++CONFIG_BE2NET=m ++ ++CONFIG_NET_VENDOR_EXAR=y ++CONFIG_S2IO=m ++CONFIG_VXGE=m ++# CONFIG_VXGE_DEBUG_TRACE_ALL is not set ++ ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_FUJITSU is not set ++# CONFIG_NET_VENDOR_HP is not set ++CONFIG_NET_VENDOR_INTEL=y ++CONFIG_E100=m ++CONFIG_E1000=m ++CONFIG_E1000E=m ++CONFIG_IGB=m ++CONFIG_IGB_HWMON=y ++CONFIG_IGB_DCA=y ++CONFIG_IGB_PTP=y ++CONFIG_IGBVF=m ++CONFIG_IXGB=m ++CONFIG_IXGBEVF=m ++CONFIG_IXGBE=m ++CONFIG_IXGBE_DCA=y ++CONFIG_IXGBE_DCB=y ++CONFIG_IXGBE_HWMON=y ++CONFIG_IXGBE_PTP=y ++CONFIG_I40E=m ++# CONFIG_I40E_VXLAN is not set ++# CONFIG_I40E_DCB is not set ++# CONFIG_I40EVF is not set ++ ++ ++# CONFIG_NET_VENDOR_I825XX is not set ++CONFIG_NET_VENDOR_MARVELL=y ++CONFIG_MVMDIO=m ++CONFIG_SKGE=m ++# CONFIG_SKGE_DEBUG is not set ++CONFIG_SKGE_GENESIS=y ++CONFIG_SKY2=m ++# CONFIG_SKY2_DEBUG is not set ++ ++CONFIG_NET_VENDOR_MICREL=y ++CONFIG_KSZ884X_PCI=m ++# CONFIG_KS8842 is not set ++# CONFIG_KS8851_MLL is not set ++ ++CONFIG_NET_VENDOR_MYRI=y ++CONFIG_MYRI10GE=m ++CONFIG_MYRI10GE_DCA=y ++ ++CONFIG_NATSEMI=m ++CONFIG_NS83820=m ++ ++CONFIG_PCMCIA_AXNET=m ++CONFIG_NE2K_PCI=m ++CONFIG_NE3210=m ++CONFIG_PCMCIA_PCNET=m ++ ++CONFIG_NET_VENDOR_NVIDIA=y ++CONFIG_FORCEDETH=m ++ ++CONFIG_NET_VENDOR_OKI=y ++# CONFIG_PCH_GBE is not set ++# CONFIG_PCH_PTP is not set ++ ++CONFIG_NET_PACKET_ENGINE=y ++CONFIG_HAMACHI=m ++CONFIG_YELLOWFIN=m ++ ++CONFIG_NET_VENDOR_QLOGIC=y ++CONFIG_QLA3XXX=m ++CONFIG_QLCNIC=m ++CONFIG_QLCNIC_SRIOV=y ++CONFIG_QLCNIC_DCB=y ++CONFIG_QLGE=m ++CONFIG_NETXEN_NIC=m ++ ++CONFIG_NET_VENDOR_REALTEK=y ++CONFIG_ATP=m ++CONFIG_8139CP=m ++CONFIG_8139TOO=m ++# CONFIG_8139TOO_PIO is not set ++# CONFIG_8139TOO_TUNE_TWISTER is not set ++CONFIG_8139TOO_8129=y ++# CONFIG_8139_OLD_RX_RESET is not set ++CONFIG_R8169=m ++ ++ ++CONFIG_NET_VENDOR_RDC=y ++CONFIG_R6040=m ++ ++ ++CONFIG_NET_VENDOR_SILAN=y ++CONFIG_SC92031=m ++ ++CONFIG_NET_VENDOR_SIS=y ++CONFIG_SIS900=m ++CONFIG_SIS190=m ++ ++CONFIG_PCMCIA_SMC91C92=m ++CONFIG_EPIC100=m ++CONFIG_SMSC9420=m ++ ++# CONFIG_STMMAC_PLATFORM is not set ++# CONFIG_STMMAC_PCI is not set ++# CONFIG_STMMAC_DA is not set ++# CONFIG_STMMAC_DUAL_MAC is not set ++# CONFIG_STMMAC_TIMER is not set ++# CONFIG_STMMAC_DEBUG_FS is not set ++ ++CONFIG_NET_VENDOR_SUN=y ++CONFIG_HAPPYMEAL=m ++CONFIG_SUNGEM=m ++CONFIG_CASSINI=m ++CONFIG_NIU=m ++ ++CONFIG_NET_VENDOR_TEHUTI=y ++CONFIG_TEHUTI=m ++ ++CONFIG_NET_VENDOR_TI=y ++CONFIG_TLAN=m ++ ++CONFIG_VIA_RHINE=m ++CONFIG_VIA_RHINE_MMIO=y ++ ++CONFIG_WIZNET_W5100=m ++CONFIG_WIZNET_W5300=m ++CONFIG_NET_VENDOR_XIRCOM=y ++CONFIG_PCMCIA_XIRC2PS=m ++ ++CONFIG_AMD_PHY=m ++CONFIG_BROADCOM_PHY=m ++CONFIG_BCM87XX_PHY=m ++CONFIG_CICADA_PHY=m ++CONFIG_DAVICOM_PHY=m ++CONFIG_DP83640_PHY=m ++CONFIG_FIXED_PHY=y ++CONFIG_MDIO_BITBANG=m ++CONFIG_NATIONAL_PHY=m ++CONFIG_ICPLUS_PHY=m ++CONFIG_BCM63XX_PHY=m ++CONFIG_LSI_ET1011C_PHY=m ++CONFIG_LXT_PHY=m ++CONFIG_MARVELL_PHY=m ++CONFIG_QSEMI_PHY=m ++CONFIG_REALTEK_PHY=m ++CONFIG_SMSC_PHY=m ++CONFIG_STE10XP=m ++CONFIG_VITESSE_PHY=m ++CONFIG_MICREL_PHY=m ++ ++CONFIG_MII=m ++CONFIG_NET_CORE=y ++CONFIG_NET_VENDOR_3COM=y ++CONFIG_VORTEX=m ++CONFIG_TYPHOON=m ++CONFIG_DNET=m ++ ++ ++CONFIG_LNE390=m ++CONFIG_ES3210=m ++CONFIG_NET_PCI=y ++CONFIG_B44=m ++CONFIG_B44_PCI=y ++CONFIG_BNX2=m ++CONFIG_BNX2X=m ++CONFIG_BNX2X_SRIOV=y ++CONFIG_CNIC=m ++CONFIG_FEALNX=m ++CONFIG_NET_POCKET=y ++ ++# ++# Ethernet (1000 Mbit) ++# ++CONFIG_TIGON3=m ++CONFIG_JME=m ++ ++# ++# Ethernet (10000 Mbit) ++# ++# CONFIG_IP1000 is not set ++# CONFIG_MLX4_EN is not set ++# CONFIG_SFC is not set ++ ++# CONFIG_FDDI is not set ++# CONFIG_DEFXX is not set ++# CONFIG_SKFP is not set ++# CONFIG_HIPPI is not set ++# CONFIG_PLIP is not set ++CONFIG_PPP=m ++CONFIG_PPP_MULTILINK=y ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_IPPP_FILTER=y ++CONFIG_PPP_BSDCOMP=y ++CONFIG_PPPOE=m ++CONFIG_PPP_MPPE=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_SMART=y ++# CONFIG_SLIP_MODE_SLIP6 is not set ++ ++# ++# Wireless LAN ++# ++# ++# CONFIG_STRIP is not set ++# CONFIG_PCMCIA_RAYCS is not set ++ ++CONFIG_CFG80211_WEXT=y ++# CONFIG_CFG80211_REG_DEBUG is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++CONFIG_NL80211=y ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_WIRELESS_EXT_SYSFS is not set ++CONFIG_LIB80211=m ++CONFIG_LIB80211_CRYPT_WEP=m ++CONFIG_LIB80211_CRYPT_CCMP=m ++CONFIG_LIB80211_CRYPT_TKIP=m ++# CONFIG_LIB80211_DEBUG is not set ++ ++CONFIG_MAC80211=m ++CONFIG_MAC80211_RC_MINSTREL=y ++# CONFIG_MAC80211_RC_DEFAULT_PID is not set ++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y ++CONFIG_MAC80211_RC_DEFAULT="minstrel" ++CONFIG_MAC80211_MESH=y ++CONFIG_MAC80211_LEDS=y ++# CONFIG_MAC80211_DEBUG_MENU is not set ++ ++# CONFIG_WIMAX is not set ++ ++# CONFIG_ADM8211 is not set ++CONFIG_ATH_COMMON=m ++CONFIG_ATH_CARDS=m ++CONFIG_ATH5K=m ++CONFIG_ATH5K_DEBUG=y ++# CONFIG_ATH5K_TRACER is not set ++CONFIG_ATH6KL=m ++CONFIG_ATH6KL_DEBUG=y ++CONFIG_ATH6KL_SDIO=m ++CONFIG_ATH6KL_USB=m ++# CONFIG_ATH6KL_TRACING is not set ++CONFIG_AR5523=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_PCI=y ++CONFIG_ATH9K_AHB=y ++# CONFIG_ATH9K_DEBUG is not set ++# CONFIG_ATH9K_MAC_DEBUG is not set ++CONFIG_ATH9K_HTC=m ++CONFIG_ATH9K_BTCOEX_SUPPORT=y ++# CONFIG_ATH9K_LEGACY_RATE_CONTROL is not set ++# CONFIG_ATH9K_WOW is not set ++# ++CONFIG_ATH10K=m ++CONFIG_ATH10K_PCI=m ++# CONFIG_ATH10K_DEBUG is not set ++# CONFIG_ATH10K_TRACING is not set ++CONFIG_ATH10K_DEBUGFS=y ++CONFIG_WCN36XX=m ++# CONFIG_WCN36XX_DEBUGFS is not set ++CONFIG_WIL6210=m ++CONFIG_WIL6210_ISR_COR=y ++# CONFIG_WIL6210_TRACING is not set ++CONFIG_CARL9170=m ++CONFIG_CARL9170_LEDS=y ++# CONFIG_CARL9170_HWRNG is not set ++CONFIG_AT76C50X_USB=m ++# CONFIG_AIRO is not set ++# CONFIG_AIRO_CS is not set ++# CONFIG_ATMEL is not set ++CONFIG_B43=m ++CONFIG_B43_PCMCIA=y ++CONFIG_B43_SDIO=y ++CONFIG_B43_BCMA=y ++# CONFIG_B43_BCMA_EXTRA is not set ++CONFIG_B43_BCMA_PIO=y ++# CONFIG_B43_DEBUG is not set ++CONFIG_B43_PHY_LP=y ++CONFIG_B43_PHY_N=y ++CONFIG_B43_PHY_HT=y ++# CONFIG_B43_FORCE_PIO is not set ++CONFIG_B43LEGACY=m ++# CONFIG_B43LEGACY_DEBUG is not set ++CONFIG_B43LEGACY_DMA=y ++CONFIG_B43LEGACY_PIO=y ++CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y ++# CONFIG_B43LEGACY_DMA_MODE is not set ++# CONFIG_B43LEGACY_PIO_MODE is not set ++CONFIG_BRCMSMAC=m ++# CONFIG_BRCMFMAC_SDIO_OOB is not set ++CONFIG_BRCMFMAC_USB=y ++# CONFIG_BRCM_TRACING is not set ++# CONFIG_BRCMISCAN is not set ++# CONFIG_BRCMDBG is not set ++CONFIG_HERMES=m ++CONFIG_HERMES_CACHE_FW_ON_INIT=y ++# CONFIG_HERMES_PRISM is not set ++CONFIG_NORTEL_HERMES=m ++CONFIG_PCI_HERMES=m ++CONFIG_PLX_HERMES=m ++CONFIG_PCMCIA_HERMES=m ++CONFIG_ORINOCO_USB=m ++# CONFIG_TMD_HERMES is not set ++# CONFIG_PCMCIA_SPECTRUM is not set ++CONFIG_CW1200=m ++CONFIG_CW1200_WLAN_SDIO=m ++CONFIG_CW1200_WLAN_SPI=m ++# CONFIG_HOSTAP is not set ++# CONFIG_IPW2100 is not set ++# CONFIG_IPW2200 is not set ++# CONFIG_IPW2100_DEBUG is not set ++# CONFIG_IPW2200_DEBUG is not set ++# CONFIG_LIBIPW_DEBUG is not set ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_CS=m ++CONFIG_LIBERTAS_SDIO=m ++# CONFIG_LIBERTAS_DEBUG is not set ++# CONFIG_LIBERTAS_THINFIRM is not set ++CONFIG_LIBERTAS_MESH=y ++CONFIG_IWLWIFI=m ++CONFIG_IWLDVM=m ++CONFIG_IWLMVM=m ++CONFIG_IWLWIFI_DEBUG=y ++CONFIG_IWLWIFI_DEVICE_SVTOOL=y ++# CONFIG_IWLWIFI_EXPERIMENTAL_MFP is not set ++CONFIG_IWLWIFI_UCODE16=y ++# CONFIG_IWLWIFI_P2P is not set ++CONFIG_IWLEGACY=m ++CONFIG_IWLEGACY_DEBUG=y ++# CONFIG_IWLWIFI_LEGACY_DEVICE_TRACING is not set ++CONFIG_IWL4965=y ++CONFIG_IWL3945=m ++# CONFIG_IWM is not set ++# CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE is not set ++CONFIG_MAC80211_HWSIM=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_P54_PCI=m ++CONFIG_MWL8K=m ++# CONFIG_PRISM54 is not set ++# CONFIG_PCMCIA_WL3501 is not set ++CONFIG_RT2X00=m ++# CONFIG_RT2X00_DEBUG is not set ++CONFIG_RT2400PCI=m ++CONFIG_RT2500PCI=m ++CONFIG_RT61PCI=m ++CONFIG_RT2500USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT33XX=y ++CONFIG_RT2800USB_RT35XX=y ++CONFIG_RT2800USB_RT3573=y ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RT2800USB_RT55XX=y ++CONFIG_RT2800USB_UNKNOWN=y ++CONFIG_RT2800PCI=m ++CONFIG_RT2800PCI_RT3290=y ++CONFIG_RT2800PCI_RT33XX=y ++CONFIG_RT2800PCI_RT35XX=y ++CONFIG_RT2800PCI_RT53XX=y ++CONFIG_RT73USB=m ++CONFIG_RTL8180=m ++CONFIG_RTL8187=m ++# CONFIG_USB_ZD1201 is not set ++# CONFIG_USB_NET_SR9800 is not set ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_USB_NET_KALMIA=m ++CONFIG_USB_NET_QMI_WWAN=m ++CONFIG_USB_NET_SMSC75XX=m ++# CONFIG_WL_TI is not set ++CONFIG_ZD1211RW=m ++# CONFIG_ZD1211RW_DEBUG is not set ++ ++CONFIG_WL12XX=m ++CONFIG_WL12XX_SPI=m ++CONFIG_WL12XX_SDIO=m ++ ++CONFIG_WL1251=m ++CONFIG_WL1251_SPI=m ++CONFIG_WL1251_SDIO=m ++ ++CONFIG_RTL_CARDS=m ++CONFIG_RTLWIFI=m ++CONFIG_RTL8192CE=m ++CONFIG_RTL8192SE=m ++CONFIG_RTL8192CU=m ++CONFIG_RTL8192DE=m ++CONFIG_RTL8723AE=m ++CONFIG_RTL8188EE=m ++ ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_MWIFIEX_PCIE=m ++CONFIG_MWIFIEX_USB=m ++ ++# ++# Token Ring devices ++# ++# CONFIG_TR is not set ++ ++CONFIG_NET_FC=y ++ ++# ++# Wan interfaces ++# ++# CONFIG_WAN is not set ++ ++# ++# PCMCIA network device support ++# ++CONFIG_NET_PCMCIA=y ++CONFIG_PCMCIA_3C589=m ++CONFIG_PCMCIA_3C574=m ++CONFIG_PCMCIA_FMVJ18X=m ++ ++# ++# Amateur Radio support ++# ++CONFIG_HAMRADIO=y ++CONFIG_AX25=m ++CONFIG_AX25_DAMA_SLAVE=y ++ ++# CONFIG_CAN is not set ++ ++CONFIG_NETROM=m ++CONFIG_ROSE=m ++CONFIG_MKISS=m ++CONFIG_6PACK=m ++CONFIG_BPQETHER=m ++CONFIG_BAYCOM_SER_FDX=m ++CONFIG_BAYCOM_SER_HDX=m ++CONFIG_BAYCOM_PAR=m ++CONFIG_BAYCOM_EPP=m ++CONFIG_YAM=m ++ ++CONFIG_NFC=m ++CONFIG_NFC_DIGITAL=m ++CONFIG_NFC_NCI=m ++CONFIG_NFC_HCI=m ++CONFIG_NFC_SHDLC=y ++CONFIG_NFC_LLCP=y ++CONFIG_NFC_SIM=m ++CONFIG_NFC_MRVL=m ++CONFIG_NFC_MRVL_USB=m ++ ++# ++# Near Field Communication (NFC) devices ++# ++CONFIG_NFC_PORT100=m ++CONFIG_NFC_PN544=m ++CONFIG_NFC_PN544_I2C=m ++CONFIG_NFC_PN533=m ++CONFIG_NFC_MICROREAD=m ++CONFIG_NFC_MICROREAD_I2C=m ++ ++# ++# IrDA (infrared) support ++# ++CONFIG_IRDA=m ++# CONFIG_IRDA_DEBUG is not set ++CONFIG_IRLAN=m ++CONFIG_IRNET=m ++CONFIG_IRCOMM=m ++# CONFIG_IRDA_ULTRA is not set ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_DONGLE=y ++CONFIG_ACTISYS_DONGLE=m ++CONFIG_ACT200L_DONGLE=m ++CONFIG_ESI_DONGLE=m ++CONFIG_GIRBIL_DONGLE=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_LITELINK_DONGLE=m ++CONFIG_MA600_DONGLE=m ++CONFIG_MCP2120_DONGLE=m ++CONFIG_OLD_BELKIN_DONGLE=m ++CONFIG_TEKRAM_DONGLE=m ++CONFIG_TOIM3232_DONGLE=m ++ ++CONFIG_ALI_FIR=m ++CONFIG_MCS_FIR=m ++CONFIG_NSC_FIR=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_SMC_IRCC_FIR=m ++# CONFIG_TOSHIBA_FIR is not set ++CONFIG_USB_IRDA=m ++CONFIG_VLSI_FIR=m ++CONFIG_VIA_FIR=m ++CONFIG_WINBOND_FIR=m ++ ++# ++# Bluetooth support ++# ++CONFIG_BT=m ++CONFIG_BT_L2CAP=y ++CONFIG_BT_SCO=y ++CONFIG_BT_CMTP=m ++CONFIG_BT_RFCOMM=m ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=m ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=m ++ ++# ++# Bluetooth device drivers ++# ++CONFIG_BT_HCIBTUSB=m ++# Disable the BT_HCIUSB driver. ++# It sucks more power than BT_HCIBTUSB which has the same functionality. ++CONFIG_BT_HCIUART=m ++CONFIG_BT_HCIUART_H4=y ++CONFIG_BT_HCIUART_BCSP=y ++CONFIG_BT_HCIUART_ATH3K=y ++CONFIG_BT_HCIUART_3WIRE=y ++CONFIG_BT_HCIDTL1=m ++CONFIG_BT_HCIBT3C=m ++CONFIG_BT_HCIBLUECARD=m ++CONFIG_BT_HCIBTUART=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBTSDIO=m ++CONFIG_BT_HCIUART_LL=y ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_BT_WILINK=m ++ ++# ++# ISDN subsystem ++# ++CONFIG_ISDN=y ++CONFIG_MISDN=m ++CONFIG_MISDN_DSP=m ++CONFIG_MISDN_L1OIP=m ++CONFIG_MISDN_AVMFRITZ=m ++CONFIG_MISDN_SPEEDFAX=m ++CONFIG_MISDN_INFINEON=m ++CONFIG_MISDN_W6692=m ++CONFIG_MISDN_NETJET=m ++ ++# ++# mISDN hardware drivers ++# ++CONFIG_MISDN_HFCPCI=m ++CONFIG_MISDN_HFCMULTI=m ++CONFIG_ISDN_I4L=m ++CONFIG_ISDN_DRV_AVMB1_B1PCI=m ++CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m ++CONFIG_ISDN_DRV_AVMB1_T1PCI=m ++CONFIG_ISDN_DRV_AVMB1_C4=m ++ ++CONFIG_MISDN_HFCUSB=m ++ ++CONFIG_ISDN_PPP=y ++CONFIG_ISDN_PPP_VJ=y ++CONFIG_ISDN_MPP=y ++# CONFIG_ISDN_PPP_BSDCOMP is not set ++CONFIG_ISDN_TTY_FAX=y ++CONFIG_DE_AOC=y ++ ++CONFIG_ISDN_AUDIO=y ++ ++CONFIG_ISDN_DRV_HISAX=m ++CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y ++CONFIG_ISDN_DRV_AVMB1_AVM_CS=m ++ ++CONFIG_ISDN_CAPI_CAPIDRV=m ++CONFIG_ISDN_DIVERSION=m ++ ++CONFIG_HISAX_EURO=y ++CONFIG_HISAX_1TR6=y ++CONFIG_HISAX_NI1=y ++CONFIG_HISAX_MAX_CARDS=8 ++CONFIG_HISAX_16_3=y ++CONFIG_HISAX_TELESPCI=y ++CONFIG_HISAX_S0BOX=y ++CONFIG_HISAX_FRITZPCI=y ++CONFIG_HISAX_AVM_A1_PCMCIA=y ++CONFIG_HISAX_ELSA=y ++CONFIG_HISAX_DIEHLDIVA=y ++CONFIG_HISAX_SEDLBAUER=y ++CONFIG_HISAX_NETJET=y ++CONFIG_HISAX_NETJET_U=y ++CONFIG_HISAX_NICCY=y ++CONFIG_HISAX_BKM_A4T=y ++CONFIG_HISAX_SCT_QUADRO=y ++CONFIG_HISAX_GAZEL=y ++CONFIG_HISAX_HFC_PCI=y ++CONFIG_HISAX_W6692=y ++CONFIG_HISAX_HFC_SX=y ++CONFIG_HISAX_ENTERNOW_PCI=y ++# CONFIG_HISAX_DEBUG is not set ++CONFIG_HISAX_AVM_A1_CS=m ++CONFIG_HISAX_ST5481=m ++# CONFIG_HISAX_HFCUSB is not set ++CONFIG_HISAX_FRITZ_PCIPNP=m ++CONFIG_HISAX_NO_SENDCOMPLETE=y ++CONFIG_HISAX_NO_LLC=y ++CONFIG_HISAX_NO_KEYPAD=y ++CONFIG_HISAX_SEDLBAUER_CS=m ++CONFIG_HISAX_ELSA_CS=m ++CONFIG_HISAX_TELES_CS=m ++CONFIG_HISAX_HFC4S8S=m ++ ++CONFIG_ISDN_DRV_LOOP=m ++CONFIG_HYSDN=m ++CONFIG_HYSDN_CAPI=y ++ ++ ++# ++# CAPI subsystem ++# ++CONFIG_ISDN_CAPI=m ++# CONFIG_CAPI_TRACE is not set ++CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y ++CONFIG_ISDN_CAPI_MIDDLEWARE=y ++CONFIG_ISDN_CAPI_CAPI20=m ++ ++# ++# CAPI hardware drivers ++# ++ ++# ++# Active AVM cards ++# ++CONFIG_CAPI_AVM=y ++ ++# ++# Active Eicon DIVA Server cards ++# ++# CONFIG_CAPI_EICON is not set ++CONFIG_ISDN_DIVAS=m ++CONFIG_ISDN_DIVAS_BRIPCI=y ++CONFIG_ISDN_DIVAS_PRIPCI=y ++CONFIG_ISDN_DIVAS_DIVACAPI=m ++CONFIG_ISDN_DIVAS_USERIDI=m ++CONFIG_ISDN_DIVAS_MAINT=m ++ ++CONFIG_ISDN_DRV_GIGASET=m ++CONFIG_GIGASET_CAPI=y ++CONFIG_GIGASET_BASE=m ++CONFIG_GIGASET_M101=m ++CONFIG_GIGASET_M105=m ++# CONFIG_GIGASET_DEBUG is not set ++ ++# ++# Telephony Support ++# ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++CONFIG_INPUT_FF_MEMLESS=m ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++CONFIG_INPUT_JOYDEV=m ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++CONFIG_INPUT_TABLET=y ++CONFIG_TABLET_USB_ACECAD=m ++CONFIG_TABLET_USB_AIPTEK=m ++CONFIG_TABLET_USB_GTCO=m ++CONFIG_TABLET_USB_HANWANG=m ++CONFIG_TABLET_USB_KBTAB=m ++CONFIG_TABLET_USB_WACOM=m ++ ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m ++CONFIG_INPUT_POLLDEV=m ++CONFIG_INPUT_SPARSEKMAP=m ++# CONFIG_INPUT_ADXL34X is not set ++# CONFIG_INPUT_BMA150 is not set ++# CONFIG_INPUT_IMS_PCU is not set ++CONFIG_INPUT_CMA3000=m ++CONFIG_INPUT_CMA3000_I2C=m ++CONFIG_INPUT_IDEAPAD_SLIDEBAR=m ++ ++# ++# Input I/O drivers ++# ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++CONFIG_GAMEPORT_EMU10K1=m ++CONFIG_GAMEPORT_FM801=m ++CONFIG_SERIO=y ++CONFIG_SERIO_I8042=y ++CONFIG_SERIO_RAW=m ++CONFIG_SERIO_ALTERA_PS2=m ++# CONFIG_SERIO_PS2MULT is not set ++CONFIG_SERIO_ARC_PS2=m ++# CONFIG_SERIO_APBPS2 is not set ++ ++# CONFIG_SERIO_CT82C710 is not set ++# CONFIG_SERIO_OLPC_APSP is not set ++# CONFIG_SERIO_PARKBD is not set ++# CONFIG_SERIO_PCIPS2 is not set ++# CONFIG_SERIO_LIBPS2 is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_SH_KEYSC is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_LM8323 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++CONFIG_INPUT_MOUSE=y ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++CONFIG_MOUSE_PS2_ELANTECH=y ++CONFIG_MOUSE_PS2_SENTELIC=y ++CONFIG_MOUSE_SERIAL=m ++CONFIG_MOUSE_VSXXXAA=m ++CONFIG_MOUSE_APPLETOUCH=m ++CONFIG_MOUSE_BCM5974=m ++CONFIG_MOUSE_SYNAPTICS_I2C=m ++CONFIG_MOUSE_SYNAPTICS_USB=m ++CONFIG_MOUSE_CYAPA=m ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_JOYSTICK_ANALOG=m ++CONFIG_JOYSTICK_A3D=m ++CONFIG_JOYSTICK_ADI=m ++CONFIG_JOYSTICK_COBRA=m ++CONFIG_JOYSTICK_GF2K=m ++CONFIG_JOYSTICK_GRIP=m ++CONFIG_JOYSTICK_GRIP_MP=m ++CONFIG_JOYSTICK_GUILLEMOT=m ++CONFIG_JOYSTICK_INTERACT=m ++CONFIG_JOYSTICK_SIDEWINDER=m ++CONFIG_JOYSTICK_TMDC=m ++CONFIG_JOYSTICK_IFORCE=m ++CONFIG_JOYSTICK_IFORCE_USB=y ++CONFIG_JOYSTICK_IFORCE_232=y ++CONFIG_JOYSTICK_WARRIOR=m ++CONFIG_JOYSTICK_MAGELLAN=m ++CONFIG_JOYSTICK_SPACEORB=m ++CONFIG_JOYSTICK_SPACEBALL=m ++CONFIG_JOYSTICK_STINGER=m ++CONFIG_JOYSTICK_DB9=m ++CONFIG_JOYSTICK_GAMECON=m ++CONFIG_JOYSTICK_TURBOGRAFX=m ++CONFIG_JOYSTICK_JOYDUMP=m ++CONFIG_JOYSTICK_TWIDJOY=m ++CONFIG_JOYSTICK_WALKERA0701=m ++CONFIG_JOYSTICK_XPAD=m ++CONFIG_JOYSTICK_XPAD_FF=y ++CONFIG_JOYSTICK_XPAD_LEDS=y ++CONFIG_JOYSTICK_ZHENHUA=m ++# CONFIG_JOYSTICK_AS5011 is not set ++ ++CONFIG_INPUT_TOUCHSCREEN=y ++# CONFIG_TOUCHSCREEN_AD7879 is not set ++CONFIG_TOUCHSCREEN_AD7879_I2C=m ++# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set ++# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set ++# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set ++CONFIG_TOUCHSCREEN_DYNAPRO=m ++CONFIG_TOUCHSCREEN_EDT_FT5X06=m ++CONFIG_TOUCHSCREEN_EETI=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_ELO=m ++CONFIG_TOUCHSCREEN_FUJITSU=m ++CONFIG_TOUCHSCREEN_GUNZE=m ++# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set ++CONFIG_TOUCHSCREEN_INEXIO=m ++CONFIG_TOUCHSCREEN_ILI210X=m ++CONFIG_TOUCHSCREEN_MMS114=m ++CONFIG_TOUCHSCREEN_MTOUCH=m ++CONFIG_TOUCHSCREEN_MCS5000=m ++CONFIG_TOUCHSCREEN_MK712=m ++CONFIG_TOUCHSCREEN_PENMOUNT=m ++# CONFIG_TOUCHSCREEN_SUR40 is not set ++# CONFIG_TOUCHSCREEN_TPS6507X is not set ++CONFIG_TOUCHSCREEN_TSC_SERIO=m ++CONFIG_TOUCHSCREEN_TSC2007=m ++CONFIG_TOUCHSCREEN_TOUCHIT213=m ++CONFIG_TOUCHSCREEN_TOUCHRIGHT=m ++CONFIG_TOUCHSCREEN_TOUCHWIN=m ++CONFIG_TOUCHSCREEN_PIXCIR=m ++CONFIG_TOUCHSCREEN_UCB1400=m ++CONFIG_TOUCHSCREEN_WACOM_W8001=m ++CONFIG_TOUCHSCREEN_WACOM_I2C=m ++CONFIG_TOUCHSCREEN_USB_E2I=y ++CONFIG_TOUCHSCREEN_USB_COMPOSITE=m ++# CONFIG_TOUCHSCREEN_WM97XX is not set ++CONFIG_TOUCHSCREEN_W90X900=m ++# CONFIG_TOUCHSCREEN_BU21013 is not set ++CONFIG_TOUCHSCREEN_ST1232=m ++CONFIG_TOUCHSCREEN_ATMEL_MXT=m ++# CONFIG_TOUCHSCREEN_MAX11801 is not set ++CONFIG_TOUCHSCREEN_AUO_PIXCIR=m ++CONFIG_TOUCHSCREEN_TI_AM335X_TSC=m ++CONFIG_TOUCHSCREEN_ZFORCE=m ++ ++CONFIG_INPUT_PCSPKR=m ++CONFIG_INPUT_RETU_PWRBUTTON=m ++CONFIG_INPUT_UINPUT=m ++CONFIG_INPUT_WISTRON_BTNS=m ++CONFIG_INPUT_ATLAS_BTNS=m ++ ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++ ++CONFIG_MAC_EMUMOUSEBTN=y ++ ++CONFIG_INPUT_WM831X_ON=m ++ ++ ++# CONFIG_INPUT_AD714X is not set ++# CONFIG_INPUT_PCF8574 is not set ++CONFIG_INPUT_MMA8450=m ++CONFIG_INPUT_MPU3050=m ++CONFIG_INPUT_KXTJ9=m ++# CONFIG_INPUT_KXTJ9_POLLED_MODE is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_SERIAL_NONSTANDARD=y ++CONFIG_ROCKETPORT=m ++CONFIG_SYNCLINK=m ++CONFIG_SYNCLINKMP=m ++CONFIG_SYNCLINK_GT=m ++CONFIG_N_HDLC=m ++CONFIG_N_GSM=m ++# CONFIG_TRACE_SINK is not set ++# CONFIG_STALDRV is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_IBM_ASM is not set ++CONFIG_TIFM_CORE=m ++CONFIG_TIFM_7XX1=m ++CONFIG_TCG_TPM=m ++CONFIG_TCG_TIS=m ++# CONFIG_TCG_TIS_I2C_INFINEON is not set ++# CONFIG_TCG_TIS_I2C_ATMEL is not set ++# CONFIG_TCG_TIS_I2C_NUVOTON is not set ++CONFIG_TCG_NSC=m ++CONFIG_TCG_ATMEL=m ++# CONFIG_TCG_INFINEON is not set ++# CONFIG_TCG_ST33_I2C is not set ++# CONFIG_TCG_XEN is not set ++CONFIG_TELCLOCK=m ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_CS=m ++CONFIG_SERIAL_8250_NR_UARTS=32 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 ++CONFIG_SERIAL_8250_EXTENDED=y ++CONFIG_SERIAL_8250_MANY_PORTS=y ++CONFIG_SERIAL_8250_SHARE_IRQ=y ++# CONFIG_SERIAL_8250_DETECT_IRQ is not set ++CONFIG_SERIAL_8250_RSA=y ++# CONFIG_SERIAL_8250_DW is not set ++CONFIG_CYCLADES=m ++# CONFIG_CYZ_INTR is not set ++# CONFIG_MOXA_INTELLIO is not set ++# CONFIG_MOXA_SMARTIO is not set ++# CONFIG_ISI is not set ++# CONFIG_RIO is not set ++CONFIG_SERIAL_JSM=m ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_MFD_HSU is not set ++ ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_TIMBERDALE is not set ++CONFIG_SERIAL_ARC=m ++CONFIG_SERIAL_ARC_NR_PORTS=1 ++# CONFIG_SERIAL_RP2 is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_SERIAL_PCH_UART is not set ++ ++CONFIG_UNIX98_PTYS=y ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y ++CONFIG_PRINTER=m ++CONFIG_LP_CONSOLE=y ++CONFIG_PPDEV=m ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++# CONFIG_I2C_MUX is not set ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++# ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_DEBUG_ALGO is not set ++CONFIG_I2C_ALGOBIT=m ++ ++# ++# I2C Hardware Bus support ++# ++ ++# CONFIG_I2C_ALI1535 is not set ++# CONFIG_I2C_ALI1563 is not set ++# CONFIG_I2C_ALI15X3 is not set ++# CONFIG_I2C_AMD756 is not set ++# CONFIG_I2C_AMD756_S4882 is not set ++# CONFIG_I2C_AMD8111 is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++# CONFIG_I2C_I801 is not set ++# CONFIG_I2C_ISCH is not set ++# CONFIG_I2C_NFORCE2_S4985 is not set ++# CONFIG_I2C_INTEL_MID is not set ++# CONFIG_I2C_EG20T is not set ++# CONFIG_I2C_CBUS_GPIO is not set ++CONFIG_I2C_VIPERBOARD=m ++ ++CONFIG_EEPROM_AT24=m ++CONFIG_EEPROM_LEGACY=m ++CONFIG_EEPROM_93CX6=m ++CONFIG_EEPROM_MAX6875=m ++ ++CONFIG_I2C_NFORCE2=m ++# CONFIG_I2C_OCORES is not set ++CONFIG_I2C_PARPORT=m ++CONFIG_I2C_PARPORT_LIGHT=m ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++CONFIG_I2C_PASEMI=m ++CONFIG_I2C_PCA_PLATFORM=m ++# CONFIG_I2C_PIIX4 is not set ++# CONFIG_SCx200_ACB is not set ++# CONFIG_I2C_SIS5595 is not set ++# CONFIG_I2C_SIS630 is not set ++# CONFIG_I2C_SIS96X is not set ++CONFIG_I2C_SIMTEC=m ++CONFIG_I2C_STUB=m ++CONFIG_I2C_TINY_USB=m ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_VIA is not set ++# CONFIG_I2C_VIAPRO is not set ++# CONFIG_I2C_DESIGNWARE is not set ++# CONFIG_I2C_XILINX is not set ++ ++CONFIG_I2C_DIOLAN_U2C=m ++ ++# ++# I2C Hardware Sensors Chip support ++# ++CONFIG_SENSORS_ATK0110=m ++CONFIG_SENSORS_ABITUGURU=m ++CONFIG_SENSORS_ABITUGURU3=m ++CONFIG_SENSORS_AD7414=m ++CONFIG_SENSORS_AD7418=m ++CONFIG_SENSORS_ADM1021=m ++CONFIG_SENSORS_ADM1025=m ++CONFIG_SENSORS_ADM1026=m ++CONFIG_SENSORS_ADM1029=m ++CONFIG_SENSORS_ADM1031=m ++CONFIG_SENSORS_ADM9240=m ++CONFIG_SENSORS_ADT7310=m ++CONFIG_SENSORS_ADT7410=m ++CONFIG_SENSORS_ADS7828=m ++CONFIG_SENSORS_ADT7462=m ++CONFIG_SENSORS_ADT7470=m ++CONFIG_SENSORS_ADT7475=m ++CONFIG_SENSORS_APPLESMC=m ++CONFIG_SENSORS_ASB100=m ++CONFIG_SENSORS_ATXP1=m ++CONFIG_SENSORS_CORETEMP=m ++CONFIG_SENSORS_DME1737=m ++CONFIG_SENSORS_DS1621=m ++# CONFIG_DS1682 is not set ++CONFIG_SENSORS_F71805F=m ++CONFIG_SENSORS_F71882FG=m ++CONFIG_SENSORS_F75375S=m ++CONFIG_SENSORS_FSCHMD=m ++CONFIG_SENSORS_G760A=m ++CONFIG_SENSORS_G762=m ++CONFIG_SENSORS_GL518SM=m ++CONFIG_SENSORS_GL520SM=m ++CONFIG_SENSORS_HDAPS=m ++# CONFIG_SENSORS_HIH6130 is not set ++# CONFIG_SENSORS_HTU21 is not set ++# CONFIG_SENSORS_I5K_AMB is not set ++# FIXME: IBMAEM x86 only? ++CONFIG_SENSORS_IBMAEM=m ++CONFIG_SENSORS_IBMPEX=m ++# CONFIG_SENSORS_IIO_HWMON is not set ++CONFIG_SENSORS_IT87=m ++CONFIG_SENSORS_K8TEMP=m ++CONFIG_SENSORS_K10TEMP=m ++CONFIG_SENSORS_LIS3LV02D=m ++CONFIG_SENSORS_LIS3_SPI=m ++CONFIG_SENSORS_LIS3_I2C=m ++CONFIG_SENSORS_LM63=m ++CONFIG_SENSORS_LM75=m ++CONFIG_SENSORS_LM77=m ++CONFIG_SENSORS_LM78=m ++CONFIG_SENSORS_LM80=m ++CONFIG_SENSORS_LM83=m ++CONFIG_SENSORS_LM85=m ++CONFIG_SENSORS_LM87=m ++CONFIG_SENSORS_LM90=m ++CONFIG_SENSORS_LM92=m ++CONFIG_SENSORS_LM93=m ++CONFIG_SENSORS_LM95234=m ++CONFIG_SENSORS_LTC4245=m ++CONFIG_SENSORS_MAX1619=m ++CONFIG_SENSORS_MAX6650=m ++CONFIG_SENSORS_MAX6697=m ++CONFIG_SENSORS_MCP3021=m ++CONFIG_SENSORS_NCT6775=m ++CONFIG_SENSORS_NTC_THERMISTOR=m ++CONFIG_SENSORS_PC87360=m ++CONFIG_SENSORS_PC87427=m ++CONFIG_SENSORS_PCF8591=m ++CONFIG_SENSORS_SHT15=m ++CONFIG_SENSORS_SIS5595=m ++CONFIG_CHARGER_SMB347=m ++CONFIG_SENSORS_SMSC47M1=m ++CONFIG_SENSORS_SMSC47M192=m ++CONFIG_SENSORS_SMSC47B397=m ++CONFIG_SENSORS_THMC50=m ++CONFIG_SENSORS_TMP401=m ++CONFIG_APDS9802ALS=m ++CONFIG_ISL29020=m ++CONFIG_ISL29003=m ++CONFIG_SENSORS_BH1770=m ++CONFIG_SENSORS_APDS990X=m ++CONFIG_SENSORS_TSL2550=m ++CONFIG_SENSORS_VIA686A=m ++CONFIG_SENSORS_VIA_CPUTEMP=m ++CONFIG_SENSORS_VT1211=m ++CONFIG_SENSORS_VT8231=m ++CONFIG_SENSORS_W83627HF=m ++CONFIG_SENSORS_W83781D=m ++CONFIG_SENSORS_W83L785TS=m ++CONFIG_SENSORS_W83L786NG=m ++CONFIG_SENSORS_W83627EHF=m ++CONFIG_SENSORS_W83791D=m ++CONFIG_SENSORS_W83792D=m ++CONFIG_SENSORS_W83793=m ++CONFIG_SENSORS_LTC4215=m ++CONFIG_SENSORS_LM95241=m ++CONFIG_SENSORS_LM95245=m ++CONFIG_SENSORS_TMP421=m ++CONFIG_SENSORS_WM8350=m ++CONFIG_SENSORS_WM831X=m ++CONFIG_SENSORS_LM73=m ++CONFIG_SENSORS_AMC6821=m ++CONFIG_SENSORS_INA2XX=m ++CONFIG_SENSORS_INA209=m ++CONFIG_SENSORS_ADT7411=m ++CONFIG_SENSORS_ASC7621=m ++CONFIG_SENSORS_EMC1403=m ++CONFIG_SENSORS_TMP102=m ++CONFIG_SENSORS_LTC4261=m ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_JC42 is not set ++# CONFIG_SENSORS_SMM665 is not set ++# CONFIG_SENSORS_EMC2103 is not set ++# CONFIG_SENSORS_GPIO_FAN is not set ++CONFIG_SENSORS_W83795=m ++# CONFIG_SENSORS_W83795_FANCTRL is not set ++CONFIG_SENSORS_DS620=m ++CONFIG_SENSORS_SHT21=m ++CONFIG_SENSORS_LINEAGE=m ++CONFIG_SENSORS_LTC4151=m ++CONFIG_SENSORS_MAX6639=m ++CONFIG_SENSORS_SCH5627=m ++CONFIG_SENSORS_SCH5636=m ++CONFIG_SENSORS_ADS1015=m ++CONFIG_SENSORS_MAX16065=m ++CONFIG_SENSORS_MAX6642=m ++CONFIG_SENSORS_ADM1275=m ++CONFIG_SENSORS_UCD9000=m ++CONFIG_SENSORS_UCD9200=m ++CONFIG_SENSORS_ZL6100=m ++CONFIG_SENSORS_EMC6W201=m ++ ++CONFIG_PMBUS=m ++CONFIG_SENSORS_PMBUS=m ++CONFIG_SENSORS_MAX16064=m ++CONFIG_SENSORS_LM25066=m ++CONFIG_SENSORS_LTC2978=m ++CONFIG_SENSORS_MAX34440=m ++CONFIG_SENSORS_MAX8688=m ++CONFIG_SENSORS_MAX1668=m ++CONFIG_SENSORS_MAX197=m ++ ++# Industrial I/O subsystem configuration ++CONFIG_IIO=m ++CONFIG_IIO_BUFFER=y ++CONFIG_IIO_BUFFER_CB=y ++# CONFIG_IIO_KFIFO_BUF is not set ++CONFIG_IIO_TRIGGERED_BUFFER=m ++CONFIG_IIO_TRIGGER=y ++CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 ++CONFIG_IIO_INTERRUPT_TRIGGER=m ++CONFIG_HID_SENSOR_IIO_COMMON=m ++CONFIG_HID_SENSOR_IIO_TRIGGER=m ++CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS=y ++# CONFIG_IIO_SYSFS_TRIGGER is not set ++# CONFIG_AD5446 is not set ++# CONFIG_AD5380 is not set ++# CONFIG_AD5064 is not set ++# CONFIG_BMA180 is not set ++# CONFIG_MAX1363 is not set ++# CONFIG_MAX517 is not set ++# CONFIG_MCP4725 is not set ++# CONFIG_ITG3200 is not set ++# CONFIG_APDS9300 is not set ++# CONFIG_CM32181 is not set ++# CONFIG_CM36651 is not set ++# CONFIG_GP2AP020A00F is not set ++# CONFIG_TSL2583 is not set ++# CONFIG_TSL2x7x is not set ++# CONFIG_TCS3472 is not set ++# CONFIG_TSL4531 is not set ++# CONFIG_NAU7802 is not set ++# CONFIG_TI_ADC081C is not set ++# CONFIG_EXYNOS_ADC is not set ++# CONFIG_VIPERBOARD_ADC is not set ++# CONFIG_INV_MPU6050_IIO is not set ++CONFIG_IIO_ST_GYRO_3AXIS=m ++CONFIG_IIO_ST_MAGN_3AXIS=m ++CONFIG_IIO_ST_ACCEL_3AXIS=m ++CONFIG_HID_SENSOR_INCLINOMETER_3D=m ++# CONFIG_ADJD_S311 is not set ++# CONFIG_SENSORS_TSL2563 is not set ++# CONFIG_VCNL4000 is not set ++# CONFIG_AK8975 is not set ++# CONFIG_MAG3110 is not set ++# CONFIG_TMP006 is not set ++# CONFIG_IIO_ST_PRESS is not set ++# CONFIG_KXSD9 is not set ++# CONFIG_AD7266 is not set ++# CONFIG_AD7298 is not set ++# CONFIG_AD7476 is not set ++# CONFIG_AD7791 is not set ++# CONFIG_AD7793 is not set ++# CONFIG_AD7887 is not set ++# CONFIG_AD7923 is not set ++# CONFIG_MCP320X is not set ++# CONFIG_MCP3422 is not set ++# CONFIG_AD8366 is not set ++# CONFIG_AD5360 is not set ++# CONFIG_AD5421 is not set ++# CONFIG_AD5449 is not set ++# CONFIG_AD5504 is not set ++# CONFIG_AD5624R_SPI is not set ++# CONFIG_AD5686 is not set ++# CONFIG_AD5755 is not set ++# CONFIG_AD5764 is not set ++# CONFIG_AD5791 is not set ++# CONFIG_AD7303 is not set ++# CONFIG_AD9523 is not set ++# CONFIG_ADF4350 is not set ++# CONFIG_ADIS16080 is not set ++# CONFIG_ADIS16130 is not set ++# CONFIG_ADIS16136 is not set ++# CONFIG_ADIS16260 is not set ++# CONFIG_ADXRS450 is not set ++# CONFIG_ADIS16400 is not set ++# CONFIG_ADIS16480 is not set ++# CONFIG_DHT11 is not set ++# CONFIG_MPL3115 is not set ++ ++# staging IIO drivers ++# CONFIG_AD7291 is not set ++# CONFIG_AD7606 is not set ++# CONFIG_AD799X is not set ++# CONFIG_ADT7316 is not set ++# CONFIG_AD7150 is not set ++# CONFIG_AD7152 is not set ++# CONFIG_AD7746 is not set ++# CONFIG_AD5933 is not set ++# CONFIG_ADE7854 is not set ++# CONFIG_SENSORS_ISL29018 is not set ++# CONFIG_SENSORS_ISL29028 is not set ++# CONFIG_SENSORS_HMC5843 is not set ++# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set ++# CONFIG_IIO_SIMPLE_DUMMY is not set ++# CONFIG_ADIS16201 is not set ++# CONFIG_ADIS16203 is not set ++# CONFIG_ADIS16204 is not set ++# CONFIG_ADIS16209 is not set ++# CONFIG_ADIS16220 is not set ++# CONFIG_ADIS16240 is not set ++# CONFIG_LIS3L02DQ is not set ++# CONFIG_SCA3000 is not set ++# CONFIG_AD7780 is not set ++# CONFIG_AD7816 is not set ++# CONFIG_AD7192 is not set ++# CONFIG_AD7280 is not set ++# CONFIG_AD5930 is not set ++# CONFIG_AD9832 is not set ++# CONFIG_AD9834 is not set ++# CONFIG_AD9850 is not set ++# CONFIG_AD9852 is not set ++# CONFIG_AD9910 is not set ++# CONFIG_AD9951 is not set ++# CONFIG_ADIS16060 is not set ++# CONFIG_ADE7753 is not set ++# CONFIG_ADE7754 is not set ++# CONFIG_ADE7758 is not set ++# CONFIG_ADE7759 is not set ++# CONFIG_AD2S90 is not set ++# CONFIG_AD2S1200 is not set ++# CONFIG_AD2S1210 is not set ++ ++ ++ ++# CONFIG_HMC6352 is not set ++# CONFIG_BMP085 is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_PCH_PHUB is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++ ++CONFIG_W1=m ++CONFIG_W1_CON=y ++# CONFIG_W1_MASTER_MATROX is not set ++CONFIG_W1_MASTER_DS2490=m ++CONFIG_W1_MASTER_DS2482=m ++CONFIG_W1_MASTER_DS1WM=m ++CONFIG_W1_MASTER_GPIO=m ++# CONFIG_HDQ_MASTER_OMAP is not set ++CONFIG_W1_SLAVE_THERM=m ++CONFIG_W1_SLAVE_SMEM=m ++CONFIG_W1_SLAVE_DS2408=m ++# CONFIG_W1_SLAVE_DS2408_READBACK is not set ++CONFIG_W1_SLAVE_DS2413=m ++CONFIG_W1_SLAVE_DS2423=m ++CONFIG_W1_SLAVE_DS2431=m ++CONFIG_W1_SLAVE_DS2433=m ++CONFIG_W1_SLAVE_DS2433_CRC=y ++CONFIG_W1_SLAVE_DS2760=m ++CONFIG_W1_SLAVE_DS2780=m ++CONFIG_W1_SLAVE_DS2781=m ++CONFIG_W1_SLAVE_DS28E04=m ++CONFIG_W1_SLAVE_BQ27000=m ++ ++# ++# Mice ++# ++ ++# ++# IPMI ++# ++CONFIG_IPMI_HANDLER=m ++# CONFIG_IPMI_PANIC_EVENT is not set ++CONFIG_IPMI_DEVICE_INTERFACE=m ++CONFIG_IPMI_WATCHDOG=m ++CONFIG_IPMI_SI=m ++CONFIG_IPMI_POWEROFF=m ++ ++# ++# Watchdog Cards ++# ++CONFIG_WATCHDOG_CORE=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++CONFIG_SOFT_WATCHDOG=m ++CONFIG_WDTPCI=m ++# CONFIG_ACQUIRE_WDT is not set ++# CONFIG_ADVANTECH_WDT is not set ++# CONFIG_EUROTECH_WDT is not set ++CONFIG_IB700_WDT=m ++# CONFIG_SCx200_WDT is not set ++# CONFIG_60XX_WDT is not set ++CONFIG_W83877F_WDT=m ++CONFIG_W83627HF_WDT=m ++CONFIG_MACHZ_WDT=m ++# CONFIG_SC520_WDT is not set ++CONFIG_ALIM7101_WDT=m ++CONFIG_ALIM1535_WDT=m ++CONFIG_IT87_WDT=m ++CONFIG_ITCO_WDT=m ++CONFIG_ITCO_VENDOR_SUPPORT=y ++# CONFIG_SC1200_WDT is not set ++# CONFIG_PC87413_WDT is not set ++# CONFIG_WAFER_WDT is not set ++# CONFIG_CPU5_WDT is not set ++CONFIG_I6300ESB_WDT=m ++CONFIG_IT8712F_WDT=m ++# CONFIG_SBC8360_WDT is not set ++# CONFIG_SBC7240_WDT is not set ++CONFIG_SMSC_SCH311X_WDT=m ++CONFIG_W83977F_WDT=m ++CONFIG_PCIPCWATCHDOG=m ++CONFIG_USBPCWATCHDOG=m ++# CONFIG_SBC_EPX_C3_WATCHDOG is not set ++CONFIG_WM8350_WATCHDOG=m ++CONFIG_WM831X_WATCHDOG=m ++# CONFIG_MAX63XX_WATCHDOG is not set ++# CONFIG_DW_WATCHDOG is not set ++CONFIG_W83697UG_WDT=m ++# CONFIG_MEN_A21_WDT is not set ++# CONFIG_GPIO_WATCHDOG is not set ++ ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_TIMERIOMEM=m ++CONFIG_HW_RANDOM_TPM=m ++# CONFIG_HW_RANDOM_ATMEL is not set ++# CONFIG_HW_RANDOM_EXYNOS is not set ++# CONFIG_NVRAM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_DEBUG is not set ++# CONFIG_GEN_RTC is not set ++CONFIG_RTC_HCTOSYS=y ++# CONFIG_RTC_SYSTOHC is not set ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++CONFIG_RTC_DRV_CMOS=y ++CONFIG_RTC_DRV_DS1307=m ++CONFIG_RTC_DRV_DS1511=m ++CONFIG_RTC_DRV_DS1553=m ++CONFIG_RTC_DRV_DS1672=m ++CONFIG_RTC_DRV_DS1742=m ++CONFIG_RTC_DRV_DS1374=m ++# CONFIG_RTC_DRV_EP93XX is not set ++CONFIG_RTC_DRV_FM3130=m ++CONFIG_RTC_DRV_ISL1208=m ++CONFIG_RTC_DRV_M41T80=m ++CONFIG_RTC_DRV_M41T80_WDT=y ++CONFIG_RTC_DRV_M48T59=m ++CONFIG_RTC_DRV_MAX6900=m ++# CONFIG_RTC_DRV_M48T86 is not set ++CONFIG_RTC_DRV_PCF2127=m ++CONFIG_RTC_DRV_PCF8563=m ++CONFIG_RTC_DRV_PCF8583=m ++CONFIG_RTC_DRV_RS5C372=m ++# CONFIG_RTC_DRV_SA1100 is not set ++# CONFIG_RTC_DRV_TEST is not set ++CONFIG_RTC_DRV_X1205=m ++CONFIG_RTC_DRV_V3020=m ++CONFIG_RTC_DRV_DS2404=m ++CONFIG_RTC_DRV_STK17TA8=m ++# CONFIG_RTC_DRV_S35390A is not set ++CONFIG_RTC_DRV_RX8581=m ++CONFIG_RTC_DRV_RX8025=m ++CONFIG_RTC_DRV_DS1286=m ++CONFIG_RTC_DRV_M48T35=m ++CONFIG_RTC_DRV_BQ4802=m ++CONFIG_RTC_DRV_WM8350=m ++# CONFIG_RTC_DRV_AB3100 is not set ++CONFIG_RTC_DRV_WM831X=m ++CONFIG_RTC_DRV_BQ32K=m ++CONFIG_RTC_DRV_MSM6242=m ++CONFIG_RTC_DRV_RP5C01=m ++CONFIG_RTC_DRV_EM3027=m ++CONFIG_RTC_DRV_RV3029C2=m ++CONFIG_RTC_DRV_PCF50633=m ++CONFIG_RTC_DRV_DS3232=m ++CONFIG_RTC_DRV_ISL12022=m ++# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set ++# CONFIG_RTC_DRV_MOXART is not set ++# CONFIG_RTC_DRV_ISL12057 is not set ++ ++CONFIG_R3964=m ++# CONFIG_APPLICOM is not set ++# CONFIG_SONYPI is not set ++ ++# ++# Ftape, the floppy tape device driver ++# ++CONFIG_AGP=y ++CONFIG_AGP_ALI=y ++CONFIG_AGP_ATI=y ++CONFIG_AGP_AMD=y ++CONFIG_AGP_AMD64=y ++CONFIG_AGP_INTEL=y ++CONFIG_AGP_NVIDIA=y ++CONFIG_AGP_SIS=y ++CONFIG_AGP_SWORKS=y ++CONFIG_AGP_VIA=y ++CONFIG_AGP_EFFICEON=y ++ ++CONFIG_VGA_ARB=y ++CONFIG_VGA_ARB_MAX_GPUS=16 ++ ++# CONFIG_STUB_POULSBO is not set ++ ++# ++# PCMCIA character devices ++# ++# CONFIG_SYNCLINK_CS is not set ++ ++CONFIG_CARDMAN_4000=m ++CONFIG_CARDMAN_4040=m ++ ++CONFIG_MWAVE=m ++CONFIG_RAW_DRIVER=y ++CONFIG_MAX_RAW_DEVS=8192 ++CONFIG_HANGCHECK_TIMER=m ++ ++CONFIG_MEDIA_PCI_SUPPORT=y ++# ++# Multimedia devices ++# ++CONFIG_MEDIA_ANALOG_TV_SUPPORT=y ++CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y ++CONFIG_MEDIA_RC_SUPPORT=y ++CONFIG_MEDIA_CONTROLLER=y ++CONFIG_VIDEO_DEV=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++CONFIG_VIDEO_HELPER_CHIPS_AUTO=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEO_V4L2_SUBDEV_API=y ++# CONFIG_VIDEO_VIVI is not set ++# CONFIG_USB_SI4713 is not set ++# CONFIG_PLATFORM_SI4713 is not set ++# CONFIG_I2C_SI4713 is not set ++# CONFIG_USB_RAREMONO is not set ++ ++# ++# Video For Linux ++# ++ ++# ++# Video Adapters ++# ++CONFIG_V4L_USB_DRIVERS=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++CONFIG_V4L_PCI_DRIVERS=y ++CONFIG_VIDEO_AU0828=m ++CONFIG_VIDEO_AU0828_V4L2=y ++CONFIG_VIDEO_BT848=m ++CONFIG_VIDEO_BT848_DVB=y ++CONFIG_VIDEO_BWQCAM=m ++CONFIG_VIDEO_SR030PC30=m ++CONFIG_VIDEO_NOON010PC30=m ++CONFIG_VIDEO_CAFE_CCIC=m ++# CONFIG_VIDEO_CPIA is not set ++CONFIG_VIDEO_CPIA2=m ++CONFIG_VIDEO_CQCAM=m ++CONFIG_VIDEO_CX23885=m ++CONFIG_MEDIA_ALTERA_CI=m ++CONFIG_VIDEO_CX18=m ++CONFIG_VIDEO_CX18_ALSA=m ++CONFIG_VIDEO_CX88=m ++CONFIG_VIDEO_CX88_DVB=m ++CONFIG_VIDEO_CX88_ALSA=m ++CONFIG_VIDEO_CX88_BLACKBIRD=m ++CONFIG_VIDEO_CX88_ENABLE_VP3054=y ++CONFIG_VIDEO_CX88_VP3054=m ++CONFIG_VIDEO_EM28XX=m ++CONFIG_VIDEO_EM28XX_V4L2=m ++CONFIG_VIDEO_EM28XX_ALSA=m ++CONFIG_VIDEO_EM28XX_DVB=m ++CONFIG_VIDEO_EM28XX_RC=y ++CONFIG_VIDEO_CX231XX=m ++CONFIG_VIDEO_CX231XX_ALSA=m ++CONFIG_VIDEO_CX231XX_DVB=m ++CONFIG_VIDEO_CX231XX_RC=y ++CONFIG_VIDEO_HEXIUM_ORION=m ++CONFIG_VIDEO_HEXIUM_GEMINI=m ++CONFIG_VIDEO_IVTV=m ++# CONFIG_VIDEO_IVTV_ALSA is not set ++CONFIG_VIDEO_MEYE=m ++CONFIG_VIDEO_MXB=m ++CONFIG_VIDEO_PVRUSB2_DVB=y ++# CONFIG_VIDEO_PMS is not set ++CONFIG_VIDEO_HDPVR=m ++CONFIG_VIDEO_SAA6588=m ++CONFIG_VIDEO_SAA7134=m ++CONFIG_VIDEO_SAA7134_ALSA=m ++CONFIG_VIDEO_SAA7134_DVB=m ++CONFIG_VIDEO_SAA7134_RC=y ++CONFIG_VIDEO_USBVISION=m ++CONFIG_VIDEO_STK1160_COMMON=m ++CONFIG_VIDEO_STK1160=m ++CONFIG_VIDEO_STK1160_AC97=y ++CONFIG_VIDEO_W9966=m ++CONFIG_VIDEO_ZORAN=m ++CONFIG_VIDEO_ZORAN_AVS6EYES=m ++CONFIG_VIDEO_ZORAN_BUZ=m ++CONFIG_VIDEO_ZORAN_DC10=m ++CONFIG_VIDEO_ZORAN_DC30=m ++CONFIG_VIDEO_ZORAN_LML33=m ++CONFIG_VIDEO_ZORAN_LML33R10=m ++CONFIG_VIDEO_ZORAN_ZR36060=m ++# CONFIG_V4L_ISA_PARPORT_DRIVERS is not set ++CONFIG_VIDEO_FB_IVTV=m ++CONFIG_VIDEO_SAA7164=m ++CONFIG_VIDEO_TM6000=m ++CONFIG_VIDEO_TM6000_ALSA=m ++CONFIG_VIDEO_TM6000_DVB=m ++CONFIG_VIDEO_TLG2300=m ++CONFIG_VIDEO_USBTV=m ++ ++CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y ++ ++# ++# Radio Adapters ++# ++CONFIG_RADIO_MAXIRADIO=m ++CONFIG_RADIO_SHARK=m ++CONFIG_RADIO_SHARK2=m ++CONFIG_RADIO_WL1273=m ++ ++CONFIG_MEDIA_ATTACH=y ++ ++# ++# V4L/DVB tuners ++# Selected automatically by not setting CONFIG_MEDIA_TUNER_CUSTOMISE ++# ++# CONFIG_MEDIA_TUNER_CUSTOMISE is not set ++ ++# ++# Digital Video Broadcasting Devices ++# ++CONFIG_DVB_CAPTURE_DRIVERS=y ++CONFIG_DVB_CORE=m ++CONFIG_DVB_NET=y ++CONFIG_DVB_MAX_ADAPTERS=8 ++CONFIG_DVB_DYNAMIC_MINORS=y ++ ++# ++# DVB frontends ++# Selected automatically by not setting CONFIG_DVB_FE_CUSTOMISE ++# ++# CONFIG_DVB_FE_CUSTOMISE is not set ++ ++# ++# Supported DVB bridge Modules ++# ++CONFIG_DVB_BT8XX=m ++CONFIG_DVB_BUDGET_CORE=m ++CONFIG_DVB_PLUTO2=m ++CONFIG_SMS_SIANO_MDTV=m ++CONFIG_SMS_SIANO_RC=y ++# CONFIG_SMS_SIANO_DEBUGFS is not set ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++CONFIG_SMS_USB_DRV=m ++CONFIG_SMS_SDIO_DRV=m ++CONFIG_DVB_TTUSB_DEC=m ++CONFIG_DVB_USB_DTV5100=m ++CONFIG_DVB_USB_AF9015=m ++CONFIG_DVB_USB_ANYSEE=m ++CONFIG_DVB_USB_DW2102=m ++CONFIG_DVB_USB_FRIIO=m ++CONFIG_DVB_USB_EC168=m ++CONFIG_DVB_USB_PCTV452E=m ++CONFIG_DVB_USB_IT913X=m ++CONFIG_DVB_USB_MXL111SF=m ++CONFIG_DVB_DM1105=m ++CONFIG_DVB_FIREDTV=m ++CONFIG_DVB_NGENE=m ++CONFIG_DVB_DDBRIDGE=m ++CONFIG_DVB_USB_TECHNISAT_USB2=m ++CONFIG_DVB_USB_V2=m ++ ++CONFIG_DVB_AV7110=m ++CONFIG_DVB_AV7110_OSD=y ++CONFIG_DVB_BUDGET=m ++CONFIG_DVB_BUDGET_CI=m ++CONFIG_DVB_BUDGET_AV=m ++CONFIG_DVB_BUDGET_PATCH=m ++ ++CONFIG_DVB_TTUSB_BUDGET=m ++ ++CONFIG_DVB_USB_CINERGY_T2=m ++CONFIG_DVB_B2C2_FLEXCOP=m ++# CONFIG_DVB_B2C2_FLEXCOP_USB_DEBUG is not set ++ ++CONFIG_DVB_B2C2_FLEXCOP_PCI=m ++# CONFIG_DVB_B2C2_FLEXCOP_PCI_DEBUG is not set ++CONFIG_DVB_B2C2_FLEXCOP_USB=m ++# CONFIG_DVB_B2C2_FLEXCOP_DEBUG is not set ++CONFIG_DVB_USB=m ++# CONFIG_DVB_USB_DEBUG is not set ++CONFIG_DVB_USB_A800=m ++CONFIG_DVB_USB_AF9005=m ++CONFIG_DVB_USB_AF9005_REMOTE=m ++CONFIG_DVB_USB_AU6610=m ++CONFIG_DVB_USB_CXUSB=m ++CONFIG_DVB_USB_DIBUSB_MB=m ++# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set ++CONFIG_DVB_USB_DIBUSB_MC=m ++CONFIG_DVB_USB_DIB0700=m ++CONFIG_DVB_USB_DIGITV=m ++CONFIG_DVB_USB_DTT200U=m ++CONFIG_DVB_USB_GL861=m ++CONFIG_DVB_USB_GP8PSK=m ++CONFIG_DVB_USB_M920X=m ++CONFIG_DVB_USB_NOVA_T_USB2=m ++CONFIG_DVB_USB_CE6230=m ++CONFIG_DVB_USB_OPERA1=m ++CONFIG_DVB_USB_TTUSB2=m ++CONFIG_DVB_USB_UMT_010=m ++CONFIG_DVB_USB_VP702X=m ++CONFIG_DVB_USB_VP7045=m ++CONFIG_DVB_USB_AZ6027=m ++CONFIG_DVB_USB_AZ6007=m ++CONFIG_DVB_USB_LME2510=m ++CONFIG_DVB_USB_RTL28XXU=m ++CONFIG_DVB_USB_AF9035=m ++ ++CONFIG_DVB_PT1=m ++ ++CONFIG_MANTIS_CORE=m ++CONFIG_DVB_MANTIS=m ++CONFIG_DVB_HOPPER=m ++ ++CONFIG_VIDEO_SAA7146=m ++CONFIG_VIDEO_SAA7146_VV=m ++CONFIG_VIDEO_TVP5150=m ++CONFIG_VIDEO_TUNER=m ++CONFIG_VIDEO_BTCX=m ++CONFIG_VIDEO_PVRUSB2=m ++CONFIG_VIDEO_PVRUSB2_SYSFS=y ++# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set ++ ++CONFIG_RC_CORE=m ++CONFIG_RC_DECODERS=y ++CONFIG_LIRC=m ++CONFIG_RC_LOOPBACK=m ++CONFIG_RC_MAP=m ++CONFIG_RC_DEVICES=y ++CONFIG_RC_ATI_REMOTE=m ++CONFIG_IR_NEC_DECODER=m ++CONFIG_IR_RC5_DECODER=m ++CONFIG_IR_RC6_DECODER=m ++CONFIG_IR_JVC_DECODER=m ++CONFIG_IR_SONY_DECODER=m ++CONFIG_IR_RC5_SZ_DECODER=m ++CONFIG_IR_SANYO_DECODER=m ++CONFIG_IR_MCE_KBD_DECODER=m ++CONFIG_IR_LIRC_CODEC=m ++CONFIG_IR_IMON=m ++CONFIG_IR_MCEUSB=m ++CONFIG_IR_ITE_CIR=m ++CONFIG_IR_NUVOTON=m ++CONFIG_IR_FINTEK=m ++CONFIG_IR_REDRAT3=m ++CONFIG_IR_ENE=m ++CONFIG_IR_STREAMZAP=m ++CONFIG_IR_WINBOND_CIR=m ++CONFIG_IR_IGUANA=m ++CONFIG_IR_TTUSBIR=m ++CONFIG_IR_GPIO_CIR=m ++ ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set ++# CONFIG_VIDEO_SH_VEU is not set ++# CONFIG_VIDEO_RENESAS_VSP1 is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# CONFIG_VIDEO_MEM2MEM_TESTDEV is not set ++ ++# ++# Broadcom Crystal HD video decoder driver ++# ++CONFIG_CRYSTALHD=m ++ ++# ++# Graphics support ++# ++ ++CONFIG_DISPLAY_SUPPORT=m ++CONFIG_VIDEO_OUTPUT_CONTROL=m ++ ++# ++# Console display driver support ++# ++CONFIG_VGA_CONSOLE=y ++CONFIG_VGACON_SOFT_SCROLLBACK=y ++CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y ++ ++# ++# Logo configuration ++# ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++ ++# ++# Advanced Linux Sound Architecture ++# ++CONFIG_SOUND_OSS_CORE_PRECLAIM=y ++# CONFIG_SND_DEBUG_VERBOSE is not set ++CONFIG_SND_VERBOSE_PROCFS=y ++CONFIG_SND_SEQUENCER=y ++CONFIG_SND_HRTIMER=y ++CONFIG_SND_SEQ_HRTIMER_DEFAULT=y ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_SEQ_RTCTIMER_DEFAULT=y ++CONFIG_SND_OSSEMUL=y ++CONFIG_SND_MIXER_OSS=y ++CONFIG_SND_PCM_OSS=y ++CONFIG_SND_PCM_OSS_PLUGINS=y ++CONFIG_SND_RTCTIMER=y ++CONFIG_SND_DYNAMIC_MINORS=y ++CONFIG_SND_MAX_CARDS=32 ++# CONFIG_SND_SUPPORT_OLD_API is not set ++ ++# ++# Generic devices ++# ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_MTS64=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_PORTMAN2X4=m ++CONFIG_SND_AC97_POWER_SAVE=y ++CONFIG_SND_AC97_POWER_SAVE_DEFAULT=0 ++ ++CONFIG_SND_DRIVERS=y ++ ++# ++# ISA devices ++# ++CONFIG_SND_AD1889=m ++ ++# ++# PCI devices ++# ++CONFIG_SND_PCI=y ++CONFIG_SND_ALI5451=m ++CONFIG_SND_ALS300=m ++CONFIG_SND_ALS4000=m ++CONFIG_SND_ATIIXP=m ++CONFIG_SND_ATIIXP_MODEM=m ++CONFIG_SND_AU8810=m ++CONFIG_SND_AU8820=m ++CONFIG_SND_AU8830=m ++# CONFIG_SND_AW2 is not set ++CONFIG_SND_AZT3328=m ++CONFIG_SND_BT87X=m ++# CONFIG_SND_BT87X_OVERCLOCK is not set ++CONFIG_SND_CA0106=m ++CONFIG_SND_CMIPCI=m ++CONFIG_SND_CS46XX=m ++CONFIG_SND_CS46XX_NEW_DSP=y ++CONFIG_SND_CS4281=m ++CONFIG_SND_CS5530=m ++CONFIG_SND_CS5535AUDIO=m ++CONFIG_SND_EMU10K1=m ++CONFIG_SND_EMU10K1X=m ++CONFIG_SND_ENS1370=m ++CONFIG_SND_ENS1371=m ++CONFIG_SND_ES1938=m ++CONFIG_SND_ES1968=m ++CONFIG_SND_ES1968_INPUT=y ++CONFIG_SND_ES1968_RADIO=y ++CONFIG_SND_FM801=m ++CONFIG_SND_FM801_TEA575X_BOOL=y ++CONFIG_SND_CTXFI=m ++CONFIG_SND_LX6464ES=m ++CONFIG_SND_HDA_INTEL=y ++CONFIG_SND_HDA_INPUT_BEEP=y ++CONFIG_SND_HDA_INPUT_BEEP_MODE=0 ++CONFIG_SND_HDA_INPUT_JACK=y ++CONFIG_SND_HDA_PATCH_LOADER=y ++CONFIG_SND_HDA_HWDEP=y ++CONFIG_SND_HDA_CODEC_REALTEK=y ++CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS=y ++CONFIG_SND_HDA_CODEC_CA0110=y ++CONFIG_SND_HDA_CODEC_ANALOG=y ++CONFIG_SND_HDA_CODEC_SIGMATEL=y ++CONFIG_SND_HDA_CODEC_VIA=y ++CONFIG_SND_HDA_CODEC_CIRRUS=y ++CONFIG_SND_HDA_CODEC_CONEXANT=y ++CONFIG_SND_HDA_CODEC_CMEDIA=y ++CONFIG_SND_HDA_CODEC_SI3054=y ++CONFIG_SND_HDA_CODEC_HDMI=y ++CONFIG_SND_HDA_I915=y ++CONFIG_SND_HDA_CODEC_CA0132=y ++CONFIG_SND_HDA_CODEC_CA0132_DSP=y ++CONFIG_SND_HDA_GENERIC=y ++CONFIG_SND_HDA_POWER_SAVE=y ++CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 ++CONFIG_SND_HDA_RECONFIG=y ++CONFIG_SND_HDA_PREALLOC_SIZE=4096 ++CONFIG_SND_HDSPM=m ++CONFIG_SND_ICE1712=m ++CONFIG_SND_ICE1724=m ++CONFIG_SND_INTEL8X0=y ++CONFIG_SND_INTEL8X0M=m ++CONFIG_SND_KORG1212=m ++CONFIG_SND_MAESTRO3=m ++CONFIG_SND_MAESTRO3_INPUT=y ++CONFIG_SND_MIXART=m ++CONFIG_SND_NM256=m ++CONFIG_SND_OXYGEN=m ++CONFIG_SND_RME32=m ++CONFIG_SND_PCSP=m ++CONFIG_SND_PCXHR=m ++CONFIG_SND_RIPTIDE=m ++CONFIG_SND_RME96=m ++CONFIG_SND_RME9652=m ++CONFIG_SND_SIS7019=m ++CONFIG_SND_SONICVIBES=m ++CONFIG_SND_HDSP=m ++CONFIG_SND_TRIDENT=m ++CONFIG_SND_VIA82XX=m ++CONFIG_SND_VIA82XX_MODEM=m ++CONFIG_SND_VIRTUOSO=m ++CONFIG_SND_VX222=m ++CONFIG_SND_YMFPCI=m ++CONFIG_SND_ASIHPI=m ++CONFIG_SND_LOLA=m ++ ++# ++# ALSA USB devices ++# ++CONFIG_SND_USB=y ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_CAIAQ_INPUT=y ++CONFIG_SND_USB_USX2Y=m ++CONFIG_SND_USB_US122L=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SND_USB_HIFACE=m ++ ++# ++# PCMCIA devices ++# ++# CONFIG_SND_PCMCIA is not set ++ ++CONFIG_SND_FIREWIRE=y ++CONFIG_SND_FIREWIRE_SPEAKERS=m ++CONFIG_SND_ISIGHT=m ++CONFIG_SND_SCS1X=m ++CONFIG_SND_DICE=m ++ ++# ++# Open Sound System ++# ++# CONFIG_SOUND_PRIME is not set ++ ++# ++# USB support ++# ++CONFIG_USB_SUPPORT=y ++# CONFIG_USB_DEBUG is not set ++ ++# DEPRECATED: See bug 362221. Fix udev. ++# CONFIG_USB_DEVICE_CLASS is not set ++ ++ ++# ++# Miscellaneous USB options ++# ++ ++# Deprecated. ++# CONFIG_USB_DEVICEFS is not set ++ ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++CONFIG_USB_SUSPEND=y ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_EHCI_ROOT_HUB_TT=y ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++# CONFIG_USB_EHCI_MV is not set ++# CONFIG_USB_EHCI_HCD_PLATFORM is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++CONFIG_USB_ISP1362_HCD=m ++CONFIG_USB_FUSBH200_HCD=m ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_GR_UDC is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PCI=y ++# CONFIG_USB_OHCI_HCD_SSB is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++# CONFIG_USB_OHCI_HCD_PLATFORM is not set ++CONFIG_USB_UHCI_HCD=y ++CONFIG_USB_SL811_HCD=m ++CONFIG_USB_SL811_HCD_ISO=y ++# CONFIG_USB_SL811_CS is not set ++# CONFIG_USB_R8A66597_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_HCD_DEBUGGING is not set ++ ++# ++# USB Device Class drivers ++# ++ ++# ++# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem ++# ++CONFIG_USB_ACM=m ++CONFIG_USB_PRINTER=m ++CONFIG_USB_WDM=m ++CONFIG_USB_TMC=m ++# CONFIG_BLK_DEV_UB is not set ++# CONFIG_USB_STORAGE_DEBUG is not set ++CONFIG_USB_STORAGE_CYPRESS_ATACB=m ++CONFIG_USB_STORAGE_DATAFAB=m ++CONFIG_USB_STORAGE_FREECOM=m ++CONFIG_USB_STORAGE_ISD200=m ++CONFIG_USB_STORAGE_SDDR09=m ++CONFIG_USB_STORAGE_SDDR55=m ++CONFIG_USB_STORAGE_JUMPSHOT=m ++CONFIG_USB_STORAGE_USBAT=y ++CONFIG_USB_STORAGE_ONETOUCH=m ++CONFIG_USB_STORAGE_ALAUDA=m ++CONFIG_USB_STORAGE_KARMA=m ++CONFIG_USB_STORAGE_REALTEK=m ++CONFIG_REALTEK_AUTOPM=y ++CONFIG_USB_STORAGE_ENE_UB6250=m ++# CONFIG_USB_LIBUSUAL is not set ++# CONFIG_USB_UAS is not set ++ ++ ++# ++# USB Human Interface Devices (HID) ++# ++CONFIG_USB_HID=y ++ ++CONFIG_HID_SUPPORT=y ++ ++CONFIG_HID=y ++CONFIG_I2C_HID=m ++CONFIG_HID_BATTERY_STRENGTH=y ++# debugging default is y upstream now ++CONFIG_HIDRAW=y ++CONFIG_UHID=m ++CONFIG_HID_PID=y ++CONFIG_LOGITECH_FF=y ++CONFIG_HID_LOGITECH_DJ=m ++CONFIG_LOGIWII_FF=y ++CONFIG_LOGIRUMBLEPAD2_FF=y ++CONFIG_PANTHERLORD_FF=y ++CONFIG_THRUSTMASTER_FF=y ++CONFIG_HID_WACOM=m ++CONFIG_HID_WACOM_POWER_SUPPLY=y ++CONFIG_ZEROPLUS_FF=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_IDMOUSE=m ++CONFIG_DRAGONRISE_FF=y ++CONFIG_GREENASIA_FF=y ++CONFIG_SMARTJOYPLUS_FF=y ++CONFIG_LOGIG940_FF=y ++CONFIG_LOGIWHEELS_FF=y ++CONFIG_HID_MAGICMOUSE=y ++CONFIG_HID_MULTITOUCH=m ++CONFIG_HID_NTRIG=y ++CONFIG_HID_QUANTA=y ++CONFIG_HID_PRIMAX=m ++CONFIG_HID_PS3REMOTE=m ++CONFIG_HID_PRODIKEYS=m ++CONFIG_HID_DRAGONRISE=m ++CONFIG_HID_GYRATION=m ++CONFIG_HID_ICADE=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_ORTEK=m ++CONFIG_HID_PANTHERLORD=m ++CONFIG_HID_PETALYNX=m ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_RMI=m ++CONFIG_HID_ROCCAT=m ++CONFIG_HID_ROCCAT_KONE=m ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++CONFIG_SONY_FF=y ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_STEELSERIES=m ++CONFIG_HID_GREENASIA=m ++CONFIG_HID_SMARTJOYPLUS=m ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THINGM=m ++CONFIG_HID_THRUSTMASTER=m ++CONFIG_HID_XINMO=m ++CONFIG_HID_ZEROPLUS=m ++CONFIG_HID_ZYDACRON=m ++CONFIG_HID_SENSOR_HUB=m ++CONFIG_HID_SENSOR_GYRO_3D=m ++CONFIG_HID_SENSOR_MAGNETOMETER_3D=m ++CONFIG_HID_SENSOR_ALS=m ++CONFIG_HID_SENSOR_ACCEL_3D=m ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m ++CONFIG_HID_ELO=m ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m ++CONFIG_HID_ROCCAT_PYRA=m ++CONFIG_HID_ROCCAT_KONEPLUS=m ++CONFIG_HID_ACRUX=m ++CONFIG_HID_ACRUX_FF=y ++CONFIG_HID_KEYTOUCH=m ++CONFIG_HID_LCPOWER=m ++CONFIG_HID_LENOVO_TPKBD=m ++CONFIG_HID_ROCCAT_ARVO=m ++CONFIG_HID_ROCCAT_ISKU=m ++CONFIG_HID_ROCCAT_KOVAPLUS=m ++CONFIG_HID_HOLTEK=m ++CONFIG_HOLTEK_FF=y ++CONFIG_HID_HUION=m ++CONFIG_HID_SPEEDLINK=m ++CONFIG_HID_WIIMOTE=m ++CONFIG_HID_WIIMOTE_EXT=y ++CONFIG_HID_KYE=m ++CONFIG_HID_SAITEK=m ++CONFIG_HID_TIVO=m ++CONFIG_HID_GENERIC=y ++CONFIG_HID_AUREAL=m ++CONFIG_HID_APPLEIR=m ++ ++ ++# ++# USB Imaging devices ++# ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++ ++# ++# USB Multimedia devices ++# ++ ++CONFIG_USB_DSBR=m ++# CONFIG_USB_ET61X251 is not set ++CONFIG_USB_M5602=m ++CONFIG_USB_STV06XX=m ++CONFIG_USB_GSPCA=m ++CONFIG_USB_GSPCA_MR97310A=m ++CONFIG_USB_GSPCA_BENQ=m ++CONFIG_USB_GSPCA_CONEX=m ++CONFIG_USB_GSPCA_CPIA1=m ++CONFIG_USB_GSPCA_ETOMS=m ++CONFIG_USB_GSPCA_FINEPIX=m ++CONFIG_USB_GSPCA_MARS=m ++CONFIG_USB_GSPCA_OV519=m ++CONFIG_USB_GSPCA_OV534=m ++CONFIG_USB_GSPCA_OV534_9=m ++CONFIG_USB_GSPCA_PAC207=m ++CONFIG_USB_GSPCA_PAC7311=m ++CONFIG_USB_GSPCA_SN9C2028=m ++CONFIG_USB_GSPCA_SN9C20X=m ++CONFIG_USB_GSPCA_SONIXB=m ++CONFIG_USB_GSPCA_SONIXJ=m ++CONFIG_USB_GSPCA_SPCA500=m ++CONFIG_USB_GSPCA_SPCA501=m ++CONFIG_USB_GSPCA_SPCA505=m ++CONFIG_USB_GSPCA_SPCA506=m ++CONFIG_USB_GSPCA_SPCA508=m ++CONFIG_USB_GSPCA_SPCA561=m ++CONFIG_USB_GSPCA_STK014=m ++CONFIG_USB_GSPCA_STK1135=m ++CONFIG_USB_GSPCA_SUNPLUS=m ++CONFIG_USB_GSPCA_T613=m ++CONFIG_USB_GSPCA_TOPRO=m ++CONFIG_USB_GSPCA_TV8532=m ++CONFIG_USB_GSPCA_VC032X=m ++CONFIG_USB_GSPCA_ZC3XX=m ++CONFIG_USB_GSPCA_SQ905=m ++CONFIG_USB_GSPCA_SQ905C=m ++CONFIG_USB_GSPCA_PAC7302=m ++CONFIG_USB_GSPCA_STV0680=m ++CONFIG_USB_GL860=m ++CONFIG_USB_GSPCA_JEILINJ=m ++CONFIG_USB_GSPCA_JL2005BCD=m ++CONFIG_USB_GSPCA_KONICA=m ++CONFIG_USB_GSPCA_XIRLINK_CIT=m ++CONFIG_USB_GSPCA_SPCA1528=m ++CONFIG_USB_GSPCA_SQ930X=m ++CONFIG_USB_GSPCA_NW80X=m ++CONFIG_USB_GSPCA_VICAM=m ++CONFIG_USB_GSPCA_KINECT=m ++CONFIG_USB_GSPCA_SE401=m ++ ++CONFIG_USB_S2255=m ++# CONFIG_VIDEO_SH_MOBILE_CEU is not set ++# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set ++# CONFIG_USB_SN9C102 is not set ++CONFIG_USB_ZR364XX=m ++ ++# ++# USB Network adaptors ++# ++CONFIG_USB_CATC=m ++CONFIG_USB_HSO=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m ++CONFIG_USB_RTL8152=m ++CONFIG_USB_USBNET=m ++CONFIG_USB_SPEEDTOUCH=m ++CONFIG_USB_NET_AX8817X=m ++CONFIG_USB_NET_AX88179_178A=m ++CONFIG_USB_NET_DM9601=m ++CONFIG_USB_NET_SR9700=m ++CONFIG_USB_NET_SMSC95XX=m ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_RNDIS_HOST=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_NET_CDC_EEM=m ++CONFIG_USB_NET_CDC_NCM=m ++CONFIG_USB_NET_HUAWEI_CDC_NCM=m ++CONFIG_USB_NET_CDC_MBIM=m ++CONFIG_USB_NET_ZAURUS=m ++CONFIG_USB_NET_CX82310_ETH=m ++CONFIG_USB_NET_INT51X1=m ++CONFIG_USB_CDC_PHONET=m ++CONFIG_USB_IPHETH=m ++CONFIG_USB_SIERRA_NET=m ++CONFIG_USB_VL600=m ++ ++# ++# USB Host-to-Host Cables ++# ++CONFIG_USB_AN2720=y ++CONFIG_USB_BELKIN=y ++ ++# ++# Intelligent USB Devices/Gadgets ++# ++CONFIG_USB_ARMLINUX=y ++CONFIG_USB_EPSON2888=y ++CONFIG_USB_KC2190=y ++ ++# CONFIG_USB_MUSB_HDRC is not set ++ ++# ++# USB port drivers ++# ++CONFIG_USB_USS720=m ++ ++# ++# USB Serial Converter support ++# ++CONFIG_USB_SERIAL=y ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_SIMPLE=m ++CONFIG_USB_SERIAL_AIRCABLE=m ++CONFIG_USB_SERIAL_ARK3116=m ++CONFIG_USB_SERIAL_BELKIN=m ++CONFIG_USB_SERIAL_CH341=m ++CONFIG_USB_SERIAL_CYPRESS_M8=m ++CONFIG_USB_SERIAL_CYBERJACK=m ++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m ++CONFIG_USB_SERIAL_CP210X=m ++CONFIG_USB_SERIAL_QUALCOMM=m ++CONFIG_USB_SERIAL_SYMBOL=m ++CONFIG_USB_SERIAL_EDGEPORT=m ++CONFIG_USB_SERIAL_EDGEPORT_TI=m ++CONFIG_USB_SERIAL_EMPEG=m ++# CONFIG_USB_SERIAL_F81232 is not set ++CONFIG_USB_SERIAL_FTDI_SIO=m ++CONFIG_USB_SERIAL_FUNSOFT=m ++CONFIG_USB_SERIAL_GARMIN=m ++CONFIG_USB_SERIAL_HP4X=m ++CONFIG_USB_SERIAL_IPAQ=m ++CONFIG_USB_SERIAL_IPW=m ++CONFIG_USB_SERIAL_IR=m ++CONFIG_USB_SERIAL_IUU=m ++CONFIG_USB_SERIAL_KEYSPAN_PDA=m ++CONFIG_USB_SERIAL_KEYSPAN=m ++CONFIG_USB_SERIAL_KEYSPAN_MPR=y ++CONFIG_USB_SERIAL_KEYSPAN_USA28=y ++CONFIG_USB_SERIAL_KEYSPAN_USA28X=y ++CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y ++CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y ++CONFIG_USB_SERIAL_KEYSPAN_USA19=y ++CONFIG_USB_SERIAL_KEYSPAN_USA18X=y ++CONFIG_USB_SERIAL_KEYSPAN_USA19W=y ++CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y ++CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y ++CONFIG_USB_SERIAL_KEYSPAN_USA49W=y ++CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y ++CONFIG_USB_SERIAL_KLSI=m ++CONFIG_USB_SERIAL_KOBIL_SCT=m ++CONFIG_USB_SERIAL_MCT_U232=m ++# CONFIG_USB_SERIAL_METRO is not set ++CONFIG_USB_SERIAL_MOS7720=m ++CONFIG_USB_SERIAL_MOS7715_PARPORT=y ++# CONFIG_USB_SERIAL_ZIO is not set ++# CONFIG_USB_SERIAL_WISHBONE is not set ++# CONFIG_USB_SERIAL_ZTE is not set ++CONFIG_USB_SERIAL_MOS7840=m ++CONFIG_USB_SERIAL_MOTOROLA=m ++# CONFIG_USB_SERIAL_MXUPORT is not set ++CONFIG_USB_SERIAL_NAVMAN=m ++CONFIG_USB_SERIAL_OPTION=m ++CONFIG_USB_SERIAL_OTI6858=m ++CONFIG_USB_SERIAL_OPTICON=m ++CONFIG_USB_SERIAL_OMNINET=m ++CONFIG_USB_SERIAL_PL2303=m ++# CONFIG_USB_SERIAL_QUATECH2 is not set ++CONFIG_USB_SERIAL_SAFE=m ++CONFIG_USB_SERIAL_SAFE_PADDED=y ++CONFIG_USB_SERIAL_SIERRAWIRELESS=m ++CONFIG_USB_SERIAL_SIEMENS_MPI=m ++CONFIG_USB_SERIAL_SPCP8X5=m ++CONFIG_USB_SERIAL_TI=m ++CONFIG_USB_SERIAL_VISOR=m ++CONFIG_USB_SERIAL_WHITEHEAT=m ++CONFIG_USB_SERIAL_XIRCOM=m ++CONFIG_USB_SERIAL_QCAUX=m ++CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m ++CONFIG_USB_SERIAL_XSENS_MT=m ++CONFIG_USB_SERIAL_DEBUG=m ++CONFIG_USB_SERIAL_SSU100=m ++CONFIG_USB_SERIAL_QT2=m ++CONFIG_USB_SERIAL_FLASHLOADER=m ++CONFIG_USB_SERIAL_SUUNTO=m ++CONFIG_USB_SERIAL_CONSOLE=y ++ ++CONFIG_USB_EZUSB=y ++CONFIG_USB_EMI62=m ++CONFIG_USB_LED=m ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++ ++# ++# USB Miscellaneous drivers ++# ++ ++CONFIG_USB_ADUTUX=m ++CONFIG_USB_SEVSEG=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_APPLEDISPLAY=m ++ ++# Physical Layer USB driver ++# CONFIG_USB_OTG_FSM is not set ++ ++# CONFIG_GENERIC_PHY is not set ++# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set ++# CONFIG_PHY_EXYNOS_DP_VIDEO is not set ++# CONFIG_OMAP_USB2 is not set ++# CONFIG_OMAP_USB3 is not set ++# CONFIG_OMAP_CONTROL_USB is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_SAMSUNG_USBPHY is not set ++# CONFIG_SAMSUNG_USB2PHY is not set ++# CONFIG_SAMSUNG_USB3PHY is not set ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_USB_RCAR_PHY=m ++CONFIG_USB_ATM=m ++CONFIG_USB_CXACRU=m ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_CYTHERM is not set ++CONFIG_USB_EMI26=m ++CONFIG_USB_FTDI_ELAN=m ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m ++CONFIG_USB_EZUSB_FX2=m ++CONFIG_USB_HSIC_USB3503=m ++CONFIG_USB_LCD=m ++CONFIG_USB_LD=m ++CONFIG_USB_LEGOTOWER=m ++CONFIG_USB_MON=y ++CONFIG_USB_PWC=m ++CONFIG_USB_PWC_INPUT_EVDEV=y ++# CONFIG_USB_PWC_DEBUG is not set ++# CONFIG_USB_RIO500 is not set ++CONFIG_USB_SISUSBVGA=m ++CONFIG_USB_SISUSBVGA_CON=y ++CONFIG_RADIO_SI470X=y ++CONFIG_USB_KEENE=m ++CONFIG_USB_MA901=m ++CONFIG_USB_SI470X=m ++CONFIG_I2C_SI470X=m ++CONFIG_RADIO_SI4713=m ++# CONFIG_RADIO_TEF6862 is not set ++CONFIG_USB_MR800=m ++CONFIG_USB_STKWEBCAM=m ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_U132_HCD=m ++CONFIG_USB_UEAGLEATM=m ++CONFIG_USB_XUSBATM=m ++ ++# CONFIG_USB_DWC2 is not set ++ ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++ ++# CONFIG_USB_ISP1301 is not set ++ ++# CONFIG_USB_OTG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB=m ++CONFIG_SSB_PCIHOST=y ++CONFIG_SSB_SDIOHOST=y ++CONFIG_SSB_PCMCIAHOST=y ++# CONFIG_SSB_SILENT is not set ++# CONFIG_SSB_DEBUG is not set ++CONFIG_SSB_DRIVER_PCICORE=y ++CONFIG_SSB_DRIVER_GPIO=y ++ ++# Multifunction USB devices ++# CONFIG_MFD_PCF50633 is not set ++CONFIG_PCF50633_ADC=m ++CONFIG_PCF50633_GPIO=m ++# CONFIG_AB3100_CORE is not set ++CONFIG_INPUT_PCF50633_PMU=m ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++ ++CONFIG_MFD_SUPPORT=y ++CONFIG_MFD_VX855=m ++CONFIG_MFD_SM501=m ++CONFIG_MFD_SM501_GPIO=y ++CONFIG_MFD_RTSX_PCI=m ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++CONFIG_MFD_VIPERBOARD=m ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8350 is not set ++# CONFIG_MFD_WM831X is not set ++# CONFIG_AB3100_OTP is not set ++# CONFIG_MFD_TIMBERDALE is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_LPC_SCH is not set ++# CONFIG_LPC_ICH is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_RDC321X is not set ++# CONFIG_MFD_JANZ_CMODIO is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_CS5535 is not set ++# CONFIG_MFD_STMPE is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_ARIZONA is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_SYSCON is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_LP3943 is not set ++ ++# ++# File systems ++# ++CONFIG_MISC_FILESYSTEMS=y ++ ++# ext4 is used for ext2 and ext3 filesystems ++CONFIG_JBD2=y ++CONFIG_FS_MBCACHE=y ++CONFIG_REISERFS_FS=m ++# CONFIG_REISERFS_CHECK is not set ++CONFIG_REISERFS_PROC_INFO=y ++CONFIG_REISERFS_FS_XATTR=y ++CONFIG_REISERFS_FS_POSIX_ACL=y ++CONFIG_REISERFS_FS_SECURITY=y ++CONFIG_JFS_FS=m ++# CONFIG_JFS_DEBUG is not set ++# CONFIG_JFS_STATISTICS is not set ++CONFIG_JFS_POSIX_ACL=y ++CONFIG_JFS_SECURITY=y ++CONFIG_XFS_FS=m ++# CONFIG_XFS_DEBUG is not set ++# CONFIG_XFS_RT is not set ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_MINIX_FS=m ++CONFIG_ROMFS_FS=m ++# CONFIG_QFMT_V1 is not set ++CONFIG_QFMT_V2=y ++CONFIG_QUOTACTL=y ++CONFIG_DNOTIFY=y ++# Autofsv3 is obsolete. ++# systemd is dependant upon AUTOFS, so build it in. ++# CONFIG_EXOFS_FS is not set ++# CONFIG_EXOFS_DEBUG is not set ++CONFIG_NILFS2_FS=m ++# CONFIG_LOGFS is not set ++CONFIG_CEPH_FS=m ++CONFIG_CEPH_FSCACHE=y ++CONFIG_BLK_DEV_RBD=m ++CONFIG_CEPH_LIB=m ++CONFIG_CEPH_FS_POSIX_ACL=y ++# CONFIG_CEPH_LIB_USE_DNS_RESOLVER is not set ++ ++CONFIG_FSCACHE=m ++CONFIG_FSCACHE_STATS=y ++# CONFIG_FSCACHE_HISTOGRAM is not set ++# CONFIG_FSCACHE_DEBUG is not set ++CONFIG_FSCACHE_OBJECT_LIST=y ++ ++CONFIG_CACHEFILES=m ++# CONFIG_CACHEFILES_DEBUG is not set ++# CONFIG_CACHEFILES_HISTOGRAM is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=m ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_VMCORE=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_TMPFS_XATTR=y ++CONFIG_HUGETLBFS=y ++CONFIG_HUGETLB_PAGE=y ++# CONFIG_DEBUG_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++CONFIG_AFFS_FS=m ++CONFIG_ECRYPT_FS=m ++# CONFIG_ECRYPT_FS_MESSAGING is not set ++CONFIG_HFS_FS=m ++CONFIG_HFSPLUS_FS=m ++# CONFIG_HFSPLUS_FS_POSIX_ACL is not set ++CONFIG_BEFS_FS=m ++# CONFIG_BEFS_DEBUG is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++ ++CONFIG_CRAMFS=m ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_SQUASHFS_ZLIB=y ++# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set ++# CONFIG_SQUASHFS_EMBEDDED is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++CONFIG_SYSV_FS=m ++CONFIG_UFS_FS=m ++# CONFIG_UFS_FS_WRITE is not set ++# CONFIG_UFS_DEBUG is not set ++CONFIG_9P_FS=m ++CONFIG_9P_FSCACHE=y ++CONFIG_9P_FS_POSIX_ACL=y ++CONFIG_9P_FS_SECURITY=y ++# CONFIG_OMFS_FS is not set ++CONFIG_CUSE=m ++# CONFIG_F2FS_FS is not set ++ ++# ++# Network File Systems ++# ++CONFIG_NETWORK_FILESYSTEMS=y ++# CONFIG_NFS_V2 is not set ++CONFIG_NFS_V3=y ++CONFIG_NFS_SWAP=y ++CONFIG_NFS_V4_1=y ++CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" ++# CONFIG_NFS_V4_1_MIGRATION is not set ++CONFIG_NFS_V4_2=y ++CONFIG_NFSD=m ++CONFIG_NFSD_V3=y ++CONFIG_NFSD_V3_ACL=y ++CONFIG_NFSD_V4=y ++CONFIG_NFSD_V4_SECURITY_LABEL=y ++CONFIG_NFS_FSCACHE=y ++# CONFIG_NFS_USE_LEGACY_DNS is not set ++CONFIG_PNFS_OBJLAYOUT=m ++CONFIG_PNFS_BLOCK=m ++CONFIG_LOCKD=m ++CONFIG_LOCKD_V4=y ++CONFIG_EXPORTFS=y ++CONFIG_SUNRPC=m ++CONFIG_SUNRPC_GSS=m ++CONFIG_SUNRPC_XPRT_RDMA=m ++CONFIG_SUNRPC_DEBUG=y ++CONFIG_RPCSEC_GSS_KRB5=m ++CONFIG_CIFS=m ++CONFIG_CIFS_STATS=y ++# CONFIG_CIFS_STATS2 is not set ++CONFIG_CIFS_SMB2=y ++CONFIG_CIFS_UPCALL=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_CIFS_FSCACHE=y ++CONFIG_CIFS_ACL=y ++CONFIG_CIFS_WEAK_PW_HASH=y ++CONFIG_CIFS_DEBUG=y ++# CONFIG_CIFS_DEBUG2 is not set ++CONFIG_CIFS_DFS_UPCALL=y ++CONFIG_CIFS_NFSD_EXPORT=y ++CONFIG_NCP_FS=m ++CONFIG_NCPFS_PACKET_SIGNING=y ++CONFIG_NCPFS_IOCTL_LOCKING=y ++CONFIG_NCPFS_STRONG=y ++CONFIG_NCPFS_NFS_NS=y ++CONFIG_NCPFS_OS2_NS=y ++CONFIG_NCPFS_SMALLDOS=y ++CONFIG_NCPFS_NLS=y ++CONFIG_NCPFS_EXTRAS=y ++CONFIG_CODA_FS=m ++# CONFIG_AFS_FS is not set ++# CONFIG_AF_RXRPC is not set ++ ++CONFIG_OCFS2_FS=m ++# CONFIG_OCFS2_DEBUG_FS is not set ++# CONFIG_OCFS2_DEBUG_MASKLOG is not set ++CONFIG_OCFS2_FS_O2CB=m ++CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m ++# CONFIG_OCFS2_FS_STATS is not set ++ ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++# Maybe see if we want this on for debug kernels? ++# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set ++# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set ++# CONFIG_BTRFS_DEBUG is not set ++# CONFIG_BTRFS_ASSERT is not set ++ ++CONFIG_CONFIGFS_FS=y ++ ++CONFIG_DLM=m ++CONFIG_DLM_DEBUG=y ++CONFIG_GFS2_FS=m ++CONFIG_GFS2_FS_LOCKING_DLM=y ++ ++ ++CONFIG_UBIFS_FS_XATTR=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++# CONFIG_UBIFS_FS_DEBUG is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++CONFIG_AIX_PARTITION=y ++CONFIG_AMIGA_PARTITION=y ++# CONFIG_ATARI_PARTITION is not set ++CONFIG_BSD_DISKLABEL=y ++CONFIG_EFI_PARTITION=y ++CONFIG_KARMA_PARTITION=y ++CONFIG_LDM_PARTITION=y ++# CONFIG_LDM_DEBUG is not set ++CONFIG_MAC_PARTITION=y ++CONFIG_MSDOS_PARTITION=y ++CONFIG_MINIX_SUBPARTITION=y ++CONFIG_OSF_PARTITION=y ++CONFIG_SGI_PARTITION=y ++CONFIG_SOLARIS_X86_PARTITION=y ++CONFIG_SUN_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_UNIXWARE_DISKLABEL=y ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_CMDLINE_PARTITION is not set ++ ++CONFIG_NLS=y ++ ++# ++# Native Language Support ++# ++CONFIG_NLS_CODEPAGE_737=m ++CONFIG_NLS_CODEPAGE_775=m ++CONFIG_NLS_CODEPAGE_850=m ++CONFIG_NLS_CODEPAGE_852=m ++CONFIG_NLS_CODEPAGE_855=m ++CONFIG_NLS_CODEPAGE_857=m ++CONFIG_NLS_CODEPAGE_860=m ++CONFIG_NLS_CODEPAGE_861=m ++CONFIG_NLS_CODEPAGE_862=m ++CONFIG_NLS_CODEPAGE_863=m ++CONFIG_NLS_CODEPAGE_864=m ++CONFIG_NLS_CODEPAGE_865=m ++CONFIG_NLS_CODEPAGE_866=m ++CONFIG_NLS_CODEPAGE_869=m ++CONFIG_NLS_CODEPAGE_936=m ++CONFIG_NLS_CODEPAGE_950=m ++CONFIG_NLS_CODEPAGE_932=m ++CONFIG_NLS_CODEPAGE_949=m ++CONFIG_NLS_CODEPAGE_874=m ++CONFIG_NLS_ISO8859_8=m ++CONFIG_NLS_CODEPAGE_1250=m ++CONFIG_NLS_CODEPAGE_1251=m ++CONFIG_NLS_ISO8859_2=m ++CONFIG_NLS_ISO8859_3=m ++CONFIG_NLS_ISO8859_4=m ++CONFIG_NLS_ISO8859_5=m ++CONFIG_NLS_ISO8859_6=m ++CONFIG_NLS_ISO8859_7=m ++CONFIG_NLS_ISO8859_9=m ++CONFIG_NLS_ISO8859_13=m ++CONFIG_NLS_ISO8859_14=m ++CONFIG_NLS_KOI8_R=m ++CONFIG_NLS_KOI8_U=m ++CONFIG_NLS_MAC_ROMAN=m ++CONFIG_NLS_MAC_CELTIC=m ++CONFIG_NLS_MAC_CENTEURO=m ++CONFIG_NLS_MAC_CROATIAN=m ++CONFIG_NLS_MAC_CYRILLIC=m ++CONFIG_NLS_MAC_GAELIC=m ++CONFIG_NLS_MAC_GREEK=m ++CONFIG_NLS_MAC_ICELAND=m ++CONFIG_NLS_MAC_INUIT=m ++CONFIG_NLS_MAC_ROMANIAN=m ++CONFIG_NLS_MAC_TURKISH=m ++ ++# ++# Profiling support ++# ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=m ++CONFIG_OPROFILE_EVENT_MULTIPLEX=y ++ ++# ++# Kernel hacking ++# ++CONFIG_DEBUG_KERNEL=y ++CONFIG_FRAME_WARN=1024 ++CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0 ++# CONFIG_DEBUG_INFO is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++# CONFIG_DEBUG_DRIVER is not set ++CONFIG_HEADERS_CHECK=y ++# CONFIG_LKDTM is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_READABLE_ASM is not set ++ ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_LOCKDEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++ ++# DEBUG options that don't get enabled/disabled with 'make debug/release' ++ ++# This generates a huge amount of dmesg spew ++# CONFIG_DEBUG_KOBJECT is not set ++# ++# This breaks booting until the module patches are in-tree ++# CONFIG_DEBUG_KOBJECT_RELEASE is not set ++# ++# ++# These debug options are deliberatly left on (even in 'make release' kernels). ++# They aren't that much of a performance impact, and the value ++# from getting useful bug-reports makes it worth leaving them on. ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++CONFIG_BOOT_PRINTK_DELAY=y ++CONFIG_DEBUG_DEVRES=y ++CONFIG_DEBUG_RODATA_TEST=y ++CONFIG_DEBUG_NX_TEST=m ++CONFIG_DEBUG_SET_MODULE_RONX=y ++CONFIG_DEBUG_BOOT_PARAMS=y ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set ++CONFIG_LOCKUP_DETECTOR=y ++# CONFIG_DEBUG_INFO_REDUCED is not set ++# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set ++# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_ATOMIC64_SELFTEST=y ++CONFIG_MEMORY_FAILURE=y ++CONFIG_HWPOISON_INJECT=m ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++CONFIG_RESOURCE_COUNTERS=y ++# CONFIG_DEBUG_VIRTUAL is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++CONFIG_EARLY_PRINTK_DBGP=y ++# CONFIG_PAGE_POISONING is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_CRASH is not set ++# CONFIG_GCOV_KERNEL is not set ++# CONFIG_RAMOOPS is not set ++ ++ ++# ++# Security options ++# ++CONFIG_SECURITY=y ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++CONFIG_SECURITY_NETWORK=y ++CONFIG_SECURITY_NETWORK_XFRM=y ++# CONFIG_SECURITY_PATH is not set ++CONFIG_SECURITY_SELINUX=y ++CONFIG_SECURITY_SELINUX_BOOTPARAM=y ++CONFIG_SECURITY_SELINUX_DISABLE=y ++CONFIG_SECURITY_SELINUX_DEVELOP=y ++CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 ++CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 ++CONFIG_SECURITY_SELINUX_AVC_STATS=y ++# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set ++# CONFIG_SECURITY_SMACK is not set ++# CONFIG_SECURITY_TOMOYO is not set ++# CONFIG_SECURITY_APPARMOR is not set ++# CONFIG_SECURITY_YAMA is not set ++CONFIG_AUDIT=y ++CONFIG_AUDITSYSCALL=y ++# http://lists.fedoraproject.org/pipermail/kernel/2013-February/004125.html ++CONFIG_AUDIT_LOGINUID_IMMUTABLE=y ++ ++CONFIG_SECCOMP=y ++ ++# CONFIG_SSBI is not set ++ ++# ++# Cryptographic options ++# ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_FIPS=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# Note, CONFIG_CRYPTO_MANAGER_DISABLE_TESTS needs to be unset, or FIPS will be disabled. ++# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set ++CONFIG_CRYPTO_HW=y ++CONFIG_CRYPTO_BLKCIPHER=y ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_AES=y ++CONFIG_CRYPTO_ARC4=m ++CONFIG_CRYPTO_ANUBIS=m ++CONFIG_CRYPTO_AUTHENC=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_CAST6=m ++CONFIG_CRYPTO_CRC32C=y ++CONFIG_CRYPTO_CRC32=m ++CONFIG_CRYPTO_CTR=y ++CONFIG_CRYPTO_DEFLATE=m ++CONFIG_CRYPTO_FCRYPT=m ++CONFIG_CRYPTO_GF128MUL=m ++CONFIG_CRYPTO_CMAC=m ++CONFIG_CRYPTO_HMAC=y ++CONFIG_CRYPTO_KHAZAD=m ++CONFIG_CRYPTO_LZO=m ++CONFIG_CRYPTO_LZ4=m ++CONFIG_CRYPTO_LZ4HC=m ++CONFIG_CRYPTO_NULL=m ++CONFIG_CRYPTO_PCBC=m ++CONFIG_CRYPTO_SALSA20=m ++CONFIG_CRYPTO_SALSA20_586=m ++CONFIG_CRYPTO_SEED=m ++CONFIG_CRYPTO_SEQIV=m ++CONFIG_CRYPTO_SERPENT=m ++CONFIG_CRYPTO_TEA=m ++CONFIG_CRYPTO_XCBC=m ++CONFIG_CRYPTO_VMAC=m ++CONFIG_CRYPTO_CRC32C_INTEL=m ++CONFIG_CRYPTO_GHASH=m ++CONFIG_CRYPTO_DEV_HIFN_795X=m ++CONFIG_CRYPTO_DEV_HIFN_795X_RNG=y ++CONFIG_CRYPTO_PCRYPT=m ++ ++ ++ ++# Random number generation ++ ++# ++# Library routines ++# ++CONFIG_CRC16=y ++CONFIG_CRC32=m ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC_ITU_T=m ++CONFIG_CRC8=m ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_CORDIC=m ++# CONFIG_DDR is not set ++ ++CONFIG_CRYPTO_ZLIB=m ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=m ++ ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_KEYS=y ++CONFIG_PERSISTENT_KEYRINGS=y ++CONFIG_BIG_KEYS=y ++CONFIG_TRUSTED_KEYS=m ++CONFIG_ENCRYPTED_KEYS=m ++CONFIG_KEYS_DEBUG_PROC_KEYS=y ++CONFIG_CDROM_PKTCDVD=m ++CONFIG_CDROM_PKTCDVD_BUFFERS=8 ++# CONFIG_CDROM_PKTCDVD_WCACHE is not set ++ ++CONFIG_ATA_OVER_ETH=m ++CONFIG_BACKLIGHT_LCD_SUPPORT=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=m ++# CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_PROGEAR=m ++ ++CONFIG_LCD_CLASS_DEVICE=m ++CONFIG_LCD_PLATFORM=m ++ ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_CFS_BANDWIDTH=y ++CONFIG_SCHED_OMIT_FRAME_POINTER=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_SCHED_AUTOGROUP=y ++ ++CONFIG_CPUSETS=y ++CONFIG_PROC_PID_CPUSET=y ++ ++# CONFIG_CGROUP_DEBUG is not set ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_SCHED=y ++CONFIG_MEMCG=y ++CONFIG_MEMCG_SWAP=y ++CONFIG_MEMCG_SWAP_ENABLED=y ++CONFIG_MEMCG_KMEM=y ++# CONFIG_CGROUP_HUGETLB is not set ++CONFIG_CGROUP_PERF=y ++CONFIG_CGROUP_NET_PRIO=m ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_BLK_CGROUP=y ++ ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_SYSFS_DEPRECATED_V2 is not set ++ ++CONFIG_PRINTK_TIME=y ++ ++CONFIG_ENABLE_MUST_CHECK=y ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++ ++CONFIG_KEXEC=y ++ ++CONFIG_HWMON=y ++# CONFIG_HWMON_DEBUG_CHIP is not set ++CONFIG_THERMAL_HWMON=y ++# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set ++# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set ++CONFIG_THERMAL_GOV_FAIR_SHARE=y ++# CONFIG_THERMAL_GOV_USER_SPACE is not set ++CONFIG_THERMAL_GOV_STEP_WISE=y ++# CONFIG_THERMAL_EMULATION is not set ++ ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++ ++# ++# Bus devices ++# ++# CONFIG_OMAP_OCP2SCP is not set ++CONFIG_PROC_EVENTS=y ++ ++CONFIG_IBMASR=m ++ ++CONFIG_PM=y ++CONFIG_PM_STD_PARTITION="" ++# CONFIG_DPM_WATCHDOG is not set # revisit this in debug ++CONFIG_PM_TRACE=y ++CONFIG_PM_TRACE_RTC=y ++# CONFIG_PM_OPP is not set ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++CONFIG_HIBERNATION=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_SUSPEND=y ++ ++CONFIG_CPU_FREQ_TABLE=y ++CONFIG_CPU_FREQ_STAT=m ++CONFIG_CPU_FREQ_STAT_DETAILS=y ++ ++ ++CONFIG_NET_VENDOR_SMC=y ++# CONFIG_IBMTR is not set ++# CONFIG_SKISA is not set ++# CONFIG_PROTEON is not set ++# CONFIG_SMCTR is not set ++ ++# CONFIG_MOUSE_ATIXL is not set ++ ++# CONFIG_MEDIA_PARPORT_SUPPORT is not set ++ ++CONFIG_RADIO_TEA5764=m ++CONFIG_RADIO_SAA7706H=m ++CONFIG_RADIO_CADET=m ++CONFIG_RADIO_RTRACK=m ++CONFIG_RADIO_RTRACK2=m ++CONFIG_RADIO_AZTECH=m ++CONFIG_RADIO_GEMTEK=m ++CONFIG_RADIO_SF16FMI=m ++CONFIG_RADIO_SF16FMR2=m ++CONFIG_RADIO_TERRATEC=m ++CONFIG_RADIO_TRUST=m ++CONFIG_RADIO_TYPHOON=m ++CONFIG_RADIO_ZOLTRIX=m ++ ++CONFIG_SND_DARLA20=m ++CONFIG_SND_GINA20=m ++CONFIG_SND_LAYLA20=m ++CONFIG_SND_DARLA24=m ++CONFIG_SND_GINA24=m ++CONFIG_SND_LAYLA24=m ++CONFIG_SND_MONA=m ++CONFIG_SND_MIA=m ++CONFIG_SND_ECHO3G=m ++CONFIG_SND_INDIGO=m ++CONFIG_SND_INDIGOIO=m ++CONFIG_SND_INDIGODJ=m ++CONFIG_SND_INDIGOIOX=m ++CONFIG_SND_INDIGODJX=m ++ ++CONFIG_BALLOON_COMPACTION=y ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++CONFIG_BOUNCE=y ++# CONFIG_LEDS_AMS_DELTA is not set ++# CONFIG_LEDS_LOCOMO is not set ++# CONFIG_LEDS_NET48XX is not set ++# CONFIG_LEDS_NET5501 is not set ++# CONFIG_LEDS_PCA9532 is not set ++# CONFIG_LEDS_PCA955X is not set ++# CONFIG_LEDS_BD2802 is not set ++# CONFIG_LEDS_S3C24XX is not set ++# CONFIG_LEDS_PCA9633 is not set ++CONFIG_LEDS_DELL_NETBOOKS=m ++# CONFIG_LEDS_TCA6507 is not set ++# CONFIG_LEDS_LM355x is not set ++# CONFIG_LEDS_OT200 is not set ++# CONFIG_LEDS_PWM is not set ++# CONFIG_LEDS_LP8501 is not set ++# CONFIG_LEDS_PCA963X is not set ++# CONFIG_LEDS_PCA9685 is not set ++CONFIG_LEDS_TRIGGER_TIMER=m ++CONFIG_LEDS_TRIGGER_ONESHOT=m ++CONFIG_LEDS_TRIGGER_IDE_DISK=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=m ++CONFIG_LEDS_TRIGGER_BACKLIGHT=m ++# CONFIG_LEDS_TRIGGER_CPU is not set ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=m ++CONFIG_LEDS_TRIGGER_TRANSIENT=m ++CONFIG_LEDS_TRIGGER_CAMERA=m ++CONFIG_LEDS_ALIX2=m ++CONFIG_LEDS_CLEVO_MAIL=m ++CONFIG_LEDS_INTEL_SS4200=m ++CONFIG_LEDS_LM3530=m ++# CONFIG_LEDS_LM3642 is not set ++CONFIG_LEDS_LM3556=m ++CONFIG_LEDS_BLINKM=m ++CONFIG_LEDS_LP3944=m ++CONFIG_LEDS_LP5521=m ++CONFIG_LEDS_LP5523=m ++CONFIG_LEDS_LP5562=m ++CONFIG_LEDS_LT3593=m ++CONFIG_LEDS_REGULATOR=m ++CONFIG_LEDS_WM8350=m ++CONFIG_LEDS_WM831X_STATUS=m ++ ++CONFIG_DMA_ENGINE=y ++CONFIG_DW_DMAC_CORE=m ++CONFIG_DW_DMAC=m ++CONFIG_DW_DMAC_PCI=m ++# CONFIG_DW_DMAC_BIG_ENDIAN_IO is not set ++# CONFIG_TIMB_DMA is not set ++# CONFIG_DMATEST is not set ++CONFIG_ASYNC_TX_DMA=y ++ ++CONFIG_UNUSED_SYMBOLS=y ++ ++CONFIG_UPROBE_EVENT=y ++ ++CONFIG_DYNAMIC_FTRACE=y ++# CONFIG_IRQSOFF_TRACER is not set ++CONFIG_SCHED_TRACER=y ++CONFIG_CONTEXT_SWITCH_TRACER=y ++CONFIG_TRACER_SNAPSHOT=y ++# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set ++CONFIG_FTRACE_SYSCALLS=y ++CONFIG_FTRACE_MCOUNT_RECORD=y ++# CONFIG_FTRACE_STARTUP_TEST is not set ++# CONFIG_TRACE_BRANCH_PROFILING is not set ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_RING_BUFFER_BENCHMARK=m ++# CONFIG_RING_BUFFER_STARTUP_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++CONFIG_FUNCTION_TRACER=y ++CONFIG_STACK_TRACER=y ++# CONFIG_FUNCTION_GRAPH_TRACER is not set ++ ++CONFIG_KPROBES=y ++CONFIG_KPROBE_EVENT=y ++# CONFIG_KPROBES_SANITY_TEST is not set ++# CONFIG_JUMP_LABEL is not set ++CONFIG_OPTPROBES=y ++ ++CONFIG_HZ_1000=y ++ ++CONFIG_TIMER_STATS=y ++CONFIG_PERF_COUNTERS=y ++ ++# Auxillary displays ++CONFIG_KS0108=m ++CONFIG_KS0108_PORT=0x378 ++CONFIG_KS0108_DELAY=2 ++CONFIG_CFAG12864B=y ++CONFIG_CFAG12864B_RATE=20 ++ ++# CONFIG_PHANTOM is not set ++ ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++ ++# CONFIG_TEST_POWER is not set ++CONFIG_APM_POWER=m ++# CONFIG_GENERIC_ADC_BATTERY is not set ++# CONFIG_WM831X_POWER is not set ++ ++# CONFIG_BATTERY_DS2760 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ20Z75 is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_BATTERY_GOLDFISH is not set ++ ++# CONFIG_CHARGER_ISP1704 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_PCF50633 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++CONFIG_POWER_RESET=y ++ ++# CONFIG_PDA_POWER is not set ++ ++CONFIG_AUXDISPLAY=y ++ ++CONFIG_UIO=m ++CONFIG_UIO_CIF=m ++# CONFIG_UIO_PDRV is not set ++# CONFIG_UIO_PDRV_GENIRQ is not set ++# CONFIG_UIO_DMEM_GENIRQ is not set ++CONFIG_UIO_AEC=m ++CONFIG_UIO_SERCOS3=m ++CONFIG_UIO_PCI_GENERIC=m ++# CONFIG_UIO_NETX is not set ++# CONFIG_UIO_MF624 is not set ++ ++CONFIG_VFIO=m ++CONFIG_VFIO_IOMMU_TYPE1=m ++CONFIG_VFIO_PCI=m ++ ++ ++# LIRC ++CONFIG_LIRC_STAGING=y ++CONFIG_LIRC_BT829=m ++CONFIG_LIRC_IGORPLUGUSB=m ++CONFIG_LIRC_IMON=m ++CONFIG_LIRC_ZILOG=m ++CONFIG_LIRC_PARALLEL=m ++CONFIG_LIRC_SERIAL=m ++CONFIG_LIRC_SERIAL_TRANSMITTER=y ++CONFIG_LIRC_SASEM=m ++CONFIG_LIRC_SIR=m ++CONFIG_LIRC_TTUSBIR=m ++ ++# CONFIG_SAMPLES is not set ++ ++ ++CONFIG_NOZOMI=m ++# CONFIG_TPS65010 is not set ++ ++CONFIG_INPUT_APANEL=m ++CONFIG_INPUT_GP2A=m ++# CONFIG_INPUT_GPIO_TILT_POLLED is not set ++# CONFIG_INPUT_GPIO_BEEPER is not set ++ ++# CONFIG_INTEL_MENLOW is not set ++CONFIG_ENCLOSURE_SERVICES=m ++CONFIG_IPWIRELESS=m ++ ++# CONFIG_BLK_DEV_XIP is not set ++CONFIG_MEMSTICK=m ++# CONFIG_MEMSTICK_DEBUG is not set ++# CONFIG_MEMSTICK_UNSAFE_RESUME is not set ++CONFIG_MSPRO_BLOCK=m ++# CONFIG_MS_BLOCK is not set ++CONFIG_MEMSTICK_TIFM_MS=m ++CONFIG_MEMSTICK_JMICRON_38X=m ++CONFIG_MEMSTICK_R592=m ++CONFIG_MEMSTICK_REALTEK_PCI=m ++ ++CONFIG_ACCESSIBILITY=y ++CONFIG_A11Y_BRAILLE_CONSOLE=y ++ ++# CONFIG_HTC_PASIC3 is not set ++ ++# MT9V022_PCA9536_SWITCH is not set ++ ++CONFIG_OPTIMIZE_INLINING=y ++ ++# FIXME: This should be x86/ia64 only ++# CONFIG_HP_ILO is not set ++ ++CONFIG_GPIOLIB=y ++# CONFIG_PINCTRL is not set ++# CONFIG_DEBUG_PINCTRL is not set ++# CONFIG_PINMUX is not set ++# CONFIG_PINCONF is not set ++ ++CONFIG_NET_DSA=m ++CONFIG_NET_DSA_MV88E6060=m ++CONFIG_NET_DSA_MV88E6131=m ++CONFIG_NET_DSA_MV88E6123_61_65=m ++ ++# Used by Maemo, we don't care. ++# CONFIG_PHONET is not set ++ ++# CONFIG_ICS932S401 is not set ++# CONFIG_ATMEL_SSC is not set ++ ++# CONFIG_C2PORT is not set ++ ++# CONFIG_REGULATOR_DEBUG is not set ++ ++CONFIG_WM8350_POWER=m ++ ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++ ++CONFIG_USB_WUSB=m ++CONFIG_USB_WUSB_CBAF=m ++# CONFIG_USB_WUSB_CBAF_DEBUG is not set ++CONFIG_USB_WHCI_HCD=m ++CONFIG_USB_HWA_HCD=m ++# CONFIG_USB_HCD_BCMA is not set ++# CONFIG_USB_HCD_SSB is not set ++ ++CONFIG_UWB=m ++CONFIG_UWB_HWA=m ++CONFIG_UWB_WHCI=m ++CONFIG_UWB_I1480U=m ++ ++# CONFIG_ANDROID is not set ++CONFIG_STAGING_MEDIA=y ++# CONFIG_DVB_AS102 is not set ++# CONFIG_ET131X is not set ++# CONFIG_SLICOSS is not set ++# CONFIG_WLAGS49_H2 is not set ++# CONFIG_WLAGS49_H25 is not set ++# CONFIG_VIDEO_DT3155 is not set ++# CONFIG_TI_ST is not set ++# CONFIG_FB_XGI is not set ++# CONFIG_VIDEO_GO7007 is not set ++# CONFIG_I2C_BCM2048 is not set ++# CONFIG_VIDEO_TCM825X is not set ++# CONFIG_VIDEO_OMAP4 is not set ++# CONFIG_USB_MSI3101 is not set ++# CONFIG_DT3155 is not set ++# CONFIG_W35UND is not set ++# CONFIG_PRISM2_USB is not set ++# CONFIG_ECHO is not set ++CONFIG_USB_ATMEL=m ++# CONFIG_COMEDI is not set ++# CONFIG_ASUS_OLED is not set ++# CONFIG_PANEL is not set ++# CONFIG_TRANZPORT is not set ++# CONFIG_POHMELFS is not set ++# CONFIG_IDE_PHISON is not set ++# CONFIG_LINE6_USB is not set ++# CONFIG_VME_BUS is not set ++# CONFIG_RAR_REGISTER is not set ++# CONFIG_VT6656 is not set ++# CONFIG_USB_SERIAL_QUATECH_USB2 is not set ++# Larry Finger maintains these (rhbz 913753) ++CONFIG_RTLLIB=m ++CONFIG_RTLLIB_CRYPTO_CCMP=m ++CONFIG_RTLLIB_CRYPTO_TKIP=m ++CONFIG_RTLLIB_CRYPTO_WEP=m ++CONFIG_RTL8192E=m ++# CONFIG_INPUT_GPIO is not set ++# CONFIG_VIDEO_CX25821 is not set ++# CONFIG_R8187SE is not set ++# CONFIG_R8188EU is not set ++# CONFIG_R8821AE is not set ++# CONFIG_RTL8192U is not set ++# CONFIG_FB_SM7XX is not set ++# CONFIG_SPECTRA is not set ++# CONFIG_EASYCAP is not set ++# CONFIG_SOLO6X10 is not set ++# CONFIG_ACPI_QUICKSTART is not set ++# CONFIG_LTE_GDM724X is not set ++CONFIG_R8712U=m # Larry Finger maintains this (rhbz 699618) ++# CONFIG_R8712_AP is not set ++# CONFIG_ATH6K_LEGACY is not set ++# CONFIG_USB_ENESTORAGE is not set ++# CONFIG_BCM_WIMAX is not set ++# CONFIG_USB_BTMTK is not set ++# CONFIG_FT1000 is not set ++# CONFIG_SPEAKUP is not set ++# CONFIG_DX_SEP is not set ++# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set ++# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set ++# CONFIG_RTS_PSTOR is not set ++CONFIG_ALTERA_STAPL=m ++# CONFIG_DVB_CXD2099 is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_INTEL_MEI is not set ++# CONFIG_ZCACHE is not set ++# CONFIG_RTS5139 is not set ++# CONFIG_NVEC_LEDS is not set ++# CONFIG_VT6655 is not set ++# CONFIG_RAMSTER is not set ++# CONFIG_USB_WPAN_HCD is not set ++# CONFIG_WIMAX_GDM72XX is not set ++# CONFIG_IPACK_BUS is not set ++# CONFIG_CSR_WIFI is not set ++# CONFIG_ZCACHE2 is not set ++# CONFIG_NET_VENDOR_SILICOM is not set ++# CONFIG_SBYPASS is not set ++# CONFIG_BPCTL is not set ++# CONFIG_CED1401 is not set ++# CONFIG_DGRP is not set ++# CONFIG_SB105X is not set ++# CONFIG_LUSTRE_FS is not set ++# CONFIG_XILLYBUS is not set ++# CONFIG_DGAP is not set ++# CONFIG_DGNC is not set ++# CONFIG_RTS5208 is not set ++# END OF STAGING ++ ++# ++# Remoteproc drivers (EXPERIMENTAL) ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++CONFIG_LIBFC=m ++CONFIG_LIBFCOE=m ++CONFIG_FCOE=m ++CONFIG_FCOE_FNIC=m ++ ++ ++# CONFIG_IMA is not set ++CONFIG_IMA_MEASURE_PCR_IDX=10 ++CONFIG_IMA_AUDIT=y ++CONFIG_IMA_LSM_RULES=y ++ ++# CONFIG_EVM is not set ++# CONFIG_PWM_PCA9685 is not set ++ ++CONFIG_LSM_MMAP_MIN_ADDR=65536 ++ ++CONFIG_STRIP_ASM_SYMS=y ++ ++# CONFIG_RCU_FANOUT_EXACT is not set ++# FIXME: Revisit FAST_NO_HZ after it's fixed ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_RCU_NOCB_CPU is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_USER_QS is not set ++ ++CONFIG_KSM=y ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++ ++CONFIG_FSNOTIFY=y ++CONFIG_FANOTIFY=y ++CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y ++ ++CONFIG_IEEE802154=m ++CONFIG_IEEE802154_6LOWPAN=m ++CONFIG_IEEE802154_DRIVERS=m ++CONFIG_IEEE802154_FAKEHARD=m ++CONFIG_IEEE802154_FAKELB=m ++ ++CONFIG_MAC802154=m ++CONFIG_NET_MPLS_GSO=m ++ ++# CONFIG_HSR is not set ++ ++# CONFIG_EXTCON is not set ++# CONFIG_EXTCON_ADC_JACK is not set ++# CONFIG_MEMORY is not set ++ ++CONFIG_PPS=m ++# CONFIG_PPS_CLIENT_KTIMER is not set ++CONFIG_PPS_CLIENT_LDISC=m ++# CONFIG_PPS_DEBUG is not set ++CONFIG_PPS_CLIENT_PARPORT=m ++CONFIG_PPS_GENERATOR_PARPORT=m ++CONFIG_PPS_CLIENT_GPIO=m ++CONFIG_NTP_PPS=y ++ ++CONFIG_PTP_1588_CLOCK=m ++CONFIG_PTP_1588_CLOCK_PCH=m ++ ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_ZSWAP=y ++CONFIG_ZSMALLOC=y ++# CONFIG_PGTABLE_MAPPING is not set ++ ++# CONFIG_MDIO_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_DESIGNWARE_PCI is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_DEBUG_GPIO is not set ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_CS5535 is not set ++# CONFIG_GPIO_IT8761E is not set ++# CONFIG SB105x is not set ++# CONFIG_GPIO_TS5500 is not set ++CONFIG_GPIO_VIPERBOARD=m ++# CONFIG_UCB1400_CORE is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_RADIO_MIROPCM20 is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_GPIO_SCH is not set ++# CONFIG_GPIO_LANGWELL is not set ++# CONFIG_GPIO_RDC321X is not set ++# CONFIG_GPIO_VX855 is not set ++# CONFIG_GPIO_PCH is not set ++# CONFIG_GPIO_ML_IOH is not set ++# CONFIG_GPIO_AMD8111 is not set ++# CONFIG_GPIO_BT8XX is not set ++# CONFIG_GPIO_GRGPIO is not set ++# CONFIG_GPIO_PL061 is not set ++# CONFIG_GPIO_BCM_KONA is not set ++# CONFIG_GPIO_SCH311X is not set ++CONFIG_GPIO_MAX730X=m ++CONFIG_GPIO_MAX7300=m ++CONFIG_GPIO_MAX732X=m ++CONFIG_GPIO_PCF857X=m ++CONFIG_GPIO_SX150X=y ++CONFIG_GPIO_ADP5588=m ++CONFIG_GPIO_ADNP=m ++CONFIG_GPIO_MAX7301=m ++CONFIG_GPIO_MCP23S08=m ++CONFIG_GPIO_MC33880=m ++CONFIG_GPIO_74X164=m ++ ++# FIXME: Why? ++CONFIG_EVENT_POWER_TRACING_DEPRECATED=y ++ ++CONFIG_TEST_KSTRTOX=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++# CONFIG_XZ_DEC_IA64 is not set ++CONFIG_XZ_DEC_ARM=y ++# CONFIG_XZ_DEC_ARMTHUMB is not set ++# CONFIG_XZ_DEC_SPARC is not set ++# CONFIG_XZ_DEC_TEST is not set ++ ++# CONFIG_POWER_AVS is not set ++ ++CONFIG_TARGET_CORE=m ++CONFIG_ISCSI_TARGET=m ++CONFIG_LOOPBACK_TARGET=m ++CONFIG_SBP_TARGET=m ++CONFIG_TCM_IBLOCK=m ++CONFIG_TCM_FILEIO=m ++CONFIG_TCM_PSCSI=m ++CONFIG_TCM_FC=m ++ ++CONFIG_HWSPINLOCK=m ++ ++CONFIG_PSTORE=y ++CONFIG_PSTORE_RAM=m ++# CONFIG_PSTORE_CONSOLE is not set ++# CONFIG_PSTORE_FTRACE is not set ++ ++# CONFIG_TEST_MODULE is not set ++# CONFIG_TEST_USER_COPY is not set ++ ++# CONFIG_AVERAGE is not set ++# CONFIG_VMXNET3 is not set ++ ++# CONFIG_SIGMA is not set ++ ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++ ++CONFIG_BCMA=m ++CONFIG_BCMA_BLOCKIO=y ++CONFIG_BCMA_HOST_PCI_POSSIBLE=y ++CONFIG_BCMA_HOST_PCI=y ++# CONFIG_BCMA_HOST_SOC is not set ++CONFIG_BCMA_DRIVER_GMAC_CMN=y ++CONFIG_BCMA_DRIVER_GPIO=y ++# CONFIG_BCMA_DEBUG is not set ++ ++# CONFIG_GOOGLE_FIRMWARE is not set ++# CONFIG_INTEL_MID_PTI is not set ++ ++# CONFIG_MAILBOX is not set ++ ++CONFIG_FMC=m ++CONFIG_FMC_FAKEDEV=m ++CONFIG_FMC_TRIVIAL=m ++CONFIG_FMC_WRITE_EEPROM=m ++CONFIG_FMC_CHARDEV=m ++ ++# CONFIG_GENWQE is not set ++ ++# CONFIG_POWERCAP is not set ++ ++# CONFIG_HSI is not set ++ ++ ++# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set ++ ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_SYSTEM_TRUSTED_KEYRING is not set ++# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set ++# CONFIG_MODULE_VERIFY_ELF is not set ++# CONFIG_CRYPTO_KEY_TYPE is not set ++# CONFIG_PGP_LIBRARY is not set ++# CONFIG_PGP_PRELOAD is not set ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_PROC_DEVICETREE=y ++ +diff -Nur linux-3.14.36/arch/arm/configs/imx_v7_defconfig linux-openelec/arch/arm/configs/imx_v7_defconfig +--- linux-3.14.36/arch/arm/configs/imx_v7_defconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/configs/imx_v7_defconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,343 @@ ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_KERNEL_LZO=y ++CONFIG_SYSVIPC=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_LOG_BUF_SHIFT=18 ++CONFIG_CGROUPS=y ++CONFIG_RELAY=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EXPERT=y ++CONFIG_PERF_EVENTS=y ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++# CONFIG_BLK_DEV_BSG is not set ++CONFIG_GPIO_PCA953X=y ++CONFIG_ARCH_MXC=y ++CONFIG_MXC_DEBUG_BOARD=y ++CONFIG_MACH_IMX51_DT=y ++CONFIG_MACH_EUKREA_CPUIMX51SD=y ++CONFIG_SOC_IMX53=y ++CONFIG_SOC_IMX6Q=y ++CONFIG_SOC_IMX6SL=y ++CONFIG_SOC_VF610=y ++# CONFIG_SWP_EMULATE is not set ++CONFIG_SMP=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_PREEMPT=y ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++CONFIG_HIGHMEM=y ++CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_ARM_IMX6_CPUFREQ=y ++CONFIG_CPU_IDLE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_BINFMT_MISC=m ++CONFIG_PM_RUNTIME=y ++CONFIG_PM_DEBUG=y ++CONFIG_PM_TEST_SUSPEND=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++CONFIG_IPV6=y ++CONFIG_NETFILTER=y ++CONFIG_VLAN_8021Q=y ++# CONFIG_WIRELESS is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++# CONFIG_STANDALONE is not set ++CONFIG_CMA=y ++CONFIG_CMA_SIZE_MBYTES=320 ++CONFIG_IMX_WEIM=y ++CONFIG_CONNECTOR=y ++CONFIG_MTD=y ++CONFIG_MTD_CMDLINE_PARTS=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_CFI=y ++CONFIG_MTD_JEDECPROBE=y ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_CFI_AMDSTD=y ++CONFIG_MTD_CFI_STAA=y ++CONFIG_MTD_PHYSMAP_OF=y ++CONFIG_MTD_DATAFLASH=y ++CONFIG_MTD_M25P80=y ++CONFIG_MTD_SST25L=y ++CONFIG_MTD_NAND=y ++CONFIG_MTD_NAND_GPMI_NAND=y ++CONFIG_MTD_NAND_MXC=y ++CONFIG_MTD_UBI=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++CONFIG_EEPROM_AT24=y ++CONFIG_EEPROM_AT25=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_SCSI_MULTI_LUN=y ++CONFIG_SCSI_CONSTANTS=y ++CONFIG_SCSI_LOGGING=y ++CONFIG_SCSI_SCAN_ASYNC=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_ATA=y ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_AHCI_IMX=y ++CONFIG_PATA_IMX=y ++CONFIG_NETDEVICES=y ++# CONFIG_NET_VENDOR_BROADCOM is not set ++CONFIG_CS89x0=y ++CONFIG_CS89x0_PLATFORM=y ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++CONFIG_SMC91X=y ++CONFIG_SMC911X=y ++CONFIG_SMSC911X=y ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_WLAN is not set ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_EVDEV=y ++CONFIG_INPUT_EVBUG=m ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_IMX=y ++CONFIG_MOUSE_PS2=m ++CONFIG_MOUSE_PS2_ELANTECH=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_EGALAX=y ++CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH=y ++CONFIG_TOUCHSCREEN_MAX11801=y ++CONFIG_TOUCHSCREEN_MC13783=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_MMA8450=y ++CONFIG_INPUT_ISL29023=y ++CONFIG_SERIO_SERPORT=m ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_IMX=y ++CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_FSL_LPUART=y ++CONFIG_SERIAL_FSL_LPUART_CONSOLE=y ++CONFIG_FSL_OTP=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_ALGOPCF=m ++CONFIG_I2C_ALGOPCA=m ++CONFIG_I2C_IMX=y ++CONFIG_SPI=y ++CONFIG_SPI_IMX=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_SABRESD_MAX8903=y ++CONFIG_IMX6_USB_CHARGER=y ++CONFIG_SENSORS_MAG3110=y ++CONFIG_THERMAL=y ++CONFIG_CPU_THERMAL=y ++CONFIG_IMX_THERMAL=y ++CONFIG_DEVICE_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_IMX2_WDT=y ++CONFIG_MFD_DA9052_I2C=y ++CONFIG_MFD_MC13XXX_SPI=y ++CONFIG_MFD_MC13XXX_I2C=y ++CONFIG_MFD_SI476X_CORE=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_DA9052=y ++CONFIG_REGULATOR_ANATOP=y ++CONFIG_REGULATOR_MC13783=y ++CONFIG_REGULATOR_MC13892=y ++CONFIG_REGULATOR_PFUZE100=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_RADIO_SUPPORT=y ++CONFIG_VIDEO_V4L2_INT_DEVICE=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_MXC_OUTPUT=y ++CONFIG_VIDEO_MXC_CAPTURE=m ++CONFIG_VIDEO_MXC_CSI_CAMERA=m ++CONFIG_MXC_CAMERA_OV5640=m ++CONFIG_MXC_CAMERA_OV5642=m ++CONFIG_MXC_CAMERA_OV5640_MIPI=m ++CONFIG_MXC_TVIN_ADV7180=m ++CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m ++CONFIG_VIDEO_MXC_IPU_OUTPUT=y ++CONFIG_VIDEO_MXC_PXP_V4L2=y ++CONFIG_SOC_CAMERA=y ++CONFIG_VIDEO_MX3=y ++CONFIG_RADIO_SI476X=y ++CONFIG_SOC_CAMERA_OV2640=y ++CONFIG_DRM=y ++CONFIG_DRM_VIVANTE=y ++CONFIG_FB=y ++CONFIG_FB_MXS=y ++CONFIG_BACKLIGHT_LCD_SUPPORT=y ++CONFIG_LCD_CLASS_DEVICE=y ++CONFIG_LCD_L4F00242T03=y ++CONFIG_LCD_PLATFORM=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_FB_MXC_SYNC_PANEL=y ++CONFIG_FB_MXC_LDB=y ++CONFIG_FB_MXC_MIPI_DSI=y ++CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y ++CONFIG_FB_MXC_HDMI=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_LOGO=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_SOC=y ++CONFIG_SND_IMX_SOC=y ++CONFIG_SND_SOC_EUKREA_TLV320=y ++CONFIG_SND_SOC_IMX_CS42888=y ++CONFIG_SND_SOC_IMX_WM8962=y ++CONFIG_SND_SOC_IMX_SGTL5000=y ++CONFIG_SND_SOC_IMX_SPDIF=y ++CONFIG_SND_SOC_IMX_MC13783=y ++CONFIG_SND_SOC_IMX_HDMI=y ++CONFIG_SND_SOC_IMX_SI476X=y ++CONFIG_USB=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_CHIPIDEA=y ++CONFIG_USB_CHIPIDEA_UDC=y ++CONFIG_USB_CHIPIDEA_HOST=y ++CONFIG_USB_PHY=y ++CONFIG_NOP_USB_XCEIV=y ++CONFIG_USB_MXS_PHY=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_ZERO=m ++CONFIG_USB_ETH=m ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_ESDHC_IMX=y ++CONFIG_MXC_IPU=y ++CONFIG_MXC_GPU_VIV=y ++CONFIG_MXC_ASRC=y ++CONFIG_MXC_MIPI_CSI2=y ++CONFIG_MXC_MLB150=m ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGERS=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_INTF_DEV_UIE_EMUL=y ++CONFIG_RTC_DRV_MC13XXX=y ++CONFIG_RTC_DRV_MXC=y ++CONFIG_RTC_DRV_SNVS=y ++CONFIG_DMADEVICES=y ++CONFIG_MXC_PXP_V2=y ++CONFIG_IMX_SDMA=y ++CONFIG_MXS_DMA=y ++CONFIG_STAGING=y ++CONFIG_COMMON_CLK_DEBUG=y ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_PWM=y ++CONFIG_PWM_IMX=y ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++CONFIG_EXT2_FS_POSIX_ACL=y ++CONFIG_EXT2_FS_SECURITY=y ++CONFIG_EXT3_FS=y ++CONFIG_EXT3_FS_POSIX_ACL=y ++CONFIG_EXT3_FS_SECURITY=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_QUOTA=y ++CONFIG_QUOTA_NETLINK_INTERFACE=y ++# CONFIG_PRINT_QUOTA_WARNING is not set ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=m ++CONFIG_VFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_UBIFS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_NLS_DEFAULT="cp437" ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_UTF8=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_FTRACE is not set ++CONFIG_SECURITYFS=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_TEST=m ++CONFIG_CRYPTO_CCM=y ++CONFIG_CRYPTO_GCM=y ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=y ++CONFIG_CRYPTO_ECB=y ++CONFIG_CRYPTO_LRW=y ++CONFIG_CRYPTO_XTS=y ++CONFIG_CRYPTO_MD4=y ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_MICHAEL_MIC=y ++CONFIG_CRYPTO_RMD128=y ++CONFIG_CRYPTO_RMD160=y ++CONFIG_CRYPTO_RMD256=y ++CONFIG_CRYPTO_RMD320=y ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=y ++CONFIG_CRYPTO_SHA512=y ++CONFIG_CRYPTO_TGR192=y ++CONFIG_CRYPTO_WP512=y ++CONFIG_CRYPTO_BLOWFISH=y ++CONFIG_CRYPTO_CAMELLIA=y ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_TWOFISH=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++CONFIG_CRYPTO_DEV_FSL_CAAM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y ++CONFIG_CRC_CCITT=m ++CONFIG_CRC_T10DIF=y ++CONFIG_CRC7=m ++CONFIG_LIBCRC32C=m +diff -Nur linux-3.14.36/arch/arm/configs/imx_v7_mfg_defconfig linux-openelec/arch/arm/configs/imx_v7_mfg_defconfig +--- linux-3.14.36/arch/arm/configs/imx_v7_mfg_defconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/configs/imx_v7_mfg_defconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,341 @@ ++CONFIG_KERNEL_LZO=y ++CONFIG_SYSVIPC=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_LOG_BUF_SHIFT=18 ++CONFIG_CGROUPS=y ++CONFIG_RELAY=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EXPERT=y ++CONFIG_PERF_EVENTS=y ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++# CONFIG_BLK_DEV_BSG is not set ++CONFIG_GPIO_PCA953X=y ++CONFIG_ARCH_MXC=y ++CONFIG_MXC_DEBUG_BOARD=y ++CONFIG_MACH_IMX51_DT=y ++CONFIG_MACH_EUKREA_CPUIMX51SD=y ++CONFIG_SOC_IMX53=y ++CONFIG_SOC_IMX6Q=y ++CONFIG_SOC_IMX6SL=y ++CONFIG_SOC_VF610=y ++# CONFIG_SWP_EMULATE is not set ++CONFIG_SMP=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_PREEMPT_VOLUNTARY=y ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_ARM_IMX6_CPUFREQ=y ++CONFIG_CPU_IDLE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_BINFMT_MISC=m ++CONFIG_PM_RUNTIME=y ++CONFIG_PM_DEBUG=y ++CONFIG_PM_TEST_SUSPEND=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++CONFIG_IPV6=y ++CONFIG_NETFILTER=y ++CONFIG_VLAN_8021Q=y ++CONFIG_CFG80211=y ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++# CONFIG_STANDALONE is not set ++CONFIG_CMA=y ++CONFIG_CMA_SIZE_MBYTES=320 ++CONFIG_IMX_WEIM=y ++CONFIG_CONNECTOR=y ++CONFIG_MTD=y ++CONFIG_MTD_CMDLINE_PARTS=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_CFI=y ++CONFIG_MTD_JEDECPROBE=y ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_CFI_AMDSTD=y ++CONFIG_MTD_CFI_STAA=y ++CONFIG_MTD_PHYSMAP_OF=y ++CONFIG_MTD_DATAFLASH=y ++CONFIG_MTD_M25P80=y ++CONFIG_MTD_SST25L=y ++CONFIG_MTD_NAND=y ++CONFIG_MTD_NAND_GPMI_NAND=y ++CONFIG_MTD_NAND_MXC=y ++CONFIG_MTD_UBI=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++CONFIG_EEPROM_AT24=y ++CONFIG_EEPROM_AT25=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_SCSI_MULTI_LUN=y ++CONFIG_SCSI_CONSTANTS=y ++CONFIG_SCSI_LOGGING=y ++CONFIG_SCSI_SCAN_ASYNC=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_ATA=y ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_AHCI_IMX=y ++CONFIG_PATA_IMX=y ++CONFIG_NETDEVICES=y ++# CONFIG_NET_VENDOR_BROADCOM is not set ++CONFIG_CS89x0=y ++CONFIG_CS89x0_PLATFORM=y ++# CONFIG_NET_VENDOR_FARADAY is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++CONFIG_SMC91X=y ++CONFIG_SMC911X=y ++CONFIG_SMSC911X=y ++# CONFIG_NET_VENDOR_STMICRO is not set ++CONFIG_ATH_CARDS=y ++CONFIG_ATH6KL=m ++CONFIG_ATH6KL_SDIO=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_EVDEV=y ++CONFIG_INPUT_EVBUG=m ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_IMX=y ++CONFIG_MOUSE_PS2=m ++CONFIG_MOUSE_PS2_ELANTECH=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_EGALAX=y ++CONFIG_TOUCHSCREEN_ELAN=y ++CONFIG_TOUCHSCREEN_MAX11801=y ++CONFIG_TOUCHSCREEN_MC13783=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_MMA8450=y ++CONFIG_INPUT_ISL29023=y ++CONFIG_SERIO_SERPORT=m ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_IMX=y ++CONFIG_SERIAL_IMX_CONSOLE=y ++CONFIG_SERIAL_FSL_LPUART=y ++CONFIG_SERIAL_FSL_LPUART_CONSOLE=y ++CONFIG_FSL_OTP=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++# CONFIG_I2C_HELPER_AUTO is not set ++CONFIG_I2C_ALGOPCF=m ++CONFIG_I2C_ALGOPCA=m ++CONFIG_I2C_IMX=y ++CONFIG_SPI=y ++CONFIG_SPI_IMX=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_POWER_SUPPLY=y ++CONFIG_SABRESD_MAX8903=y ++CONFIG_SENSORS_MAX17135=y ++CONFIG_SENSORS_MAG3110=y ++CONFIG_THERMAL=y ++CONFIG_CPU_THERMAL=y ++CONFIG_IMX_THERMAL=y ++CONFIG_DEVICE_THERMAL=y ++CONFIG_WATCHDOG=y ++CONFIG_IMX2_WDT=y ++CONFIG_MFD_DA9052_I2C=y ++CONFIG_MFD_MC13XXX_SPI=y ++CONFIG_MFD_MC13XXX_I2C=y ++CONFIG_MFD_MAX17135=y ++CONFIG_MFD_SI476X_CORE=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_DA9052=y ++CONFIG_REGULATOR_ANATOP=y ++CONFIG_REGULATOR_MC13783=y ++CONFIG_REGULATOR_MC13892=y ++CONFIG_REGULATOR_MAX17135=y ++CONFIG_REGULATOR_PFUZE100=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++CONFIG_MEDIA_RADIO_SUPPORT=y ++CONFIG_VIDEO_V4L2_INT_DEVICE=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_MXC_OUTPUT=y ++CONFIG_VIDEO_MXC_CAPTURE=m ++CONFIG_VIDEO_MXC_CSI_CAMERA=m ++CONFIG_MXC_CAMERA_OV5640=m ++CONFIG_MXC_CAMERA_OV5642=m ++CONFIG_MXC_CAMERA_OV5640_MIPI=m ++CONFIG_MXC_TVIN_ADV7180=m ++CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m ++CONFIG_VIDEO_MXC_IPU_OUTPUT=y ++CONFIG_VIDEO_MXC_PXP_V4L2=y ++CONFIG_SOC_CAMERA=y ++CONFIG_VIDEO_MX3=y ++CONFIG_RADIO_SI476X=y ++CONFIG_SOC_CAMERA_OV2640=y ++CONFIG_DRM=y ++CONFIG_DRM_VIVANTE=y ++CONFIG_FB=y ++CONFIG_FB_MXS=y ++CONFIG_BACKLIGHT_LCD_SUPPORT=y ++CONFIG_LCD_CLASS_DEVICE=y ++CONFIG_LCD_L4F00242T03=y ++CONFIG_LCD_PLATFORM=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_PWM=y ++CONFIG_FB_MXC_SYNC_PANEL=y ++CONFIG_FB_MXC_LDB=y ++CONFIG_FB_MXC_MIPI_DSI=y ++CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y ++CONFIG_FB_MXC_HDMI=y ++CONFIG_FB_MXC_EINK_PANEL=y ++CONFIG_FB_MXS_SII902X=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_LOGO=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_SOC=y ++CONFIG_SND_IMX_SOC=y ++CONFIG_SND_SOC_EUKREA_TLV320=y ++CONFIG_SND_SOC_IMX_CS42888=y ++CONFIG_SND_SOC_IMX_WM8962=y ++CONFIG_SND_SOC_IMX_SGTL5000=y ++CONFIG_SND_SOC_IMX_SPDIF=y ++CONFIG_SND_SOC_IMX_MC13783=y ++CONFIG_SND_SOC_IMX_HDMI=y ++CONFIG_SND_SOC_IMX_SI476X=y ++CONFIG_USB=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_CHIPIDEA=y ++CONFIG_USB_CHIPIDEA_UDC=y ++CONFIG_USB_CHIPIDEA_HOST=y ++CONFIG_USB_PHY=y ++CONFIG_USB_MXS_PHY=y ++CONFIG_USB_GADGET=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_AUDIO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=y ++CONFIG_FSL_UTP=y ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_MIDI_GADGET is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_ESDHC_IMX=y ++CONFIG_MXC_IPU=y ++CONFIG_MXC_GPU_VIV=y ++CONFIG_MXC_ASRC=y ++CONFIG_MXC_MIPI_CSI2=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_INTF_DEV_UIE_EMUL=y ++CONFIG_RTC_DRV_MC13XXX=y ++CONFIG_RTC_DRV_MXC=y ++CONFIG_RTC_DRV_SNVS=y ++CONFIG_DMADEVICES=y ++CONFIG_MXC_PXP_V2=y ++CONFIG_IMX_SDMA=y ++CONFIG_MXS_DMA=y ++CONFIG_STAGING=y ++CONFIG_COMMON_CLK_DEBUG=y ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_PWM=y ++CONFIG_PWM_IMX=y ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++CONFIG_EXT2_FS_POSIX_ACL=y ++CONFIG_EXT2_FS_SECURITY=y ++CONFIG_EXT3_FS=y ++CONFIG_EXT3_FS_POSIX_ACL=y ++CONFIG_EXT3_FS_SECURITY=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_QUOTA=y ++CONFIG_QUOTA_NETLINK_INTERFACE=y ++# CONFIG_PRINT_QUOTA_WARNING is not set ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=m ++CONFIG_VFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_UBIFS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_NLS_DEFAULT="cp437" ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_UTF8=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_FTRACE is not set ++CONFIG_SECURITYFS=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_CCM=y ++CONFIG_CRYPTO_GCM=y ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=y ++CONFIG_CRYPTO_ECB=y ++CONFIG_CRYPTO_LRW=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++CONFIG_CRYPTO_DEV_FSL_CAAM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y ++CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y ++CONFIG_CRC_CCITT=m ++CONFIG_CRC_T10DIF=y ++CONFIG_CRC7=m ++CONFIG_LIBCRC32C=m +diff -Nur linux-3.14.36/arch/arm/include/asm/arch_timer.h linux-openelec/arch/arm/include/asm/arch_timer.h +--- linux-3.14.36/arch/arm/include/asm/arch_timer.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/arch_timer.h 2015-05-06 12:05:43.000000000 -0500 +@@ -107,7 +107,6 @@ + /* Also disable virtual event stream */ + cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN + | ARCH_TIMER_USR_VT_ACCESS_EN +- | ARCH_TIMER_VIRT_EVT_EN + | ARCH_TIMER_USR_VCT_ACCESS_EN + | ARCH_TIMER_USR_PCT_ACCESS_EN); + arch_timer_set_cntkctl(cntkctl); +diff -Nur linux-3.14.36/arch/arm/include/asm/atomic.h linux-openelec/arch/arm/include/asm/atomic.h +--- linux-3.14.36/arch/arm/include/asm/atomic.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/atomic.h 2015-05-06 12:05:43.000000000 -0500 +@@ -60,6 +60,7 @@ + int result; + + smp_mb(); ++ prefetchw(&v->counter); + + __asm__ __volatile__("@ atomic_add_return\n" + "1: ldrex %0, [%3]\n" +@@ -99,6 +100,7 @@ + int result; + + smp_mb(); ++ prefetchw(&v->counter); + + __asm__ __volatile__("@ atomic_sub_return\n" + "1: ldrex %0, [%3]\n" +@@ -121,6 +123,7 @@ + unsigned long res; + + smp_mb(); ++ prefetchw(&ptr->counter); + + do { + __asm__ __volatile__("@ atomic_cmpxchg\n" +@@ -138,6 +141,33 @@ + return oldval; + } + ++static inline int __atomic_add_unless(atomic_t *v, int a, int u) ++{ ++ int oldval, newval; ++ unsigned long tmp; ++ ++ smp_mb(); ++ prefetchw(&v->counter); ++ ++ __asm__ __volatile__ ("@ atomic_add_unless\n" ++"1: ldrex %0, [%4]\n" ++" teq %0, %5\n" ++" beq 2f\n" ++" add %1, %0, %6\n" ++" strex %2, %1, [%4]\n" ++" teq %2, #0\n" ++" bne 1b\n" ++"2:" ++ : "=&r" (oldval), "=&r" (newval), "=&r" (tmp), "+Qo" (v->counter) ++ : "r" (&v->counter), "r" (u), "r" (a) ++ : "cc"); ++ ++ if (oldval != u) ++ smp_mb(); ++ ++ return oldval; ++} ++ + #else /* ARM_ARCH_6 */ + + #ifdef CONFIG_SMP +@@ -186,10 +216,6 @@ + return ret; + } + +-#endif /* __LINUX_ARM_ARCH__ */ +- +-#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) +- + static inline int __atomic_add_unless(atomic_t *v, int a, int u) + { + int c, old; +@@ -200,6 +226,10 @@ + return c; + } + ++#endif /* __LINUX_ARM_ARCH__ */ ++ ++#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) ++ + #define atomic_inc(v) atomic_add(1, v) + #define atomic_dec(v) atomic_sub(1, v) + +@@ -299,6 +329,7 @@ + unsigned long tmp; + + smp_mb(); ++ prefetchw(&v->counter); + + __asm__ __volatile__("@ atomic64_add_return\n" + "1: ldrexd %0, %H0, [%3]\n" +@@ -340,6 +371,7 @@ + unsigned long tmp; + + smp_mb(); ++ prefetchw(&v->counter); + + __asm__ __volatile__("@ atomic64_sub_return\n" + "1: ldrexd %0, %H0, [%3]\n" +@@ -364,6 +396,7 @@ + unsigned long res; + + smp_mb(); ++ prefetchw(&ptr->counter); + + do { + __asm__ __volatile__("@ atomic64_cmpxchg\n" +@@ -388,6 +421,7 @@ + unsigned long tmp; + + smp_mb(); ++ prefetchw(&ptr->counter); + + __asm__ __volatile__("@ atomic64_xchg\n" + "1: ldrexd %0, %H0, [%3]\n" +@@ -409,6 +443,7 @@ + unsigned long tmp; + + smp_mb(); ++ prefetchw(&v->counter); + + __asm__ __volatile__("@ atomic64_dec_if_positive\n" + "1: ldrexd %0, %H0, [%3]\n" +@@ -436,6 +471,7 @@ + int ret = 1; + + smp_mb(); ++ prefetchw(&v->counter); + + __asm__ __volatile__("@ atomic64_add_unless\n" + "1: ldrexd %0, %H0, [%4]\n" +diff -Nur linux-3.14.36/arch/arm/include/asm/cmpxchg.h linux-openelec/arch/arm/include/asm/cmpxchg.h +--- linux-3.14.36/arch/arm/include/asm/cmpxchg.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/cmpxchg.h 2015-05-06 12:05:43.000000000 -0500 +@@ -2,6 +2,7 @@ + #define __ASM_ARM_CMPXCHG_H + + #include ++#include + #include + + #if defined(CONFIG_CPU_SA1100) || defined(CONFIG_CPU_SA110) +@@ -35,6 +36,7 @@ + #endif + + smp_mb(); ++ prefetchw((const void *)ptr); + + switch (size) { + #if __LINUX_ARM_ARCH__ >= 6 +@@ -138,6 +140,8 @@ + { + unsigned long oldval, res; + ++ prefetchw((const void *)ptr); ++ + switch (size) { + #ifndef CONFIG_CPU_V6 /* min ARCH >= ARMv6K */ + case 1: +@@ -230,6 +234,8 @@ + unsigned long long oldval; + unsigned long res; + ++ prefetchw(ptr); ++ + __asm__ __volatile__( + "1: ldrexd %1, %H1, [%3]\n" + " teq %1, %4\n" +diff -Nur linux-3.14.36/arch/arm/include/asm/ftrace.h linux-openelec/arch/arm/include/asm/ftrace.h +--- linux-3.14.36/arch/arm/include/asm/ftrace.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/ftrace.h 2015-05-06 12:05:43.000000000 -0500 +@@ -52,15 +52,7 @@ + + #endif + +-#define HAVE_ARCH_CALLER_ADDR +- +-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +-#define CALLER_ADDR1 ((unsigned long)return_address(1)) +-#define CALLER_ADDR2 ((unsigned long)return_address(2)) +-#define CALLER_ADDR3 ((unsigned long)return_address(3)) +-#define CALLER_ADDR4 ((unsigned long)return_address(4)) +-#define CALLER_ADDR5 ((unsigned long)return_address(5)) +-#define CALLER_ADDR6 ((unsigned long)return_address(6)) ++#define ftrace_return_address(n) return_address(n) + + #endif /* ifndef __ASSEMBLY__ */ + +diff -Nur linux-3.14.36/arch/arm/include/asm/futex.h linux-openelec/arch/arm/include/asm/futex.h +--- linux-3.14.36/arch/arm/include/asm/futex.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/futex.h 2015-05-06 12:05:43.000000000 -0500 +@@ -23,6 +23,7 @@ + + #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ + smp_mb(); \ ++ prefetchw(uaddr); \ + __asm__ __volatile__( \ + "1: ldrex %1, [%3]\n" \ + " " insn "\n" \ +@@ -46,6 +47,8 @@ + return -EFAULT; + + smp_mb(); ++ /* Prefetching cannot fault */ ++ prefetchw(uaddr); + __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" + "1: ldrex %1, [%4]\n" + " teq %1, %2\n" +diff -Nur linux-3.14.36/arch/arm/include/asm/glue-cache.h linux-openelec/arch/arm/include/asm/glue-cache.h +--- linux-3.14.36/arch/arm/include/asm/glue-cache.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/glue-cache.h 2015-05-06 12:05:43.000000000 -0500 +@@ -102,19 +102,19 @@ + #endif + + #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) +-# ifdef _CACHE ++//# ifdef _CACHE + # define MULTI_CACHE 1 +-# else +-# define _CACHE v6 +-# endif ++//# else ++//# define _CACHE v6 ++//# endif + #endif + + #if defined(CONFIG_CPU_V7) +-# ifdef _CACHE ++//# ifdef _CACHE + # define MULTI_CACHE 1 +-# else +-# define _CACHE v7 +-# endif ++//# else ++//# define _CACHE v7 ++//# endif + #endif + + #if defined(CONFIG_CPU_V7M) +diff -Nur linux-3.14.36/arch/arm/include/asm/hardware/cache-l2x0.h linux-openelec/arch/arm/include/asm/hardware/cache-l2x0.h +--- linux-3.14.36/arch/arm/include/asm/hardware/cache-l2x0.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/hardware/cache-l2x0.h 2015-05-06 12:05:43.000000000 -0500 +@@ -26,8 +26,8 @@ + #define L2X0_CACHE_TYPE 0x004 + #define L2X0_CTRL 0x100 + #define L2X0_AUX_CTRL 0x104 +-#define L2X0_TAG_LATENCY_CTRL 0x108 +-#define L2X0_DATA_LATENCY_CTRL 0x10C ++#define L310_TAG_LATENCY_CTRL 0x108 ++#define L310_DATA_LATENCY_CTRL 0x10C + #define L2X0_EVENT_CNT_CTRL 0x200 + #define L2X0_EVENT_CNT1_CFG 0x204 + #define L2X0_EVENT_CNT0_CFG 0x208 +@@ -54,53 +54,93 @@ + #define L2X0_LOCKDOWN_WAY_D_BASE 0x900 + #define L2X0_LOCKDOWN_WAY_I_BASE 0x904 + #define L2X0_LOCKDOWN_STRIDE 0x08 +-#define L2X0_ADDR_FILTER_START 0xC00 +-#define L2X0_ADDR_FILTER_END 0xC04 ++#define L310_ADDR_FILTER_START 0xC00 ++#define L310_ADDR_FILTER_END 0xC04 + #define L2X0_TEST_OPERATION 0xF00 + #define L2X0_LINE_DATA 0xF10 + #define L2X0_LINE_TAG 0xF30 + #define L2X0_DEBUG_CTRL 0xF40 +-#define L2X0_PREFETCH_CTRL 0xF60 +-#define L2X0_POWER_CTRL 0xF80 +-#define L2X0_DYNAMIC_CLK_GATING_EN (1 << 1) +-#define L2X0_STNDBY_MODE_EN (1 << 0) ++#define L310_PREFETCH_CTRL 0xF60 ++#define L310_POWER_CTRL 0xF80 ++#define L310_DYNAMIC_CLK_GATING_EN (1 << 1) ++#define L310_STNDBY_MODE_EN (1 << 0) + + /* Registers shifts and masks */ + #define L2X0_CACHE_ID_PART_MASK (0xf << 6) + #define L2X0_CACHE_ID_PART_L210 (1 << 6) ++#define L2X0_CACHE_ID_PART_L220 (2 << 6) + #define L2X0_CACHE_ID_PART_L310 (3 << 6) + #define L2X0_CACHE_ID_RTL_MASK 0x3f +-#define L2X0_CACHE_ID_RTL_R0P0 0x0 +-#define L2X0_CACHE_ID_RTL_R1P0 0x2 +-#define L2X0_CACHE_ID_RTL_R2P0 0x4 +-#define L2X0_CACHE_ID_RTL_R3P0 0x5 +-#define L2X0_CACHE_ID_RTL_R3P1 0x6 +-#define L2X0_CACHE_ID_RTL_R3P2 0x8 +- +-#define L2X0_AUX_CTRL_MASK 0xc0000fff ++#define L210_CACHE_ID_RTL_R0P2_02 0x00 ++#define L210_CACHE_ID_RTL_R0P1 0x01 ++#define L210_CACHE_ID_RTL_R0P2_01 0x02 ++#define L210_CACHE_ID_RTL_R0P3 0x03 ++#define L210_CACHE_ID_RTL_R0P4 0x0b ++#define L210_CACHE_ID_RTL_R0P5 0x0f ++#define L220_CACHE_ID_RTL_R1P7_01REL0 0x06 ++#define L310_CACHE_ID_RTL_R0P0 0x00 ++#define L310_CACHE_ID_RTL_R1P0 0x02 ++#define L310_CACHE_ID_RTL_R2P0 0x04 ++#define L310_CACHE_ID_RTL_R3P0 0x05 ++#define L310_CACHE_ID_RTL_R3P1 0x06 ++#define L310_CACHE_ID_RTL_R3P1_50REL0 0x07 ++#define L310_CACHE_ID_RTL_R3P2 0x08 ++#define L310_CACHE_ID_RTL_R3P3 0x09 ++ ++/* L2C auxiliary control register - bits common to L2C-210/220/310 */ ++#define L2C_AUX_CTRL_WAY_SIZE_SHIFT 17 ++#define L2C_AUX_CTRL_WAY_SIZE_MASK (7 << 17) ++#define L2C_AUX_CTRL_WAY_SIZE(n) ((n) << 17) ++#define L2C_AUX_CTRL_EVTMON_ENABLE BIT(20) ++#define L2C_AUX_CTRL_PARITY_ENABLE BIT(21) ++#define L2C_AUX_CTRL_SHARED_OVERRIDE BIT(22) ++/* L2C-210/220 common bits */ + #define L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT 0 +-#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK 0x7 ++#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK (7 << 0) + #define L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT 3 +-#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK (0x7 << 3) ++#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK (7 << 3) + #define L2X0_AUX_CTRL_TAG_LATENCY_SHIFT 6 +-#define L2X0_AUX_CTRL_TAG_LATENCY_MASK (0x7 << 6) ++#define L2X0_AUX_CTRL_TAG_LATENCY_MASK (7 << 6) + #define L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT 9 +-#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK (0x7 << 9) +-#define L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT 16 +-#define L2X0_AUX_CTRL_WAY_SIZE_SHIFT 17 +-#define L2X0_AUX_CTRL_WAY_SIZE_MASK (0x7 << 17) +-#define L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT 22 +-#define L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT 26 +-#define L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT 27 +-#define L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT 28 +-#define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT 29 +-#define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT 30 +- +-#define L2X0_LATENCY_CTRL_SETUP_SHIFT 0 +-#define L2X0_LATENCY_CTRL_RD_SHIFT 4 +-#define L2X0_LATENCY_CTRL_WR_SHIFT 8 +- +-#define L2X0_ADDR_FILTER_EN 1 ++#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK (7 << 9) ++#define L2X0_AUX_CTRL_ASSOC_SHIFT 13 ++#define L2X0_AUX_CTRL_ASSOC_MASK (15 << 13) ++/* L2C-210 specific bits */ ++#define L210_AUX_CTRL_WRAP_DISABLE BIT(12) ++#define L210_AUX_CTRL_WA_OVERRIDE BIT(23) ++#define L210_AUX_CTRL_EXCLUSIVE_ABORT BIT(24) ++/* L2C-220 specific bits */ ++#define L220_AUX_CTRL_EXCLUSIVE_CACHE BIT(12) ++#define L220_AUX_CTRL_FWA_SHIFT 23 ++#define L220_AUX_CTRL_FWA_MASK (3 << 23) ++#define L220_AUX_CTRL_NS_LOCKDOWN BIT(26) ++#define L220_AUX_CTRL_NS_INT_CTRL BIT(27) ++/* L2C-310 specific bits */ ++#define L310_AUX_CTRL_FULL_LINE_ZERO BIT(0) /* R2P0+ */ ++#define L310_AUX_CTRL_HIGHPRIO_SO_DEV BIT(10) /* R2P0+ */ ++#define L310_AUX_CTRL_STORE_LIMITATION BIT(11) /* R2P0+ */ ++#define L310_AUX_CTRL_EXCLUSIVE_CACHE BIT(12) ++#define L310_AUX_CTRL_ASSOCIATIVITY_16 BIT(16) ++#define L310_AUX_CTRL_CACHE_REPLACE_RR BIT(25) /* R2P0+ */ ++#define L310_AUX_CTRL_NS_LOCKDOWN BIT(26) ++#define L310_AUX_CTRL_NS_INT_CTRL BIT(27) ++#define L310_AUX_CTRL_DATA_PREFETCH BIT(28) ++#define L310_AUX_CTRL_INSTR_PREFETCH BIT(29) ++#define L310_AUX_CTRL_EARLY_BRESP BIT(30) /* R2P0+ */ ++ ++#define L310_LATENCY_CTRL_SETUP(n) ((n) << 0) ++#define L310_LATENCY_CTRL_RD(n) ((n) << 4) ++#define L310_LATENCY_CTRL_WR(n) ((n) << 8) ++ ++#define L310_ADDR_FILTER_EN 1 ++ ++#define L310_PREFETCH_CTRL_OFFSET_MASK 0x1f ++#define L310_PREFETCH_CTRL_DBL_LINEFILL_INCR BIT(23) ++#define L310_PREFETCH_CTRL_PREFETCH_DROP BIT(24) ++#define L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP BIT(27) ++#define L310_PREFETCH_CTRL_DATA_PREFETCH BIT(28) ++#define L310_PREFETCH_CTRL_INSTR_PREFETCH BIT(29) ++#define L310_PREFETCH_CTRL_DBL_LINEFILL BIT(30) + + #define L2X0_CTRL_EN 1 + +diff -Nur linux-3.14.36/arch/arm/include/asm/outercache.h linux-openelec/arch/arm/include/asm/outercache.h +--- linux-3.14.36/arch/arm/include/asm/outercache.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/outercache.h 2015-05-06 12:05:43.000000000 -0500 +@@ -21,6 +21,7 @@ + #ifndef __ASM_OUTERCACHE_H + #define __ASM_OUTERCACHE_H + ++#include + #include + + struct outer_cache_fns { +@@ -28,53 +29,84 @@ + void (*clean_range)(unsigned long, unsigned long); + void (*flush_range)(unsigned long, unsigned long); + void (*flush_all)(void); +- void (*inv_all)(void); + void (*disable)(void); + #ifdef CONFIG_OUTER_CACHE_SYNC + void (*sync)(void); + #endif +- void (*set_debug)(unsigned long); + void (*resume)(void); ++ ++ /* This is an ARM L2C thing */ ++ void (*write_sec)(unsigned long, unsigned); + }; + + extern struct outer_cache_fns outer_cache; + + #ifdef CONFIG_OUTER_CACHE +- ++/** ++ * outer_inv_range - invalidate range of outer cache lines ++ * @start: starting physical address, inclusive ++ * @end: end physical address, exclusive ++ */ + static inline void outer_inv_range(phys_addr_t start, phys_addr_t end) + { + if (outer_cache.inv_range) + outer_cache.inv_range(start, end); + } ++ ++/** ++ * outer_clean_range - clean dirty outer cache lines ++ * @start: starting physical address, inclusive ++ * @end: end physical address, exclusive ++ */ + static inline void outer_clean_range(phys_addr_t start, phys_addr_t end) + { + if (outer_cache.clean_range) + outer_cache.clean_range(start, end); + } ++ ++/** ++ * outer_flush_range - clean and invalidate outer cache lines ++ * @start: starting physical address, inclusive ++ * @end: end physical address, exclusive ++ */ + static inline void outer_flush_range(phys_addr_t start, phys_addr_t end) + { + if (outer_cache.flush_range) + outer_cache.flush_range(start, end); + } + ++/** ++ * outer_flush_all - clean and invalidate all cache lines in the outer cache ++ * ++ * Note: depending on implementation, this may not be atomic - it must ++ * only be called with interrupts disabled and no other active outer ++ * cache masters. ++ * ++ * It is intended that this function is only used by implementations ++ * needing to override the outer_cache.disable() method due to security. ++ * (Some implementations perform this as a clean followed by an invalidate.) ++ */ + static inline void outer_flush_all(void) + { + if (outer_cache.flush_all) + outer_cache.flush_all(); + } + +-static inline void outer_inv_all(void) +-{ +- if (outer_cache.inv_all) +- outer_cache.inv_all(); +-} +- +-static inline void outer_disable(void) +-{ +- if (outer_cache.disable) +- outer_cache.disable(); +-} +- ++/** ++ * outer_disable - clean, invalidate and disable the outer cache ++ * ++ * Disable the outer cache, ensuring that any data contained in the outer ++ * cache is pushed out to lower levels of system memory. The note and ++ * conditions above concerning outer_flush_all() applies here. ++ */ ++extern void outer_disable(void); ++ ++/** ++ * outer_resume - restore the cache configuration and re-enable outer cache ++ * ++ * Restore any configuration that the cache had when previously enabled, ++ * and re-enable the outer cache. ++ */ + static inline void outer_resume(void) + { + if (outer_cache.resume) +@@ -90,13 +122,18 @@ + static inline void outer_flush_range(phys_addr_t start, phys_addr_t end) + { } + static inline void outer_flush_all(void) { } +-static inline void outer_inv_all(void) { } + static inline void outer_disable(void) { } + static inline void outer_resume(void) { } + + #endif + + #ifdef CONFIG_OUTER_CACHE_SYNC ++/** ++ * outer_sync - perform a sync point for outer cache ++ * ++ * Ensure that all outer cache operations are complete and any store ++ * buffers are drained. ++ */ + static inline void outer_sync(void) + { + if (outer_cache.sync) +diff -Nur linux-3.14.36/arch/arm/include/asm/pmu.h linux-openelec/arch/arm/include/asm/pmu.h +--- linux-3.14.36/arch/arm/include/asm/pmu.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/pmu.h 2015-05-06 12:05:43.000000000 -0500 +@@ -62,9 +62,19 @@ + raw_spinlock_t pmu_lock; + }; + ++struct cpupmu_regs { ++ u32 pmc; ++ u32 pmcntenset; ++ u32 pmuseren; ++ u32 pmintenset; ++ u32 pmxevttype[8]; ++ u32 pmxevtcnt[8]; ++}; ++ + struct arm_pmu { + struct pmu pmu; + cpumask_t active_irqs; ++ cpumask_t valid_cpus; + char *name; + irqreturn_t (*handle_irq)(int irq_num, void *dev); + void (*enable)(struct perf_event *event); +@@ -81,6 +91,8 @@ + int (*request_irq)(struct arm_pmu *, irq_handler_t handler); + void (*free_irq)(struct arm_pmu *); + int (*map_event)(struct perf_event *event); ++ void (*save_regs)(struct arm_pmu *, struct cpupmu_regs *); ++ void (*restore_regs)(struct arm_pmu *, struct cpupmu_regs *); + int num_events; + atomic_t active_events; + struct mutex reserve_mutex; +diff -Nur linux-3.14.36/arch/arm/include/asm/psci.h linux-openelec/arch/arm/include/asm/psci.h +--- linux-3.14.36/arch/arm/include/asm/psci.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/psci.h 2015-05-06 12:05:43.000000000 -0500 +@@ -16,6 +16,10 @@ + + #define PSCI_POWER_STATE_TYPE_STANDBY 0 + #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 ++#define PSCI_POWER_STATE_AFFINITY_LEVEL0 0 ++#define PSCI_POWER_STATE_AFFINITY_LEVEL1 1 ++#define PSCI_POWER_STATE_AFFINITY_LEVEL2 2 ++#define PSCI_POWER_STATE_AFFINITY_LEVEL3 3 + + struct psci_power_state { + u16 id; +@@ -42,4 +46,12 @@ + static inline bool psci_smp_available(void) { return false; } + #endif + ++#ifdef CONFIG_ARM_PSCI ++extern int psci_probe(void); ++#else ++static inline int psci_probe(void) ++{ ++ return -ENODEV; ++} ++#endif + #endif /* __ASM_ARM_PSCI_H */ +diff -Nur linux-3.14.36/arch/arm/include/asm/topology.h linux-openelec/arch/arm/include/asm/topology.h +--- linux-3.14.36/arch/arm/include/asm/topology.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/include/asm/topology.h 2015-05-06 12:05:43.000000000 -0500 +@@ -26,11 +26,14 @@ + void init_cpu_topology(void); + void store_cpu_topology(unsigned int cpuid); + const struct cpumask *cpu_coregroup_mask(int cpu); ++int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask); + + #else + + static inline void init_cpu_topology(void) { } + static inline void store_cpu_topology(unsigned int cpuid) { } ++static inline int cluster_to_logical_mask(unsigned int socket_id, ++ cpumask_t *cluster_mask) { return -EINVAL; } + + #endif + +diff -Nur linux-3.14.36/arch/arm/Kconfig linux-openelec/arch/arm/Kconfig +--- linux-3.14.36/arch/arm/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/Kconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -1216,19 +1216,6 @@ + register of the Cortex-A9 which reduces the linefill issuing + capabilities of the processor. + +-config PL310_ERRATA_588369 +- bool "PL310 errata: Clean & Invalidate maintenance operations do not invalidate clean lines" +- depends on CACHE_L2X0 +- help +- The PL310 L2 cache controller implements three types of Clean & +- Invalidate maintenance operations: by Physical Address +- (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC). +- They are architecturally defined to behave as the execution of a +- clean operation followed immediately by an invalidate operation, +- both performing to the same memory location. This functionality +- is not correctly implemented in PL310 as clean lines are not +- invalidated as a result of these operations. +- + config ARM_ERRATA_643719 + bool "ARM errata: LoUIS bit field in CLIDR register is incorrect" + depends on CPU_V7 && SMP +@@ -1251,17 +1238,6 @@ + tables. The workaround changes the TLB flushing routines to invalidate + entries regardless of the ASID. + +-config PL310_ERRATA_727915 +- bool "PL310 errata: Background Clean & Invalidate by Way operation can cause data corruption" +- depends on CACHE_L2X0 +- help +- PL310 implements the Clean & Invalidate by Way L2 cache maintenance +- operation (offset 0x7FC). This operation runs in background so that +- PL310 can handle normal accesses while it is in progress. Under very +- rare circumstances, due to this erratum, write data can be lost when +- PL310 treats a cacheable write transaction during a Clean & +- Invalidate by Way operation. +- + config ARM_ERRATA_743622 + bool "ARM errata: Faulty hazard checking in the Store Buffer may lead to data corruption" + depends on CPU_V7 +@@ -1287,21 +1263,6 @@ + operation is received by a CPU before the ICIALLUIS has completed, + potentially leading to corrupted entries in the cache or TLB. + +-config PL310_ERRATA_753970 +- bool "PL310 errata: cache sync operation may be faulty" +- depends on CACHE_PL310 +- help +- This option enables the workaround for the 753970 PL310 (r3p0) erratum. +- +- Under some condition the effect of cache sync operation on +- the store buffer still remains when the operation completes. +- This means that the store buffer is always asked to drain and +- this prevents it from merging any further writes. The workaround +- is to replace the normal offset of cache sync operation (0x730) +- by another offset targeting an unmapped PL310 register 0x740. +- This has the same effect as the cache sync operation: store buffer +- drain and waiting for all buffers empty. +- + config ARM_ERRATA_754322 + bool "ARM errata: possible faulty MMU translations following an ASID switch" + depends on CPU_V7 +@@ -1350,18 +1311,6 @@ + relevant cache maintenance functions and sets a specific bit + in the diagnostic control register of the SCU. + +-config PL310_ERRATA_769419 +- bool "PL310 errata: no automatic Store Buffer drain" +- depends on CACHE_L2X0 +- help +- On revisions of the PL310 prior to r3p2, the Store Buffer does +- not automatically drain. This can cause normal, non-cacheable +- writes to be retained when the memory system is idle, leading +- to suboptimal I/O performance for drivers using coherent DMA. +- This option adds a write barrier to the cpu_idle loop so that, +- on systems with an outer cache, the store buffer is drained +- explicitly. +- + config ARM_ERRATA_775420 + bool "ARM errata: A data cache maintenance operation which aborts, might lead to deadlock" + depends on CPU_V7 +@@ -1391,6 +1340,29 @@ + loop buffer may deliver incorrect instructions. This + workaround disables the loop buffer to avoid the erratum. + ++config ARM_ERRATA_794072 ++ bool "ARM errata: A short loop including a DMB instruction might cause a denial of service" ++ depends on CPU_V7 && SMP ++ help ++ This option enables the workaround for the 794072 Cortex-A9 ++ (all revisions). A processor which continuously executes a short ++ loop containing a DMB instruction might prevent a CP15 operation ++ broadcast by another processor making further progress, causing ++ a denial of service. This erratum can be worked around by setting ++ bit[4] of the undocumented Diagnostic Control Register to 1. ++ ++config ARM_ERRATA_761320 ++ bool "Full cache line writes to the same memory region from at least two processors might deadlock processor" ++ depends on CPU_V7 && SMP ++ help ++ This option enables the workaround for the 761320 Cortex-A9 (r0..r3). ++ Under very rare circumstances, full cache line writes ++ from (at least) 2 processors on cache lines in hazard with ++ other requests may cause arbitration issues in the SCU, ++ leading to processor deadlock. This erratum can be ++ worked around by setting bit[21] of the undocumented ++ Diagnostic Control Register to 1. ++ + endmenu + + source "arch/arm/common/Kconfig" +@@ -1835,6 +1807,7 @@ + range 11 64 if ARCH_SHMOBILE_LEGACY + default "12" if SOC_AM33XX + default "9" if SA1111 || ARCH_EFM32 ++ default "14" if ARCH_MXC + default "11" + help + The kernel memory allocator divides physically contiguous memory +diff -Nur linux-3.14.36/arch/arm/kernel/perf_event.c linux-openelec/arch/arm/kernel/perf_event.c +--- linux-3.14.36/arch/arm/kernel/perf_event.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/kernel/perf_event.c 2015-05-06 12:05:43.000000000 -0500 +@@ -12,6 +12,7 @@ + */ + #define pr_fmt(fmt) "hw perfevents: " fmt + ++#include + #include + #include + #include +@@ -86,6 +87,9 @@ + return armpmu_map_cache_event(cache_map, config); + case PERF_TYPE_RAW: + return armpmu_map_raw_event(raw_event_mask, config); ++ default: ++ if (event->attr.type >= PERF_TYPE_MAX) ++ return armpmu_map_raw_event(raw_event_mask, config); + } + + return -ENOENT; +@@ -159,6 +163,8 @@ + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + ++ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) ++ return; + /* + * ARM pmu always has to update the counter, so ignore + * PERF_EF_UPDATE, see comments in armpmu_start(). +@@ -175,6 +181,8 @@ + struct arm_pmu *armpmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + ++ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) ++ return; + /* + * ARM pmu always has to reprogram the period, so ignore + * PERF_EF_RELOAD, see the comment below. +@@ -202,6 +210,9 @@ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + ++ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) ++ return; ++ + armpmu_stop(event, PERF_EF_UPDATE); + hw_events->events[idx] = NULL; + clear_bit(idx, hw_events->used_mask); +@@ -218,6 +229,10 @@ + int idx; + int err = 0; + ++ /* An event following a process won't be stopped earlier */ ++ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) ++ return 0; ++ + perf_pmu_disable(event->pmu); + + /* If we don't have a space for the counter then finish early. */ +@@ -419,6 +434,10 @@ + int err = 0; + atomic_t *active_events = &armpmu->active_events; + ++ if (event->cpu != -1 && ++ !cpumask_test_cpu(event->cpu, &armpmu->valid_cpus)) ++ return -ENOENT; ++ + /* does not support taken branch sampling */ + if (has_branch_stack(event)) + return -EOPNOTSUPP; +diff -Nur linux-3.14.36/arch/arm/kernel/perf_event_cpu.c linux-openelec/arch/arm/kernel/perf_event_cpu.c +--- linux-3.14.36/arch/arm/kernel/perf_event_cpu.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/kernel/perf_event_cpu.c 2015-05-06 12:05:43.000000000 -0500 +@@ -19,6 +19,7 @@ + #define pr_fmt(fmt) "CPU PMU: " fmt + + #include ++#include + #include + #include + #include +@@ -31,33 +32,36 @@ + #include + + /* Set at runtime when we know what CPU type we are. */ +-static struct arm_pmu *cpu_pmu; ++static DEFINE_PER_CPU(struct arm_pmu *, cpu_pmu); + + static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); + static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); + static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); + ++static DEFINE_PER_CPU(struct cpupmu_regs, cpu_pmu_regs); ++ + /* + * Despite the names, these two functions are CPU-specific and are used + * by the OProfile/perf code. + */ + const char *perf_pmu_name(void) + { +- if (!cpu_pmu) ++ struct arm_pmu *pmu = per_cpu(cpu_pmu, 0); ++ if (!pmu) + return NULL; + +- return cpu_pmu->name; ++ return pmu->name; + } + EXPORT_SYMBOL_GPL(perf_pmu_name); + + int perf_num_counters(void) + { +- int max_events = 0; ++ struct arm_pmu *pmu = per_cpu(cpu_pmu, 0); + +- if (cpu_pmu != NULL) +- max_events = cpu_pmu->num_events; ++ if (!pmu) ++ return 0; + +- return max_events; ++ return pmu->num_events; + } + EXPORT_SYMBOL_GPL(perf_num_counters); + +@@ -75,11 +79,13 @@ + { + int i, irq, irqs; + struct platform_device *pmu_device = cpu_pmu->plat_device; ++ int cpu = -1; + + irqs = min(pmu_device->num_resources, num_possible_cpus()); + + for (i = 0; i < irqs; ++i) { +- if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) ++ cpu = cpumask_next(cpu, &cpu_pmu->valid_cpus); ++ if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs)) + continue; + irq = platform_get_irq(pmu_device, i); + if (irq >= 0) +@@ -91,6 +97,7 @@ + { + int i, err, irq, irqs; + struct platform_device *pmu_device = cpu_pmu->plat_device; ++ int cpu = -1; + + if (!pmu_device) + return -ENODEV; +@@ -103,6 +110,7 @@ + + for (i = 0; i < irqs; ++i) { + err = 0; ++ cpu = cpumask_next(cpu, &cpu_pmu->valid_cpus); + irq = platform_get_irq(pmu_device, i); + if (irq < 0) + continue; +@@ -112,7 +120,7 @@ + * assume that we're running on a uniprocessor machine and + * continue. Otherwise, continue without this interrupt. + */ +- if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { ++ if (irq_set_affinity(irq, cpumask_of(cpu)) && irqs > 1) { + pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", + irq, i); + continue; +@@ -127,7 +135,7 @@ + return err; + } + +- cpumask_set_cpu(i, &cpu_pmu->active_irqs); ++ cpumask_set_cpu(cpu, &cpu_pmu->active_irqs); + } + + return 0; +@@ -136,7 +144,7 @@ + static void cpu_pmu_init(struct arm_pmu *cpu_pmu) + { + int cpu; +- for_each_possible_cpu(cpu) { ++ for_each_cpu_mask(cpu, cpu_pmu->valid_cpus) { + struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); + events->events = per_cpu(hw_events, cpu); + events->used_mask = per_cpu(used_mask, cpu); +@@ -149,7 +157,7 @@ + + /* Ensure the PMU has sane values out of reset. */ + if (cpu_pmu->reset) +- on_each_cpu(cpu_pmu->reset, cpu_pmu, 1); ++ on_each_cpu_mask(&cpu_pmu->valid_cpus, cpu_pmu->reset, cpu_pmu, 1); + } + + /* +@@ -161,21 +169,46 @@ + static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, + void *hcpu) + { ++ struct arm_pmu *pmu = per_cpu(cpu_pmu, (long)hcpu); ++ + if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) + return NOTIFY_DONE; + +- if (cpu_pmu && cpu_pmu->reset) +- cpu_pmu->reset(cpu_pmu); ++ if (pmu && pmu->reset) ++ pmu->reset(pmu); + else + return NOTIFY_DONE; + + return NOTIFY_OK; + } + ++static int cpu_pmu_pm_notify(struct notifier_block *b, ++ unsigned long action, void *hcpu) ++{ ++ int cpu = smp_processor_id(); ++ struct arm_pmu *pmu = per_cpu(cpu_pmu, cpu); ++ struct cpupmu_regs *pmuregs = &per_cpu(cpu_pmu_regs, cpu); ++ ++ if (!pmu) ++ return NOTIFY_DONE; ++ ++ if (action == CPU_PM_ENTER && pmu->save_regs) { ++ pmu->save_regs(pmu, pmuregs); ++ } else if (action == CPU_PM_EXIT && pmu->restore_regs) { ++ pmu->restore_regs(pmu, pmuregs); ++ } ++ ++ return NOTIFY_OK; ++} ++ + static struct notifier_block cpu_pmu_hotplug_notifier = { + .notifier_call = cpu_pmu_notify, + }; + ++static struct notifier_block cpu_pmu_pm_notifier = { ++ .notifier_call = cpu_pmu_pm_notify, ++}; ++ + /* + * PMU platform driver and devicetree bindings. + */ +@@ -247,6 +280,9 @@ + } + } + ++ /* assume PMU support all the CPUs in this case */ ++ cpumask_setall(&pmu->valid_cpus); ++ + put_cpu(); + return ret; + } +@@ -254,15 +290,10 @@ + static int cpu_pmu_device_probe(struct platform_device *pdev) + { + const struct of_device_id *of_id; +- const int (*init_fn)(struct arm_pmu *); + struct device_node *node = pdev->dev.of_node; + struct arm_pmu *pmu; +- int ret = -ENODEV; +- +- if (cpu_pmu) { +- pr_info("attempt to register multiple PMU devices!"); +- return -ENOSPC; +- } ++ int ret = 0; ++ int cpu; + + pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL); + if (!pmu) { +@@ -271,8 +302,28 @@ + } + + if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) { +- init_fn = of_id->data; +- ret = init_fn(pmu); ++ smp_call_func_t init_fn = (smp_call_func_t)of_id->data; ++ struct device_node *ncluster; ++ int cluster = -1; ++ cpumask_t sibling_mask; ++ ++ ncluster = of_parse_phandle(node, "cluster", 0); ++ if (ncluster) { ++ int len; ++ const u32 *hwid; ++ hwid = of_get_property(ncluster, "reg", &len); ++ if (hwid && len == 4) ++ cluster = be32_to_cpup(hwid); ++ } ++ /* set sibling mask to all cpu mask if socket is not specified */ ++ if (cluster == -1 || ++ cluster_to_logical_mask(cluster, &sibling_mask)) ++ cpumask_setall(&sibling_mask); ++ ++ smp_call_function_any(&sibling_mask, init_fn, pmu, 1); ++ ++ /* now set the valid_cpus after init */ ++ cpumask_copy(&pmu->valid_cpus, &sibling_mask); + } else { + ret = probe_current_pmu(pmu); + } +@@ -282,10 +333,12 @@ + goto out_free; + } + +- cpu_pmu = pmu; +- cpu_pmu->plat_device = pdev; +- cpu_pmu_init(cpu_pmu); +- ret = armpmu_register(cpu_pmu, PERF_TYPE_RAW); ++ for_each_cpu_mask(cpu, pmu->valid_cpus) ++ per_cpu(cpu_pmu, cpu) = pmu; ++ ++ pmu->plat_device = pdev; ++ cpu_pmu_init(pmu); ++ ret = armpmu_register(pmu, -1); + + if (!ret) + return 0; +@@ -314,9 +367,17 @@ + if (err) + return err; + ++ err = cpu_pm_register_notifier(&cpu_pmu_pm_notifier); ++ if (err) { ++ unregister_cpu_notifier(&cpu_pmu_hotplug_notifier); ++ return err; ++ } ++ + err = platform_driver_register(&cpu_pmu_driver); +- if (err) ++ if (err) { ++ cpu_pm_unregister_notifier(&cpu_pmu_pm_notifier); + unregister_cpu_notifier(&cpu_pmu_hotplug_notifier); ++ } + + return err; + } +diff -Nur linux-3.14.36/arch/arm/kernel/perf_event_v7.c linux-openelec/arch/arm/kernel/perf_event_v7.c +--- linux-3.14.36/arch/arm/kernel/perf_event_v7.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/kernel/perf_event_v7.c 2015-05-06 12:05:43.000000000 -0500 +@@ -950,6 +950,51 @@ + } + #endif + ++static void armv7pmu_save_regs(struct arm_pmu *cpu_pmu, ++ struct cpupmu_regs *regs) ++{ ++ unsigned int cnt; ++ asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (regs->pmc)); ++ if (!(regs->pmc & ARMV7_PMNC_E)) ++ return; ++ ++ asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (regs->pmcntenset)); ++ asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r" (regs->pmuseren)); ++ asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (regs->pmintenset)); ++ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (regs->pmxevtcnt[0])); ++ for (cnt = ARMV7_IDX_COUNTER0; ++ cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) { ++ armv7_pmnc_select_counter(cnt); ++ asm volatile("mrc p15, 0, %0, c9, c13, 1" ++ : "=r"(regs->pmxevttype[cnt])); ++ asm volatile("mrc p15, 0, %0, c9, c13, 2" ++ : "=r"(regs->pmxevtcnt[cnt])); ++ } ++ return; ++} ++ ++static void armv7pmu_restore_regs(struct arm_pmu *cpu_pmu, ++ struct cpupmu_regs *regs) ++{ ++ unsigned int cnt; ++ if (!(regs->pmc & ARMV7_PMNC_E)) ++ return; ++ ++ asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (regs->pmcntenset)); ++ asm volatile("mcr p15, 0, %0, c9, c14, 0" : : "r" (regs->pmuseren)); ++ asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (regs->pmintenset)); ++ asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (regs->pmxevtcnt[0])); ++ for (cnt = ARMV7_IDX_COUNTER0; ++ cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) { ++ armv7_pmnc_select_counter(cnt); ++ asm volatile("mcr p15, 0, %0, c9, c13, 1" ++ : : "r"(regs->pmxevttype[cnt])); ++ asm volatile("mcr p15, 0, %0, c9, c13, 2" ++ : : "r"(regs->pmxevtcnt[cnt])); ++ } ++ asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (regs->pmc)); ++} ++ + static void armv7pmu_enable_event(struct perf_event *event) + { + unsigned long flags; +@@ -1223,6 +1268,8 @@ + cpu_pmu->start = armv7pmu_start; + cpu_pmu->stop = armv7pmu_stop; + cpu_pmu->reset = armv7pmu_reset; ++ cpu_pmu->save_regs = armv7pmu_save_regs; ++ cpu_pmu->restore_regs = armv7pmu_restore_regs; + cpu_pmu->max_period = (1LLU << 32) - 1; + }; + +@@ -1240,7 +1287,7 @@ + static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) + { + armv7pmu_init(cpu_pmu); +- cpu_pmu->name = "ARMv7 Cortex-A8"; ++ cpu_pmu->name = "ARMv7_Cortex_A8"; + cpu_pmu->map_event = armv7_a8_map_event; + cpu_pmu->num_events = armv7_read_num_pmnc_events(); + return 0; +@@ -1249,7 +1296,7 @@ + static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) + { + armv7pmu_init(cpu_pmu); +- cpu_pmu->name = "ARMv7 Cortex-A9"; ++ cpu_pmu->name = "ARMv7_Cortex_A9"; + cpu_pmu->map_event = armv7_a9_map_event; + cpu_pmu->num_events = armv7_read_num_pmnc_events(); + return 0; +@@ -1258,7 +1305,7 @@ + static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) + { + armv7pmu_init(cpu_pmu); +- cpu_pmu->name = "ARMv7 Cortex-A5"; ++ cpu_pmu->name = "ARMv7_Cortex_A5"; + cpu_pmu->map_event = armv7_a5_map_event; + cpu_pmu->num_events = armv7_read_num_pmnc_events(); + return 0; +@@ -1267,7 +1314,7 @@ + static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu) + { + armv7pmu_init(cpu_pmu); +- cpu_pmu->name = "ARMv7 Cortex-A15"; ++ cpu_pmu->name = "ARMv7_Cortex_A15"; + cpu_pmu->map_event = armv7_a15_map_event; + cpu_pmu->num_events = armv7_read_num_pmnc_events(); + cpu_pmu->set_event_filter = armv7pmu_set_event_filter; +@@ -1277,7 +1324,7 @@ + static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu) + { + armv7pmu_init(cpu_pmu); +- cpu_pmu->name = "ARMv7 Cortex-A7"; ++ cpu_pmu->name = "ARMv7_Cortex_A7"; + cpu_pmu->map_event = armv7_a7_map_event; + cpu_pmu->num_events = armv7_read_num_pmnc_events(); + cpu_pmu->set_event_filter = armv7pmu_set_event_filter; +diff -Nur linux-3.14.36/arch/arm/kernel/process.c linux-openelec/arch/arm/kernel/process.c +--- linux-3.14.36/arch/arm/kernel/process.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/kernel/process.c 2015-07-24 18:03:28.436842002 -0500 +@@ -172,8 +172,10 @@ + */ + void arch_cpu_idle(void) + { ++ idle_notifier_call_chain(IDLE_START); + if (cpuidle_idle_call()) + default_idle(); ++ idle_notifier_call_chain(IDLE_END); + } + + /* +diff -Nur linux-3.14.36/arch/arm/kernel/process.c.orig linux-openelec/arch/arm/kernel/process.c.orig +--- linux-3.14.36/arch/arm/kernel/process.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/kernel/process.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,514 @@ ++/* ++ * linux/arch/arm/kernel/process.c ++ * ++ * Copyright (C) 1996-2000 Russell King - Converted to ARM. ++ * Original Copyright (C) 1995 Linus Torvalds ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_CC_STACKPROTECTOR ++#include ++unsigned long __stack_chk_guard __read_mostly; ++EXPORT_SYMBOL(__stack_chk_guard); ++#endif ++ ++static const char *processor_modes[] = { ++ "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" , ++ "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26", ++ "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" , ++ "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32" ++}; ++ ++static const char *isa_modes[] = { ++ "ARM" , "Thumb" , "Jazelle", "ThumbEE" ++}; ++ ++extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); ++typedef void (*phys_reset_t)(unsigned long); ++ ++/* ++ * A temporary stack to use for CPU reset. This is static so that we ++ * don't clobber it with the identity mapping. When running with this ++ * stack, any references to the current task *will not work* so you ++ * should really do as little as possible before jumping to your reset ++ * code. ++ */ ++static u64 soft_restart_stack[16]; ++ ++static void __soft_restart(void *addr) ++{ ++ phys_reset_t phys_reset; ++ ++ /* Take out a flat memory mapping. */ ++ setup_mm_for_reboot(); ++ ++ /* Clean and invalidate caches */ ++ flush_cache_all(); ++ ++ /* Turn off caching */ ++ cpu_proc_fin(); ++ ++ /* Push out any further dirty data, and ensure cache is empty */ ++ flush_cache_all(); ++ ++ /* Switch to the identity mapping. */ ++ phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset); ++ phys_reset((unsigned long)addr); ++ ++ /* Should never get here. */ ++ BUG(); ++} ++ ++void soft_restart(unsigned long addr) ++{ ++ u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack); ++ ++ /* Disable interrupts first */ ++ local_irq_disable(); ++ local_fiq_disable(); ++ ++ /* Disable the L2 if we're the last man standing. */ ++ if (num_online_cpus() == 1) ++ outer_disable(); ++ ++ /* Change to the new stack and continue with the reset. */ ++ call_with_stack(__soft_restart, (void *)addr, (void *)stack); ++ ++ /* Should never get here. */ ++ BUG(); ++} ++ ++static void null_restart(enum reboot_mode reboot_mode, const char *cmd) ++{ ++} ++ ++/* ++ * Function pointers to optional machine specific functions ++ */ ++void (*pm_power_off)(void); ++EXPORT_SYMBOL(pm_power_off); ++ ++void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd) = null_restart; ++EXPORT_SYMBOL_GPL(arm_pm_restart); ++ ++/* ++ * This is our default idle handler. ++ */ ++ ++void (*arm_pm_idle)(void); ++ ++static void default_idle(void) ++{ ++ if (arm_pm_idle) ++ arm_pm_idle(); ++ else ++ cpu_do_idle(); ++ local_irq_enable(); ++} ++ ++void arch_cpu_idle_prepare(void) ++{ ++ local_fiq_enable(); ++} ++ ++void arch_cpu_idle_enter(void) ++{ ++ ledtrig_cpu(CPU_LED_IDLE_START); ++#ifdef CONFIG_PL310_ERRATA_769419 ++ wmb(); ++#endif ++} ++ ++void arch_cpu_idle_exit(void) ++{ ++ ledtrig_cpu(CPU_LED_IDLE_END); ++} ++ ++#ifdef CONFIG_HOTPLUG_CPU ++void arch_cpu_idle_dead(void) ++{ ++ cpu_die(); ++} ++#endif ++ ++/* ++ * Called from the core idle loop. ++ */ ++void arch_cpu_idle(void) ++{ ++ idle_notifier_call_chain(IDLE_START); ++ if (cpuidle_idle_call()) ++ default_idle(); ++ idle_notifier_call_chain(IDLE_END); ++} ++ ++/* ++ * Called by kexec, immediately prior to machine_kexec(). ++ * ++ * This must completely disable all secondary CPUs; simply causing those CPUs ++ * to execute e.g. a RAM-based pin loop is not sufficient. This allows the ++ * kexec'd kernel to use any and all RAM as it sees fit, without having to ++ * avoid any code or data used by any SW CPU pin loop. The CPU hotplug ++ * functionality embodied in disable_nonboot_cpus() to achieve this. ++ */ ++void machine_shutdown(void) ++{ ++ disable_nonboot_cpus(); ++} ++ ++/* ++ * Halting simply requires that the secondary CPUs stop performing any ++ * activity (executing tasks, handling interrupts). smp_send_stop() ++ * achieves this. ++ */ ++void machine_halt(void) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ ++ local_irq_disable(); ++ while (1); ++} ++ ++/* ++ * Power-off simply requires that the secondary CPUs stop performing any ++ * activity (executing tasks, handling interrupts). smp_send_stop() ++ * achieves this. When the system power is turned off, it will take all CPUs ++ * with it. ++ */ ++void machine_power_off(void) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ ++ if (pm_power_off) ++ pm_power_off(); ++} ++ ++/* ++ * Restart requires that the secondary CPUs stop performing any activity ++ * while the primary CPU resets the system. Systems with a single CPU can ++ * use soft_restart() as their machine descriptor's .restart hook, since that ++ * will cause the only available CPU to reset. Systems with multiple CPUs must ++ * provide a HW restart implementation, to ensure that all CPUs reset at once. ++ * This is required so that any code running after reset on the primary CPU ++ * doesn't have to co-ordinate with other CPUs to ensure they aren't still ++ * executing pre-reset code, and using RAM that the primary CPU's code wishes ++ * to use. Implementing such co-ordination would be essentially impossible. ++ */ ++void machine_restart(char *cmd) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ ++ arm_pm_restart(reboot_mode, cmd); ++ ++ /* Give a grace period for failure to restart of 1s */ ++ mdelay(1000); ++ ++ /* Whoops - the platform was unable to reboot. Tell the user! */ ++ printk("Reboot failed -- System halted\n"); ++ local_irq_disable(); ++ while (1); ++} ++ ++void __show_regs(struct pt_regs *regs) ++{ ++ unsigned long flags; ++ char buf[64]; ++ ++ show_regs_print_info(KERN_DEFAULT); ++ ++ print_symbol("PC is at %s\n", instruction_pointer(regs)); ++ print_symbol("LR is at %s\n", regs->ARM_lr); ++ printk("pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n" ++ "sp : %08lx ip : %08lx fp : %08lx\n", ++ regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr, ++ regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); ++ printk("r10: %08lx r9 : %08lx r8 : %08lx\n", ++ regs->ARM_r10, regs->ARM_r9, ++ regs->ARM_r8); ++ printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", ++ regs->ARM_r7, regs->ARM_r6, ++ regs->ARM_r5, regs->ARM_r4); ++ printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", ++ regs->ARM_r3, regs->ARM_r2, ++ regs->ARM_r1, regs->ARM_r0); ++ ++ flags = regs->ARM_cpsr; ++ buf[0] = flags & PSR_N_BIT ? 'N' : 'n'; ++ buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z'; ++ buf[2] = flags & PSR_C_BIT ? 'C' : 'c'; ++ buf[3] = flags & PSR_V_BIT ? 'V' : 'v'; ++ buf[4] = '\0'; ++ ++ printk("Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s\n", ++ buf, interrupts_enabled(regs) ? "n" : "ff", ++ fast_interrupts_enabled(regs) ? "n" : "ff", ++ processor_modes[processor_mode(regs)], ++ isa_modes[isa_mode(regs)], ++ get_fs() == get_ds() ? "kernel" : "user"); ++#ifdef CONFIG_CPU_CP15 ++ { ++ unsigned int ctrl; ++ ++ buf[0] = '\0'; ++#ifdef CONFIG_CPU_CP15_MMU ++ { ++ unsigned int transbase, dac; ++ asm("mrc p15, 0, %0, c2, c0\n\t" ++ "mrc p15, 0, %1, c3, c0\n" ++ : "=r" (transbase), "=r" (dac)); ++ snprintf(buf, sizeof(buf), " Table: %08x DAC: %08x", ++ transbase, dac); ++ } ++#endif ++ asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl)); ++ ++ printk("Control: %08x%s\n", ctrl, buf); ++ } ++#endif ++} ++ ++void show_regs(struct pt_regs * regs) ++{ ++ printk("\n"); ++ __show_regs(regs); ++ dump_stack(); ++} ++ ++ATOMIC_NOTIFIER_HEAD(thread_notify_head); ++ ++EXPORT_SYMBOL_GPL(thread_notify_head); ++ ++/* ++ * Free current thread data structures etc.. ++ */ ++void exit_thread(void) ++{ ++ thread_notify(THREAD_NOTIFY_EXIT, current_thread_info()); ++} ++ ++void flush_thread(void) ++{ ++ struct thread_info *thread = current_thread_info(); ++ struct task_struct *tsk = current; ++ ++ flush_ptrace_hw_breakpoint(tsk); ++ ++ memset(thread->used_cp, 0, sizeof(thread->used_cp)); ++ memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); ++ memset(&thread->fpstate, 0, sizeof(union fp_state)); ++ ++ thread_notify(THREAD_NOTIFY_FLUSH, thread); ++} ++ ++void release_thread(struct task_struct *dead_task) ++{ ++} ++ ++asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); ++ ++int ++copy_thread(unsigned long clone_flags, unsigned long stack_start, ++ unsigned long stk_sz, struct task_struct *p) ++{ ++ struct thread_info *thread = task_thread_info(p); ++ struct pt_regs *childregs = task_pt_regs(p); ++ ++ memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); ++ ++ if (likely(!(p->flags & PF_KTHREAD))) { ++ *childregs = *current_pt_regs(); ++ childregs->ARM_r0 = 0; ++ if (stack_start) ++ childregs->ARM_sp = stack_start; ++ } else { ++ memset(childregs, 0, sizeof(struct pt_regs)); ++ thread->cpu_context.r4 = stk_sz; ++ thread->cpu_context.r5 = stack_start; ++ childregs->ARM_cpsr = SVC_MODE; ++ } ++ thread->cpu_context.pc = (unsigned long)ret_from_fork; ++ thread->cpu_context.sp = (unsigned long)childregs; ++ ++ clear_ptrace_hw_breakpoint(p); ++ ++ if (clone_flags & CLONE_SETTLS) ++ thread->tp_value[0] = childregs->ARM_r3; ++ thread->tp_value[1] = get_tpuser(); ++ ++ thread_notify(THREAD_NOTIFY_COPY, thread); ++ ++ return 0; ++} ++ ++/* ++ * Fill in the task's elfregs structure for a core dump. ++ */ ++int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs) ++{ ++ elf_core_copy_regs(elfregs, task_pt_regs(t)); ++ return 1; ++} ++ ++/* ++ * fill in the fpe structure for a core dump... ++ */ ++int dump_fpu (struct pt_regs *regs, struct user_fp *fp) ++{ ++ struct thread_info *thread = current_thread_info(); ++ int used_math = thread->used_cp[1] | thread->used_cp[2]; ++ ++ if (used_math) ++ memcpy(fp, &thread->fpstate.soft, sizeof (*fp)); ++ ++ return used_math != 0; ++} ++EXPORT_SYMBOL(dump_fpu); ++ ++unsigned long get_wchan(struct task_struct *p) ++{ ++ struct stackframe frame; ++ unsigned long stack_page; ++ int count = 0; ++ if (!p || p == current || p->state == TASK_RUNNING) ++ return 0; ++ ++ frame.fp = thread_saved_fp(p); ++ frame.sp = thread_saved_sp(p); ++ frame.lr = 0; /* recovered from the stack */ ++ frame.pc = thread_saved_pc(p); ++ stack_page = (unsigned long)task_stack_page(p); ++ do { ++ if (frame.sp < stack_page || ++ frame.sp >= stack_page + THREAD_SIZE || ++ unwind_frame(&frame) < 0) ++ return 0; ++ if (!in_sched_functions(frame.pc)) ++ return frame.pc; ++ } while (count ++ < 16); ++ return 0; ++} ++ ++unsigned long arch_randomize_brk(struct mm_struct *mm) ++{ ++ unsigned long range_end = mm->brk + 0x02000000; ++ return randomize_range(mm->brk, range_end, 0) ? : mm->brk; ++} ++ ++#ifdef CONFIG_MMU ++#ifdef CONFIG_KUSER_HELPERS ++/* ++ * The vectors page is always readable from user space for the ++ * atomic helpers. Insert it into the gate_vma so that it is visible ++ * through ptrace and /proc//mem. ++ */ ++static struct vm_area_struct gate_vma = { ++ .vm_start = 0xffff0000, ++ .vm_end = 0xffff0000 + PAGE_SIZE, ++ .vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC, ++}; ++ ++static int __init gate_vma_init(void) ++{ ++ gate_vma.vm_page_prot = PAGE_READONLY_EXEC; ++ return 0; ++} ++arch_initcall(gate_vma_init); ++ ++struct vm_area_struct *get_gate_vma(struct mm_struct *mm) ++{ ++ return &gate_vma; ++} ++ ++int in_gate_area(struct mm_struct *mm, unsigned long addr) ++{ ++ return (addr >= gate_vma.vm_start) && (addr < gate_vma.vm_end); ++} ++ ++int in_gate_area_no_mm(unsigned long addr) ++{ ++ return in_gate_area(NULL, addr); ++} ++#define is_gate_vma(vma) ((vma) == &gate_vma) ++#else ++#define is_gate_vma(vma) 0 ++#endif ++ ++const char *arch_vma_name(struct vm_area_struct *vma) ++{ ++ return is_gate_vma(vma) ? "[vectors]" : ++ (vma->vm_mm && vma->vm_start == vma->vm_mm->context.sigpage) ? ++ "[sigpage]" : NULL; ++} ++ ++static struct page *signal_page; ++extern struct page *get_signal_page(void); ++ ++int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ++{ ++ struct mm_struct *mm = current->mm; ++ unsigned long addr; ++ int ret; ++ ++ if (!signal_page) ++ signal_page = get_signal_page(); ++ if (!signal_page) ++ return -ENOMEM; ++ ++ down_write(&mm->mmap_sem); ++ addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0); ++ if (IS_ERR_VALUE(addr)) { ++ ret = addr; ++ goto up_fail; ++ } ++ ++ ret = install_special_mapping(mm, addr, PAGE_SIZE, ++ VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, ++ &signal_page); ++ ++ if (ret == 0) ++ mm->context.sigpage = addr; ++ ++ up_fail: ++ up_write(&mm->mmap_sem); ++ return ret; ++} ++#endif +diff -Nur linux-3.14.36/arch/arm/kernel/psci.c linux-openelec/arch/arm/kernel/psci.c +--- linux-3.14.36/arch/arm/kernel/psci.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/kernel/psci.c 2015-05-06 12:05:43.000000000 -0500 +@@ -42,6 +42,7 @@ + #define PSCI_RET_EOPNOTSUPP -1 + #define PSCI_RET_EINVAL -2 + #define PSCI_RET_EPERM -3 ++#define PSCI_RET_EALREADYON -4 + + static int psci_to_linux_errno(int errno) + { +@@ -54,6 +55,8 @@ + return -EINVAL; + case PSCI_RET_EPERM: + return -EPERM; ++ case PSCI_RET_EALREADYON: ++ return -EAGAIN; + }; + + return -EINVAL; +@@ -153,7 +156,7 @@ + return psci_to_linux_errno(err); + } + +-static const struct of_device_id psci_of_match[] __initconst = { ++static const struct of_device_id psci_of_match[] = { + { .compatible = "arm,psci", }, + {}, + }; +@@ -208,3 +211,16 @@ + of_node_put(np); + return; + } ++ ++int psci_probe(void) ++{ ++ struct device_node *np; ++ int ret = -ENODEV; ++ ++ np = of_find_matching_node(NULL, psci_of_match); ++ if (np) ++ ret = 0; ++ ++ of_node_put(np); ++ return ret; ++} +diff -Nur linux-3.14.36/arch/arm/kernel/setup.c linux-openelec/arch/arm/kernel/setup.c +--- linux-3.14.36/arch/arm/kernel/setup.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/kernel/setup.c 2015-07-24 18:03:29.292842002 -0500 +@@ -273,6 +273,19 @@ + int aliasing_icache; + unsigned int id_reg, num_sets, line_size; + ++#ifdef CONFIG_BIG_LITTLE ++ /* ++ * We expect a combination of Cortex-A15 and Cortex-A7 cores. ++ * A7 = VIPT aliasing I-cache ++ * A15 = PIPT (non-aliasing) I-cache ++ * To cater for this discrepancy, let's assume aliasing I-cache ++ * all the time. This means unneeded extra work on the A15 but ++ * only ptrace is affected which is not performance critical. ++ */ ++ if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc0f0) ++ return 1; ++#endif ++ + /* PIPT caches never alias. */ + if (icache_is_pipt()) + return 0; +diff -Nur linux-3.14.36/arch/arm/kernel/setup.c.orig linux-openelec/arch/arm/kernel/setup.c.orig +--- linux-3.14.36/arch/arm/kernel/setup.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/kernel/setup.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,1095 @@ ++/* ++ * linux/arch/arm/kernel/setup.c ++ * ++ * Copyright (C) 1995-2001 Russell King ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "atags.h" ++ ++ ++#if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE) ++char fpe_type[8]; ++ ++static int __init fpe_setup(char *line) ++{ ++ memcpy(fpe_type, line, 8); ++ return 1; ++} ++ ++__setup("fpe=", fpe_setup); ++#endif ++ ++extern void paging_init(const struct machine_desc *desc); ++extern void early_paging_init(const struct machine_desc *, ++ struct proc_info_list *); ++extern void sanity_check_meminfo(void); ++extern enum reboot_mode reboot_mode; ++extern void setup_dma_zone(const struct machine_desc *desc); ++ ++unsigned int processor_id; ++EXPORT_SYMBOL(processor_id); ++unsigned int __machine_arch_type __read_mostly; ++EXPORT_SYMBOL(__machine_arch_type); ++unsigned int cacheid __read_mostly; ++EXPORT_SYMBOL(cacheid); ++ ++unsigned int __atags_pointer __initdata; ++ ++unsigned int system_rev; ++EXPORT_SYMBOL(system_rev); ++ ++unsigned int system_serial_low; ++EXPORT_SYMBOL(system_serial_low); ++ ++unsigned int system_serial_high; ++EXPORT_SYMBOL(system_serial_high); ++ ++unsigned int elf_hwcap __read_mostly; ++EXPORT_SYMBOL(elf_hwcap); ++ ++ ++#ifdef MULTI_CPU ++struct processor processor __read_mostly; ++#endif ++#ifdef MULTI_TLB ++struct cpu_tlb_fns cpu_tlb __read_mostly; ++#endif ++#ifdef MULTI_USER ++struct cpu_user_fns cpu_user __read_mostly; ++#endif ++#ifdef MULTI_CACHE ++struct cpu_cache_fns cpu_cache __read_mostly; ++#endif ++#ifdef CONFIG_OUTER_CACHE ++struct outer_cache_fns outer_cache __read_mostly; ++EXPORT_SYMBOL(outer_cache); ++#endif ++ ++/* ++ * Cached cpu_architecture() result for use by assembler code. ++ * C code should use the cpu_architecture() function instead of accessing this ++ * variable directly. ++ */ ++int __cpu_architecture __read_mostly = CPU_ARCH_UNKNOWN; ++ ++struct stack { ++ u32 irq[3]; ++ u32 abt[3]; ++ u32 und[3]; ++} ____cacheline_aligned; ++ ++#ifndef CONFIG_CPU_V7M ++static struct stack stacks[NR_CPUS]; ++#endif ++ ++char elf_platform[ELF_PLATFORM_SIZE]; ++EXPORT_SYMBOL(elf_platform); ++ ++static const char *cpu_name; ++static const char *machine_name; ++static char __initdata cmd_line[COMMAND_LINE_SIZE]; ++const struct machine_desc *machine_desc __initdata; ++ ++static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } }; ++#define ENDIANNESS ((char)endian_test.l) ++ ++DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data); ++ ++/* ++ * Standard memory resources ++ */ ++static struct resource mem_res[] = { ++ { ++ .name = "Video RAM", ++ .start = 0, ++ .end = 0, ++ .flags = IORESOURCE_MEM ++ }, ++ { ++ .name = "Kernel code", ++ .start = 0, ++ .end = 0, ++ .flags = IORESOURCE_MEM ++ }, ++ { ++ .name = "Kernel data", ++ .start = 0, ++ .end = 0, ++ .flags = IORESOURCE_MEM ++ } ++}; ++ ++#define video_ram mem_res[0] ++#define kernel_code mem_res[1] ++#define kernel_data mem_res[2] ++ ++static struct resource io_res[] = { ++ { ++ .name = "reserved", ++ .start = 0x3bc, ++ .end = 0x3be, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY ++ }, ++ { ++ .name = "reserved", ++ .start = 0x378, ++ .end = 0x37f, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY ++ }, ++ { ++ .name = "reserved", ++ .start = 0x278, ++ .end = 0x27f, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY ++ } ++}; ++ ++#define lp0 io_res[0] ++#define lp1 io_res[1] ++#define lp2 io_res[2] ++ ++static const char *proc_arch[] = { ++ "undefined/unknown", ++ "3", ++ "4", ++ "4T", ++ "5", ++ "5T", ++ "5TE", ++ "5TEJ", ++ "6TEJ", ++ "7", ++ "7M", ++ "?(12)", ++ "?(13)", ++ "?(14)", ++ "?(15)", ++ "?(16)", ++ "?(17)", ++}; ++ ++#ifdef CONFIG_CPU_V7M ++static int __get_cpu_architecture(void) ++{ ++ return CPU_ARCH_ARMv7M; ++} ++#else ++static int __get_cpu_architecture(void) ++{ ++ int cpu_arch; ++ ++ if ((read_cpuid_id() & 0x0008f000) == 0) { ++ cpu_arch = CPU_ARCH_UNKNOWN; ++ } else if ((read_cpuid_id() & 0x0008f000) == 0x00007000) { ++ cpu_arch = (read_cpuid_id() & (1 << 23)) ? CPU_ARCH_ARMv4T : CPU_ARCH_ARMv3; ++ } else if ((read_cpuid_id() & 0x00080000) == 0x00000000) { ++ cpu_arch = (read_cpuid_id() >> 16) & 7; ++ if (cpu_arch) ++ cpu_arch += CPU_ARCH_ARMv3; ++ } else if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) { ++ unsigned int mmfr0; ++ ++ /* Revised CPUID format. Read the Memory Model Feature ++ * Register 0 and check for VMSAv7 or PMSAv7 */ ++ asm("mrc p15, 0, %0, c0, c1, 4" ++ : "=r" (mmfr0)); ++ if ((mmfr0 & 0x0000000f) >= 0x00000003 || ++ (mmfr0 & 0x000000f0) >= 0x00000030) ++ cpu_arch = CPU_ARCH_ARMv7; ++ else if ((mmfr0 & 0x0000000f) == 0x00000002 || ++ (mmfr0 & 0x000000f0) == 0x00000020) ++ cpu_arch = CPU_ARCH_ARMv6; ++ else ++ cpu_arch = CPU_ARCH_UNKNOWN; ++ } else ++ cpu_arch = CPU_ARCH_UNKNOWN; ++ ++ return cpu_arch; ++} ++#endif ++ ++int __pure cpu_architecture(void) ++{ ++ BUG_ON(__cpu_architecture == CPU_ARCH_UNKNOWN); ++ ++ return __cpu_architecture; ++} ++ ++static int cpu_has_aliasing_icache(unsigned int arch) ++{ ++ int aliasing_icache; ++ unsigned int id_reg, num_sets, line_size; ++ ++#ifdef CONFIG_BIG_LITTLE ++ /* ++ * We expect a combination of Cortex-A15 and Cortex-A7 cores. ++ * A7 = VIPT aliasing I-cache ++ * A15 = PIPT (non-aliasing) I-cache ++ * To cater for this discrepancy, let's assume aliasing I-cache ++ * all the time. This means unneeded extra work on the A15 but ++ * only ptrace is affected which is not performance critical. ++ */ ++ if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc0f0) ++ return 1; ++#endif ++ ++ /* PIPT caches never alias. */ ++ if (icache_is_pipt()) ++ return 0; ++ ++ /* arch specifies the register format */ ++ switch (arch) { ++ case CPU_ARCH_ARMv7: ++ asm("mcr p15, 2, %0, c0, c0, 0 @ set CSSELR" ++ : /* No output operands */ ++ : "r" (1)); ++ isb(); ++ asm("mrc p15, 1, %0, c0, c0, 0 @ read CCSIDR" ++ : "=r" (id_reg)); ++ line_size = 4 << ((id_reg & 0x7) + 2); ++ num_sets = ((id_reg >> 13) & 0x7fff) + 1; ++ aliasing_icache = (line_size * num_sets) > PAGE_SIZE; ++ break; ++ case CPU_ARCH_ARMv6: ++ aliasing_icache = read_cpuid_cachetype() & (1 << 11); ++ break; ++ default: ++ /* I-cache aliases will be handled by D-cache aliasing code */ ++ aliasing_icache = 0; ++ } ++ ++ return aliasing_icache; ++} ++ ++static void __init cacheid_init(void) ++{ ++ unsigned int arch = cpu_architecture(); ++ ++ if (arch == CPU_ARCH_ARMv7M) { ++ cacheid = 0; ++ } else if (arch >= CPU_ARCH_ARMv6) { ++ unsigned int cachetype = read_cpuid_cachetype(); ++ if ((cachetype & (7 << 29)) == 4 << 29) { ++ /* ARMv7 register format */ ++ arch = CPU_ARCH_ARMv7; ++ cacheid = CACHEID_VIPT_NONALIASING; ++ switch (cachetype & (3 << 14)) { ++ case (1 << 14): ++ cacheid |= CACHEID_ASID_TAGGED; ++ break; ++ case (3 << 14): ++ cacheid |= CACHEID_PIPT; ++ break; ++ } ++ } else { ++ arch = CPU_ARCH_ARMv6; ++ if (cachetype & (1 << 23)) ++ cacheid = CACHEID_VIPT_ALIASING; ++ else ++ cacheid = CACHEID_VIPT_NONALIASING; ++ } ++ if (cpu_has_aliasing_icache(arch)) ++ cacheid |= CACHEID_VIPT_I_ALIASING; ++ } else { ++ cacheid = CACHEID_VIVT; ++ } ++ ++ pr_info("CPU: %s data cache, %s instruction cache\n", ++ cache_is_vivt() ? "VIVT" : ++ cache_is_vipt_aliasing() ? "VIPT aliasing" : ++ cache_is_vipt_nonaliasing() ? "PIPT / VIPT nonaliasing" : "unknown", ++ cache_is_vivt() ? "VIVT" : ++ icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" : ++ icache_is_vipt_aliasing() ? "VIPT aliasing" : ++ icache_is_pipt() ? "PIPT" : ++ cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown"); ++} ++ ++/* ++ * These functions re-use the assembly code in head.S, which ++ * already provide the required functionality. ++ */ ++extern struct proc_info_list *lookup_processor_type(unsigned int); ++ ++void __init early_print(const char *str, ...) ++{ ++ extern void printascii(const char *); ++ char buf[256]; ++ va_list ap; ++ ++ va_start(ap, str); ++ vsnprintf(buf, sizeof(buf), str, ap); ++ va_end(ap); ++ ++#ifdef CONFIG_DEBUG_LL ++ printascii(buf); ++#endif ++ printk("%s", buf); ++} ++ ++static void __init cpuid_init_hwcaps(void) ++{ ++ unsigned int divide_instrs, vmsa; ++ ++ if (cpu_architecture() < CPU_ARCH_ARMv7) ++ return; ++ ++ divide_instrs = (read_cpuid_ext(CPUID_EXT_ISAR0) & 0x0f000000) >> 24; ++ ++ switch (divide_instrs) { ++ case 2: ++ elf_hwcap |= HWCAP_IDIVA; ++ case 1: ++ elf_hwcap |= HWCAP_IDIVT; ++ } ++ ++ /* LPAE implies atomic ldrd/strd instructions */ ++ vmsa = (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xf) >> 0; ++ if (vmsa >= 5) ++ elf_hwcap |= HWCAP_LPAE; ++} ++ ++static void __init feat_v6_fixup(void) ++{ ++ int id = read_cpuid_id(); ++ ++ if ((id & 0xff0f0000) != 0x41070000) ++ return; ++ ++ /* ++ * HWCAP_TLS is available only on 1136 r1p0 and later, ++ * see also kuser_get_tls_init. ++ */ ++ if ((((id >> 4) & 0xfff) == 0xb36) && (((id >> 20) & 3) == 0)) ++ elf_hwcap &= ~HWCAP_TLS; ++} ++ ++/* ++ * cpu_init - initialise one CPU. ++ * ++ * cpu_init sets up the per-CPU stacks. ++ */ ++void notrace cpu_init(void) ++{ ++#ifndef CONFIG_CPU_V7M ++ unsigned int cpu = smp_processor_id(); ++ struct stack *stk = &stacks[cpu]; ++ ++ if (cpu >= NR_CPUS) { ++ pr_crit("CPU%u: bad primary CPU number\n", cpu); ++ BUG(); ++ } ++ ++ /* ++ * This only works on resume and secondary cores. For booting on the ++ * boot cpu, smp_prepare_boot_cpu is called after percpu area setup. ++ */ ++ set_my_cpu_offset(per_cpu_offset(cpu)); ++ ++ cpu_proc_init(); ++ ++ /* ++ * Define the placement constraint for the inline asm directive below. ++ * In Thumb-2, msr with an immediate value is not allowed. ++ */ ++#ifdef CONFIG_THUMB2_KERNEL ++#define PLC "r" ++#else ++#define PLC "I" ++#endif ++ ++ /* ++ * setup stacks for re-entrant exception handlers ++ */ ++ __asm__ ( ++ "msr cpsr_c, %1\n\t" ++ "add r14, %0, %2\n\t" ++ "mov sp, r14\n\t" ++ "msr cpsr_c, %3\n\t" ++ "add r14, %0, %4\n\t" ++ "mov sp, r14\n\t" ++ "msr cpsr_c, %5\n\t" ++ "add r14, %0, %6\n\t" ++ "mov sp, r14\n\t" ++ "msr cpsr_c, %7" ++ : ++ : "r" (stk), ++ PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE), ++ "I" (offsetof(struct stack, irq[0])), ++ PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE), ++ "I" (offsetof(struct stack, abt[0])), ++ PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE), ++ "I" (offsetof(struct stack, und[0])), ++ PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE) ++ : "r14"); ++#endif ++} ++ ++u32 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = MPIDR_INVALID }; ++ ++void __init smp_setup_processor_id(void) ++{ ++ int i; ++ u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0; ++ u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); ++ ++ cpu_logical_map(0) = cpu; ++ for (i = 1; i < nr_cpu_ids; ++i) ++ cpu_logical_map(i) = i == cpu ? 0 : i; ++ ++ /* ++ * clear __my_cpu_offset on boot CPU to avoid hang caused by ++ * using percpu variable early, for example, lockdep will ++ * access percpu variable inside lock_release ++ */ ++ set_my_cpu_offset(0); ++ ++ pr_info("Booting Linux on physical CPU 0x%x\n", mpidr); ++} ++ ++struct mpidr_hash mpidr_hash; ++#ifdef CONFIG_SMP ++/** ++ * smp_build_mpidr_hash - Pre-compute shifts required at each affinity ++ * level in order to build a linear index from an ++ * MPIDR value. Resulting algorithm is a collision ++ * free hash carried out through shifting and ORing ++ */ ++static void __init smp_build_mpidr_hash(void) ++{ ++ u32 i, affinity; ++ u32 fs[3], bits[3], ls, mask = 0; ++ /* ++ * Pre-scan the list of MPIDRS and filter out bits that do ++ * not contribute to affinity levels, ie they never toggle. ++ */ ++ for_each_possible_cpu(i) ++ mask |= (cpu_logical_map(i) ^ cpu_logical_map(0)); ++ pr_debug("mask of set bits 0x%x\n", mask); ++ /* ++ * Find and stash the last and first bit set at all affinity levels to ++ * check how many bits are required to represent them. ++ */ ++ for (i = 0; i < 3; i++) { ++ affinity = MPIDR_AFFINITY_LEVEL(mask, i); ++ /* ++ * Find the MSB bit and LSB bits position ++ * to determine how many bits are required ++ * to express the affinity level. ++ */ ++ ls = fls(affinity); ++ fs[i] = affinity ? ffs(affinity) - 1 : 0; ++ bits[i] = ls - fs[i]; ++ } ++ /* ++ * An index can be created from the MPIDR by isolating the ++ * significant bits at each affinity level and by shifting ++ * them in order to compress the 24 bits values space to a ++ * compressed set of values. This is equivalent to hashing ++ * the MPIDR through shifting and ORing. It is a collision free ++ * hash though not minimal since some levels might contain a number ++ * of CPUs that is not an exact power of 2 and their bit ++ * representation might contain holes, eg MPIDR[7:0] = {0x2, 0x80}. ++ */ ++ mpidr_hash.shift_aff[0] = fs[0]; ++ mpidr_hash.shift_aff[1] = MPIDR_LEVEL_BITS + fs[1] - bits[0]; ++ mpidr_hash.shift_aff[2] = 2*MPIDR_LEVEL_BITS + fs[2] - ++ (bits[1] + bits[0]); ++ mpidr_hash.mask = mask; ++ mpidr_hash.bits = bits[2] + bits[1] + bits[0]; ++ pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] mask[0x%x] bits[%u]\n", ++ mpidr_hash.shift_aff[0], ++ mpidr_hash.shift_aff[1], ++ mpidr_hash.shift_aff[2], ++ mpidr_hash.mask, ++ mpidr_hash.bits); ++ /* ++ * 4x is an arbitrary value used to warn on a hash table much bigger ++ * than expected on most systems. ++ */ ++ if (mpidr_hash_size() > 4 * num_possible_cpus()) ++ pr_warn("Large number of MPIDR hash buckets detected\n"); ++ sync_cache_w(&mpidr_hash); ++} ++#endif ++ ++static void __init setup_processor(void) ++{ ++ struct proc_info_list *list; ++ ++ /* ++ * locate processor in the list of supported processor ++ * types. The linker builds this table for us from the ++ * entries in arch/arm/mm/proc-*.S ++ */ ++ list = lookup_processor_type(read_cpuid_id()); ++ if (!list) { ++ pr_err("CPU configuration botched (ID %08x), unable to continue.\n", ++ read_cpuid_id()); ++ while (1); ++ } ++ ++ cpu_name = list->cpu_name; ++ __cpu_architecture = __get_cpu_architecture(); ++ ++#ifdef MULTI_CPU ++ processor = *list->proc; ++#endif ++#ifdef MULTI_TLB ++ cpu_tlb = *list->tlb; ++#endif ++#ifdef MULTI_USER ++ cpu_user = *list->user; ++#endif ++#ifdef MULTI_CACHE ++ cpu_cache = *list->cache; ++#endif ++ ++ pr_info("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n", ++ cpu_name, read_cpuid_id(), read_cpuid_id() & 15, ++ proc_arch[cpu_architecture()], cr_alignment); ++ ++ snprintf(init_utsname()->machine, __NEW_UTS_LEN + 1, "%s%c", ++ list->arch_name, ENDIANNESS); ++ snprintf(elf_platform, ELF_PLATFORM_SIZE, "%s%c", ++ list->elf_name, ENDIANNESS); ++ elf_hwcap = list->elf_hwcap; ++ ++ cpuid_init_hwcaps(); ++ ++#ifndef CONFIG_ARM_THUMB ++ elf_hwcap &= ~(HWCAP_THUMB | HWCAP_IDIVT); ++#endif ++ ++ erratum_a15_798181_init(); ++ ++ feat_v6_fixup(); ++ ++ cacheid_init(); ++ cpu_init(); ++} ++ ++void __init dump_machine_table(void) ++{ ++ const struct machine_desc *p; ++ ++ early_print("Available machine support:\n\nID (hex)\tNAME\n"); ++ for_each_machine_desc(p) ++ early_print("%08x\t%s\n", p->nr, p->name); ++ ++ early_print("\nPlease check your kernel config and/or bootloader.\n"); ++ ++ while (true) ++ /* can't use cpu_relax() here as it may require MMU setup */; ++} ++ ++int __init arm_add_memory(u64 start, u64 size) ++{ ++ struct membank *bank = &meminfo.bank[meminfo.nr_banks]; ++ u64 aligned_start; ++ ++ if (meminfo.nr_banks >= NR_BANKS) { ++ pr_crit("NR_BANKS too low, ignoring memory at 0x%08llx\n", ++ (long long)start); ++ return -EINVAL; ++ } ++ ++ /* ++ * Ensure that start/size are aligned to a page boundary. ++ * Size is appropriately rounded down, start is rounded up. ++ */ ++ size -= start & ~PAGE_MASK; ++ aligned_start = PAGE_ALIGN(start); ++ ++#ifndef CONFIG_ARCH_PHYS_ADDR_T_64BIT ++ if (aligned_start > ULONG_MAX) { ++ pr_crit("Ignoring memory at 0x%08llx outside 32-bit physical address space\n", ++ (long long)start); ++ return -EINVAL; ++ } ++ ++ if (aligned_start + size > ULONG_MAX) { ++ pr_crit("Truncating memory at 0x%08llx to fit in 32-bit physical address space\n", ++ (long long)start); ++ /* ++ * To ensure bank->start + bank->size is representable in ++ * 32 bits, we use ULONG_MAX as the upper limit rather than 4GB. ++ * This means we lose a page after masking. ++ */ ++ size = ULONG_MAX - aligned_start; ++ } ++#endif ++ ++ if (aligned_start < PHYS_OFFSET) { ++ if (aligned_start + size <= PHYS_OFFSET) { ++ pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n", ++ aligned_start, aligned_start + size); ++ return -EINVAL; ++ } ++ ++ pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n", ++ aligned_start, (u64)PHYS_OFFSET); ++ ++ size -= PHYS_OFFSET - aligned_start; ++ aligned_start = PHYS_OFFSET; ++ } ++ ++ bank->start = aligned_start; ++ bank->size = size & ~(phys_addr_t)(PAGE_SIZE - 1); ++ ++ /* ++ * Check whether this memory region has non-zero size or ++ * invalid node number. ++ */ ++ if (bank->size == 0) ++ return -EINVAL; ++ ++ meminfo.nr_banks++; ++ return 0; ++} ++ ++/* ++ * Pick out the memory size. We look for mem=size@start, ++ * where start and size are "size[KkMm]" ++ */ ++static int __init early_mem(char *p) ++{ ++ static int usermem __initdata = 0; ++ u64 size; ++ u64 start; ++ char *endp; ++ ++ /* ++ * If the user specifies memory size, we ++ * blow away any automatically generated ++ * size. ++ */ ++ if (usermem == 0) { ++ usermem = 1; ++ meminfo.nr_banks = 0; ++ } ++ ++ start = PHYS_OFFSET; ++ size = memparse(p, &endp); ++ if (*endp == '@') ++ start = memparse(endp + 1, NULL); ++ ++ arm_add_memory(start, size); ++ ++ return 0; ++} ++early_param("mem", early_mem); ++ ++static void __init request_standard_resources(const struct machine_desc *mdesc) ++{ ++ struct memblock_region *region; ++ struct resource *res; ++ ++ kernel_code.start = virt_to_phys(_text); ++ kernel_code.end = virt_to_phys(_etext - 1); ++ kernel_data.start = virt_to_phys(_sdata); ++ kernel_data.end = virt_to_phys(_end - 1); ++ ++ for_each_memblock(memory, region) { ++ res = memblock_virt_alloc(sizeof(*res), 0); ++ res->name = "System RAM"; ++ res->start = __pfn_to_phys(memblock_region_memory_base_pfn(region)); ++ res->end = __pfn_to_phys(memblock_region_memory_end_pfn(region)) - 1; ++ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ ++ request_resource(&iomem_resource, res); ++ ++ if (kernel_code.start >= res->start && ++ kernel_code.end <= res->end) ++ request_resource(res, &kernel_code); ++ if (kernel_data.start >= res->start && ++ kernel_data.end <= res->end) ++ request_resource(res, &kernel_data); ++ } ++ ++ if (mdesc->video_start) { ++ video_ram.start = mdesc->video_start; ++ video_ram.end = mdesc->video_end; ++ request_resource(&iomem_resource, &video_ram); ++ } ++ ++ /* ++ * Some machines don't have the possibility of ever ++ * possessing lp0, lp1 or lp2 ++ */ ++ if (mdesc->reserve_lp0) ++ request_resource(&ioport_resource, &lp0); ++ if (mdesc->reserve_lp1) ++ request_resource(&ioport_resource, &lp1); ++ if (mdesc->reserve_lp2) ++ request_resource(&ioport_resource, &lp2); ++} ++ ++#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) ++struct screen_info screen_info = { ++ .orig_video_lines = 30, ++ .orig_video_cols = 80, ++ .orig_video_mode = 0, ++ .orig_video_ega_bx = 0, ++ .orig_video_isVGA = 1, ++ .orig_video_points = 8 ++}; ++#endif ++ ++static int __init customize_machine(void) ++{ ++ /* ++ * customizes platform devices, or adds new ones ++ * On DT based machines, we fall back to populating the ++ * machine from the device tree, if no callback is provided, ++ * otherwise we would always need an init_machine callback. ++ */ ++ if (machine_desc->init_machine) ++ machine_desc->init_machine(); ++#ifdef CONFIG_OF ++ else ++ of_platform_populate(NULL, of_default_bus_match_table, ++ NULL, NULL); ++#endif ++ return 0; ++} ++arch_initcall(customize_machine); ++ ++static int __init init_machine_late(void) ++{ ++ if (machine_desc->init_late) ++ machine_desc->init_late(); ++ return 0; ++} ++late_initcall(init_machine_late); ++ ++#ifdef CONFIG_KEXEC ++static inline unsigned long long get_total_mem(void) ++{ ++ unsigned long total; ++ ++ total = max_low_pfn - min_low_pfn; ++ return total << PAGE_SHIFT; ++} ++ ++/** ++ * reserve_crashkernel() - reserves memory are for crash kernel ++ * ++ * This function reserves memory area given in "crashkernel=" kernel command ++ * line parameter. The memory reserved is used by a dump capture kernel when ++ * primary kernel is crashing. ++ */ ++static void __init reserve_crashkernel(void) ++{ ++ unsigned long long crash_size, crash_base; ++ unsigned long long total_mem; ++ int ret; ++ ++ total_mem = get_total_mem(); ++ ret = parse_crashkernel(boot_command_line, total_mem, ++ &crash_size, &crash_base); ++ if (ret) ++ return; ++ ++ ret = memblock_reserve(crash_base, crash_size); ++ if (ret < 0) { ++ pr_warn("crashkernel reservation failed - memory is in use (0x%lx)\n", ++ (unsigned long)crash_base); ++ return; ++ } ++ ++ pr_info("Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n", ++ (unsigned long)(crash_size >> 20), ++ (unsigned long)(crash_base >> 20), ++ (unsigned long)(total_mem >> 20)); ++ ++ crashk_res.start = crash_base; ++ crashk_res.end = crash_base + crash_size - 1; ++ insert_resource(&iomem_resource, &crashk_res); ++} ++#else ++static inline void reserve_crashkernel(void) {} ++#endif /* CONFIG_KEXEC */ ++ ++static int __init meminfo_cmp(const void *_a, const void *_b) ++{ ++ const struct membank *a = _a, *b = _b; ++ long cmp = bank_pfn_start(a) - bank_pfn_start(b); ++ return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; ++} ++ ++void __init hyp_mode_check(void) ++{ ++#ifdef CONFIG_ARM_VIRT_EXT ++ sync_boot_mode(); ++ ++ if (is_hyp_mode_available()) { ++ pr_info("CPU: All CPU(s) started in HYP mode.\n"); ++ pr_info("CPU: Virtualization extensions available.\n"); ++ } else if (is_hyp_mode_mismatched()) { ++ pr_warn("CPU: WARNING: CPU(s) started in wrong/inconsistent modes (primary CPU mode 0x%x)\n", ++ __boot_cpu_mode & MODE_MASK); ++ pr_warn("CPU: This may indicate a broken bootloader or firmware.\n"); ++ } else ++ pr_info("CPU: All CPU(s) started in SVC mode.\n"); ++#endif ++} ++ ++void __init setup_arch(char **cmdline_p) ++{ ++ const struct machine_desc *mdesc; ++ ++ setup_processor(); ++ mdesc = setup_machine_fdt(__atags_pointer); ++ if (!mdesc) ++ mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type); ++ machine_desc = mdesc; ++ machine_name = mdesc->name; ++ ++ if (mdesc->reboot_mode != REBOOT_HARD) ++ reboot_mode = mdesc->reboot_mode; ++ ++ init_mm.start_code = (unsigned long) _text; ++ init_mm.end_code = (unsigned long) _etext; ++ init_mm.end_data = (unsigned long) _edata; ++ init_mm.brk = (unsigned long) _end; ++ ++ /* populate cmd_line too for later use, preserving boot_command_line */ ++ strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); ++ *cmdline_p = cmd_line; ++ ++ parse_early_param(); ++ ++ sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL); ++ ++ early_paging_init(mdesc, lookup_processor_type(read_cpuid_id())); ++ setup_dma_zone(mdesc); ++ sanity_check_meminfo(); ++ arm_memblock_init(&meminfo, mdesc); ++ ++ paging_init(mdesc); ++ request_standard_resources(mdesc); ++ ++ if (mdesc->restart) ++ arm_pm_restart = mdesc->restart; ++ ++ unflatten_device_tree(); ++ ++ arm_dt_init_cpu_maps(); ++ psci_init(); ++#ifdef CONFIG_SMP ++ if (is_smp()) { ++ if (!mdesc->smp_init || !mdesc->smp_init()) { ++ if (psci_smp_available()) ++ smp_set_ops(&psci_smp_ops); ++ else if (mdesc->smp) ++ smp_set_ops(mdesc->smp); ++ } ++ smp_init_cpus(); ++ smp_build_mpidr_hash(); ++ } ++#endif ++ ++ if (!is_smp()) ++ hyp_mode_check(); ++ ++ reserve_crashkernel(); ++ ++#ifdef CONFIG_MULTI_IRQ_HANDLER ++ handle_arch_irq = mdesc->handle_irq; ++#endif ++ ++#ifdef CONFIG_VT ++#if defined(CONFIG_VGA_CONSOLE) ++ conswitchp = &vga_con; ++#elif defined(CONFIG_DUMMY_CONSOLE) ++ conswitchp = &dummy_con; ++#endif ++#endif ++ ++ if (mdesc->init_early) ++ mdesc->init_early(); ++} ++ ++ ++static int __init topology_init(void) ++{ ++ int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ struct cpuinfo_arm *cpuinfo = &per_cpu(cpu_data, cpu); ++ cpuinfo->cpu.hotpluggable = 1; ++ register_cpu(&cpuinfo->cpu, cpu); ++ } ++ ++ return 0; ++} ++subsys_initcall(topology_init); ++ ++#ifdef CONFIG_HAVE_PROC_CPU ++static int __init proc_cpu_init(void) ++{ ++ struct proc_dir_entry *res; ++ ++ res = proc_mkdir("cpu", NULL); ++ if (!res) ++ return -ENOMEM; ++ return 0; ++} ++fs_initcall(proc_cpu_init); ++#endif ++ ++static const char *hwcap_str[] = { ++ "swp", ++ "half", ++ "thumb", ++ "26bit", ++ "fastmult", ++ "fpa", ++ "vfp", ++ "edsp", ++ "java", ++ "iwmmxt", ++ "crunch", ++ "thumbee", ++ "neon", ++ "vfpv3", ++ "vfpv3d16", ++ "tls", ++ "vfpv4", ++ "idiva", ++ "idivt", ++ "vfpd32", ++ "lpae", ++ "evtstrm", ++ NULL ++}; ++ ++static int c_show(struct seq_file *m, void *v) ++{ ++ int i, j; ++ u32 cpuid; ++ ++ for_each_online_cpu(i) { ++ /* ++ * glibc reads /proc/cpuinfo to determine the number of ++ * online processors, looking for lines beginning with ++ * "processor". Give glibc what it expects. ++ */ ++ seq_printf(m, "processor\t: %d\n", i); ++ cpuid = is_smp() ? per_cpu(cpu_data, i).cpuid : read_cpuid_id(); ++ seq_printf(m, "model name\t: %s rev %d (%s)\n", ++ cpu_name, cpuid & 15, elf_platform); ++ ++ /* dump out the processor features */ ++ seq_puts(m, "Features\t: "); ++ ++ for (j = 0; hwcap_str[j]; j++) ++ if (elf_hwcap & (1 << j)) ++ seq_printf(m, "%s ", hwcap_str[j]); ++ ++ seq_printf(m, "\nCPU implementer\t: 0x%02x\n", cpuid >> 24); ++ seq_printf(m, "CPU architecture: %s\n", ++ proc_arch[cpu_architecture()]); ++ ++ if ((cpuid & 0x0008f000) == 0x00000000) { ++ /* pre-ARM7 */ ++ seq_printf(m, "CPU part\t: %07x\n", cpuid >> 4); ++ } else { ++ if ((cpuid & 0x0008f000) == 0x00007000) { ++ /* ARM7 */ ++ seq_printf(m, "CPU variant\t: 0x%02x\n", ++ (cpuid >> 16) & 127); ++ } else { ++ /* post-ARM7 */ ++ seq_printf(m, "CPU variant\t: 0x%x\n", ++ (cpuid >> 20) & 15); ++ } ++ seq_printf(m, "CPU part\t: 0x%03x\n", ++ (cpuid >> 4) & 0xfff); ++ } ++ seq_printf(m, "CPU revision\t: %d\n\n", cpuid & 15); ++ } ++ ++ seq_printf(m, "Hardware\t: %s\n", machine_name); ++ seq_printf(m, "Revision\t: %04x\n", system_rev); ++ seq_printf(m, "Serial\t\t: %08x%08x\n", ++ system_serial_high, system_serial_low); ++ ++ return 0; ++} ++ ++static void *c_start(struct seq_file *m, loff_t *pos) ++{ ++ return *pos < 1 ? (void *)1 : NULL; ++} ++ ++static void *c_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ ++*pos; ++ return NULL; ++} ++ ++static void c_stop(struct seq_file *m, void *v) ++{ ++} ++ ++const struct seq_operations cpuinfo_op = { ++ .start = c_start, ++ .next = c_next, ++ .stop = c_stop, ++ .show = c_show ++}; +diff -Nur linux-3.14.36/arch/arm/kernel/topology.c linux-openelec/arch/arm/kernel/topology.c +--- linux-3.14.36/arch/arm/kernel/topology.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/kernel/topology.c 2015-05-06 12:05:43.000000000 -0500 +@@ -267,6 +267,33 @@ + } + + /* ++ * cluster_to_logical_mask - return cpu logical mask of CPUs in a cluster ++ * @socket_id: cluster HW identifier ++ * @cluster_mask: the cpumask location to be initialized, modified by the ++ * function only if return value == 0 ++ * ++ * Return: ++ * ++ * 0 on success ++ * -EINVAL if cluster_mask is NULL or there is no record matching socket_id ++ */ ++int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask) ++{ ++ int cpu; ++ ++ if (!cluster_mask) ++ return -EINVAL; ++ ++ for_each_online_cpu(cpu) ++ if (socket_id == topology_physical_package_id(cpu)) { ++ cpumask_copy(cluster_mask, topology_core_cpumask(cpu)); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/* + * init_cpu_topology is called at boot when only one cpu is running + * which prevent simultaneous write access to cpu_topology array + */ +diff -Nur linux-3.14.36/arch/arm/lib/bitops.h linux-openelec/arch/arm/lib/bitops.h +--- linux-3.14.36/arch/arm/lib/bitops.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/lib/bitops.h 2015-05-06 12:05:43.000000000 -0500 +@@ -37,6 +37,11 @@ + add r1, r1, r0, lsl #2 @ Get word offset + mov r3, r2, lsl r3 @ create mask + smp_dmb ++#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) ++ .arch_extension mp ++ ALT_SMP(W(pldw) [r1]) ++ ALT_UP(W(nop)) ++#endif + 1: ldrex r2, [r1] + ands r0, r2, r3 @ save old value of bit + \instr r2, r2, r3 @ toggle bit +diff -Nur linux-3.14.36/arch/arm/mach-berlin/berlin.c linux-openelec/arch/arm/mach-berlin/berlin.c +--- linux-3.14.36/arch/arm/mach-berlin/berlin.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-berlin/berlin.c 2015-05-06 12:05:43.000000000 -0500 +@@ -24,7 +24,7 @@ + * with DT probing for L2CCs, berlin_init_machine can be removed. + * Note: 88DE3005 (Armada 1500-mini) uses pl310 l2cc + */ +- l2x0_of_init(0x70c00000, 0xfeffffff); ++ l2x0_of_init(0x30c00000, 0xfeffffff); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + } + +diff -Nur linux-3.14.36/arch/arm/mach-cns3xxx/core.c linux-openelec/arch/arm/mach-cns3xxx/core.c +--- linux-3.14.36/arch/arm/mach-cns3xxx/core.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-cns3xxx/core.c 2015-05-06 12:05:43.000000000 -0500 +@@ -240,9 +240,9 @@ + * + * 1 cycle of latency for setup, read and write accesses + */ +- val = readl(base + L2X0_TAG_LATENCY_CTRL); ++ val = readl(base + L310_TAG_LATENCY_CTRL); + val &= 0xfffff888; +- writel(val, base + L2X0_TAG_LATENCY_CTRL); ++ writel(val, base + L310_TAG_LATENCY_CTRL); + + /* + * Data RAM Control register +@@ -253,12 +253,12 @@ + * + * 1 cycle of latency for setup, read and write accesses + */ +- val = readl(base + L2X0_DATA_LATENCY_CTRL); ++ val = readl(base + L310_DATA_LATENCY_CTRL); + val &= 0xfffff888; +- writel(val, base + L2X0_DATA_LATENCY_CTRL); ++ writel(val, base + L310_DATA_LATENCY_CTRL); + + /* 32 KiB, 8-way, parity disable */ +- l2x0_init(base, 0x00540000, 0xfe000fff); ++ l2x0_init(base, 0x00500000, 0xfe0f0fff); + } + + #endif /* CONFIG_CACHE_L2X0 */ +diff -Nur linux-3.14.36/arch/arm/mach-exynos/common.c linux-openelec/arch/arm/mach-exynos/common.c +--- linux-3.14.36/arch/arm/mach-exynos/common.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-exynos/common.c 2015-05-06 12:05:43.000000000 -0500 +@@ -45,9 +45,6 @@ + #include "common.h" + #include "regs-pmu.h" + +-#define L2_AUX_VAL 0x7C470001 +-#define L2_AUX_MASK 0xC200ffff +- + static const char name_exynos4210[] = "EXYNOS4210"; + static const char name_exynos4212[] = "EXYNOS4212"; + static const char name_exynos4412[] = "EXYNOS4412"; +@@ -400,7 +397,7 @@ + { + int ret; + +- ret = l2x0_of_init(L2_AUX_VAL, L2_AUX_MASK); ++ ret = l2x0_of_init(0x3c400001, 0xc20fffff); + if (ret) + return ret; + +diff -Nur linux-3.14.36/arch/arm/mach-highbank/highbank.c linux-openelec/arch/arm/mach-highbank/highbank.c +--- linux-3.14.36/arch/arm/mach-highbank/highbank.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-highbank/highbank.c 2015-05-06 12:05:43.000000000 -0500 +@@ -20,7 +20,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -51,11 +51,13 @@ + } + + +-static void highbank_l2x0_disable(void) ++static void highbank_l2c310_write_sec(unsigned long val, unsigned reg) + { +- outer_flush_all(); +- /* Disable PL310 L2 Cache controller */ +- highbank_smc1(0x102, 0x0); ++ if (reg == L2X0_CTRL) ++ highbank_smc1(0x102, val); ++ else ++ WARN_ONCE(1, "Highbank L2C310: ignoring write to reg 0x%x\n", ++ reg); + } + + static void __init highbank_init_irq(void) +@@ -66,11 +68,9 @@ + highbank_scu_map_io(); + + /* Enable PL310 L2 Cache controller */ +- if (IS_ENABLED(CONFIG_CACHE_L2X0) && +- of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) { +- highbank_smc1(0x102, 0x1); +- l2x0_of_init(0, ~0UL); +- outer_cache.disable = highbank_l2x0_disable; ++ if (IS_ENABLED(CONFIG_CACHE_L2X0)) { ++ outer_cache.write_sec = highbank_l2c310_write_sec; ++ l2x0_of_init(0, ~0); + } + } + +diff -Nur linux-3.14.36/arch/arm/mach-imx/anatop.c linux-openelec/arch/arm/mach-imx/anatop.c +--- linux-3.14.36/arch/arm/mach-imx/anatop.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/anatop.c 2015-05-06 12:05:43.000000000 -0500 +@@ -9,6 +9,7 @@ + * http://www.gnu.org/copyleft/gpl.html + */ + ++#include + #include + #include + #include +@@ -35,6 +36,10 @@ + #define BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B 0x80000 + #define BM_ANADIG_USB_CHRG_DETECT_EN_B 0x100000 + ++#define ANADIG_REG_TARG_MASK 0x1f ++#define ANADIG_REG1_TARG_SHIFT 9 /* VDDPU */ ++#define ANADIG_REG2_TARG_SHIFT 18 /* VDDSOC */ ++ + static struct regmap *anatop; + + static void imx_anatop_enable_weak2p5(bool enable) +@@ -78,6 +83,28 @@ + BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B); + } + ++void imx_anatop_pu_enable(bool enable) ++{ ++ u32 val; ++ ++ regmap_read(anatop, ANADIG_REG_CORE, &val); ++ val &= ANADIG_REG_TARG_MASK << ANADIG_REG2_TARG_SHIFT; ++ /* ++ * set pu regulator only in LDO_BYPASS mode(know by VDDSOC reg 0x1f), ++ * else handled by anatop regulator driver. ++ */ ++ if (((val >> (ANADIG_REG2_TARG_SHIFT)) & ANADIG_REG_TARG_MASK) ++ == ANADIG_REG_TARG_MASK) { ++ if (enable) { ++ regmap_write(anatop, ANADIG_REG_CORE + REG_SET, ++ ANADIG_REG_TARG_MASK << ANADIG_REG1_TARG_SHIFT); ++ udelay(70); /* bypass need 70us to be stable */ ++ } else { ++ regmap_write(anatop, ANADIG_REG_CORE + REG_CLR, ++ ANADIG_REG_TARG_MASK << ANADIG_REG1_TARG_SHIFT); ++ } ++ } ++} + void __init imx_init_revision_from_anatop(void) + { + struct device_node *np; +@@ -104,6 +131,15 @@ + case 2: + revision = IMX_CHIP_REVISION_1_2; + break; ++ case 3: ++ revision = IMX_CHIP_REVISION_1_3; ++ break; ++ case 4: ++ revision = IMX_CHIP_REVISION_1_4; ++ break; ++ case 5: ++ revision = IMX_CHIP_REVISION_1_5; ++ break; + default: + revision = IMX_CHIP_REVISION_UNKNOWN; + } +diff -Nur linux-3.14.36/arch/arm/mach-imx/busfreq_ddr3.c linux-openelec/arch/arm/mach-imx/busfreq_ddr3.c +--- linux-3.14.36/arch/arm/mach-imx/busfreq_ddr3.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/busfreq_ddr3.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,471 @@ ++/* ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file busfreq_ddr3.c ++ * ++ * @brief iMX6 DDR3 frequency change specific file. ++ * ++ * @ingroup PM ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hardware.h" ++ ++/* DDR settings */ ++static unsigned long (*iram_ddr_settings)[2]; ++static unsigned long (*normal_mmdc_settings)[2]; ++static unsigned long (*iram_iomux_settings)[2]; ++static void __iomem *mmdc_base; ++static void __iomem *iomux_base; ++static void __iomem *ccm_base; ++static void __iomem *l2_base; ++static void __iomem *gic_dist_base; ++static u32 *irqs_used; ++ ++static void *ddr_freq_change_iram_base; ++static int ddr_settings_size; ++static int iomux_settings_size; ++static volatile unsigned int cpus_in_wfe; ++static volatile bool wait_for_ddr_freq_update; ++static int curr_ddr_rate; ++ ++void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings, ++ bool dll_mode, void *iomux_offsets) = NULL; ++ ++extern unsigned int ddr_med_rate; ++extern unsigned int ddr_normal_rate; ++extern int low_bus_freq_mode; ++extern int audio_bus_freq_mode; ++extern void mx6_ddr3_freq_change(u32 freq, void *ddr_settings, ++ bool dll_mode, void *iomux_offsets); ++ ++#define MIN_DLL_ON_FREQ 333000000 ++#define MAX_DLL_OFF_FREQ 125000000 ++#define DDR_FREQ_CHANGE_SIZE 0x2000 ++ ++unsigned long ddr3_dll_mx6q[][2] = { ++ {0x0c, 0x0}, ++ {0x10, 0x0}, ++ {0x1C, 0x04088032}, ++ {0x1C, 0x0408803a}, ++ {0x1C, 0x08408030}, ++ {0x1C, 0x08408038}, ++ {0x818, 0x0}, ++}; ++ ++unsigned long ddr3_calibration[][2] = { ++ {0x83c, 0x0}, ++ {0x840, 0x0}, ++ {0x483c, 0x0}, ++ {0x4840, 0x0}, ++ {0x848, 0x0}, ++ {0x4848, 0x0}, ++ {0x850, 0x0}, ++ {0x4850, 0x0}, ++}; ++ ++unsigned long ddr3_dll_mx6dl[][2] = { ++ {0x0c, 0x0}, ++ {0x10, 0x0}, ++ {0x1C, 0x04008032}, ++ {0x1C, 0x0400803a}, ++ {0x1C, 0x07208030}, ++ {0x1C, 0x07208038}, ++ {0x818, 0x0}, ++}; ++ ++unsigned long iomux_offsets_mx6q[][2] = { ++ {0x5A8, 0x0}, ++ {0x5B0, 0x0}, ++ {0x524, 0x0}, ++ {0x51C, 0x0}, ++ {0x518, 0x0}, ++ {0x50C, 0x0}, ++ {0x5B8, 0x0}, ++ {0x5C0, 0x0}, ++}; ++ ++unsigned long iomux_offsets_mx6dl[][2] = { ++ {0x4BC, 0x0}, ++ {0x4C0, 0x0}, ++ {0x4C4, 0x0}, ++ {0x4C8, 0x0}, ++ {0x4CC, 0x0}, ++ {0x4D0, 0x0}, ++ {0x4D4, 0x0}, ++ {0x4D8, 0x0}, ++}; ++ ++unsigned long ddr3_400[][2] = { ++ {0x83c, 0x42490249}, ++ {0x840, 0x02470247}, ++ {0x483c, 0x42570257}, ++ {0x4840, 0x02400240}, ++ {0x848, 0x4039363C}, ++ {0x4848, 0x3A39333F}, ++ {0x850, 0x38414441}, ++ {0x4850, 0x472D4833} ++}; ++ ++int can_change_ddr_freq(void) ++{ ++ return 1; ++} ++ ++/* ++ * each active core apart from the one changing ++ * the DDR frequency will execute this function. ++ * the rest of the cores have to remain in WFE ++ * state until the frequency is changed. ++ */ ++irqreturn_t wait_in_wfe_irq(int irq, void *dev_id) ++{ ++ u32 me = smp_processor_id(); ++ ++ *((char *)(&cpus_in_wfe) + (u8)me) = 0xff; ++ ++ while (wait_for_ddr_freq_update) ++ wfe(); ++ ++ *((char *)(&cpus_in_wfe) + (u8)me) = 0; ++ ++ return IRQ_HANDLED; ++} ++ ++/* change the DDR frequency. */ ++int update_ddr_freq(int ddr_rate) ++{ ++ int i, j; ++ unsigned int reg; ++ bool dll_off = false; ++ unsigned int online_cpus = 0; ++ int cpu = 0; ++ int me; ++ ++ if (!can_change_ddr_freq()) ++ return -1; ++ ++ if (ddr_rate == curr_ddr_rate) ++ return 0; ++ ++ pr_debug("Bus freq set to %d start...\n", ddr_rate); ++ ++ if (low_bus_freq_mode || audio_bus_freq_mode) ++ dll_off = true; ++ ++ iram_ddr_settings[0][0] = ddr_settings_size; ++ iram_iomux_settings[0][0] = iomux_settings_size; ++ if (ddr_rate == ddr_med_rate && cpu_is_imx6q()) { ++ for (i = 0; i < ARRAY_SIZE(ddr3_dll_mx6q); i++) { ++ iram_ddr_settings[i + 1][0] = ++ normal_mmdc_settings[i][0]; ++ iram_ddr_settings[i + 1][1] = ++ normal_mmdc_settings[i][1]; ++ } ++ for (j = 0, i = ARRAY_SIZE(ddr3_dll_mx6q); ++ i < iram_ddr_settings[0][0]; j++, i++) { ++ iram_ddr_settings[i + 1][0] = ++ ddr3_400[j][0]; ++ iram_ddr_settings[i + 1][1] = ++ ddr3_400[j][1]; ++ } ++ } else if (ddr_rate == ddr_normal_rate) { ++ for (i = 0; i < iram_ddr_settings[0][0]; i++) { ++ iram_ddr_settings[i + 1][0] = ++ normal_mmdc_settings[i][0]; ++ iram_ddr_settings[i + 1][1] = ++ normal_mmdc_settings[i][1]; ++ } ++ } ++ ++ /* ensure that all Cores are in WFE. */ ++ local_irq_disable(); ++ ++ me = smp_processor_id(); ++ ++ *((char *)(&cpus_in_wfe) + (u8)me) = 0xff; ++ wait_for_ddr_freq_update = true; ++ for_each_online_cpu(cpu) { ++ *((char *)(&online_cpus) + (u8)cpu) = 0xff; ++ if (cpu != me) { ++ /* set the interrupt to be pending in the GIC. */ ++ reg = 1 << (irqs_used[cpu] % 32); ++ writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET ++ + (irqs_used[cpu] / 32) * 4); ++ } ++ } ++ while (cpus_in_wfe != online_cpus) ++ udelay(5); ++ ++ /* ++ * Flush the TLB, to ensure no TLB maintenance occurs ++ * when DDR is in self-refresh. ++ */ ++ local_flush_tlb_all(); ++ /* Now we can change the DDR frequency. */ ++ mx6_change_ddr_freq(ddr_rate, iram_ddr_settings, ++ dll_off, iram_iomux_settings); ++ ++ curr_ddr_rate = ddr_rate; ++ ++ /* DDR frequency change is done . */ ++ wait_for_ddr_freq_update = false; ++ ++ /* wake up all the cores. */ ++ sev(); ++ ++ *((char *)(&cpus_in_wfe) + (u8)me) = 0; ++ ++ local_irq_enable(); ++ ++ pr_debug("Bus freq set to %d done!\n", ddr_rate); ++ ++ return 0; ++} ++ ++int init_mmdc_ddr3_settings(struct platform_device *busfreq_pdev) ++{ ++ struct device *dev = &busfreq_pdev->dev; ++ struct platform_device *ocram_dev; ++ unsigned int iram_paddr; ++ int i, err; ++ u32 cpu; ++ struct device_node *node; ++ struct gen_pool *iram_pool; ++ ++ node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc-combine"); ++ if (!node) { ++ pr_err("failed to find imx6q-mmdc device tree data!\n"); ++ return -EINVAL; ++ } ++ mmdc_base = of_iomap(node, 0); ++ WARN(!mmdc_base, "unable to map mmdc registers\n"); ++ ++ node = NULL; ++ if (cpu_is_imx6q()) ++ node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-iomuxc"); ++ if (cpu_is_imx6dl()) ++ node = of_find_compatible_node(NULL, NULL, ++ "fsl,imx6dl-iomuxc"); ++ if (!node) { ++ pr_err("failed to find imx6q-iomux device tree data!\n"); ++ return -EINVAL; ++ } ++ iomux_base = of_iomap(node, 0); ++ WARN(!iomux_base, "unable to map iomux registers\n"); ++ ++ node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm"); ++ if (!node) { ++ pr_err("failed to find imx6q-ccm device tree data!\n"); ++ return -EINVAL; ++ } ++ ccm_base = of_iomap(node, 0); ++ WARN(!ccm_base, "unable to map mmdc registers\n"); ++ ++ node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); ++ if (!node) { ++ pr_err("failed to find imx6q-pl310-cache device tree data!\n"); ++ return -EINVAL; ++ } ++ l2_base = of_iomap(node, 0); ++ WARN(!ccm_base, "unable to map mmdc registers\n"); ++ ++ node = NULL; ++ node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic"); ++ if (!node) { ++ pr_err("failed to find imx6q-a9-gic device tree data!\n"); ++ return -EINVAL; ++ } ++ gic_dist_base = of_iomap(node, 0); ++ WARN(!gic_dist_base, "unable to map gic dist registers\n"); ++ ++ if (cpu_is_imx6q()) ++ ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6q) + ++ ARRAY_SIZE(ddr3_calibration); ++ if (cpu_is_imx6dl()) ++ ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6dl) + ++ ARRAY_SIZE(ddr3_calibration); ++ ++ normal_mmdc_settings = kmalloc((ddr_settings_size * 8), GFP_KERNEL); ++ if (cpu_is_imx6q()) { ++ memcpy(normal_mmdc_settings, ddr3_dll_mx6q, ++ sizeof(ddr3_dll_mx6q)); ++ memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6q)), ++ ddr3_calibration, sizeof(ddr3_calibration)); ++ } ++ if (cpu_is_imx6dl()) { ++ memcpy(normal_mmdc_settings, ddr3_dll_mx6dl, ++ sizeof(ddr3_dll_mx6dl)); ++ memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6dl)), ++ ddr3_calibration, sizeof(ddr3_calibration)); ++ } ++ /* store the original DDR settings at boot. */ ++ for (i = 0; i < ddr_settings_size; i++) { ++ /* ++ * writes via command mode register cannot be read back. ++ * hence hardcode them in the initial static array. ++ * this may require modification on a per customer basis. ++ */ ++ if (normal_mmdc_settings[i][0] != 0x1C) ++ normal_mmdc_settings[i][1] = ++ readl_relaxed(mmdc_base ++ + normal_mmdc_settings[i][0]); ++ } ++ ++ irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(), ++ GFP_KERNEL); ++ ++ for_each_online_cpu(cpu) { ++ int irq; ++ ++ /* ++ * set up a reserved interrupt to get all ++ * the active cores into a WFE state ++ * before changing the DDR frequency. ++ */ ++ irq = platform_get_irq(busfreq_pdev, cpu); ++ err = request_irq(irq, wait_in_wfe_irq, ++ IRQF_PERCPU, "mmdc_1", NULL); ++ if (err) { ++ dev_err(dev, ++ "Busfreq:request_irq failed %d, err = %d\n", ++ irq, err); ++ return err; ++ } ++ err = irq_set_affinity(irq, cpumask_of(cpu)); ++ if (err) { ++ dev_err(dev, ++ "Busfreq: Cannot set irq affinity irq=%d,\n", ++ irq); ++ return err; ++ } ++ irqs_used[cpu] = irq; ++ } ++ ++ node = NULL; ++ node = of_find_compatible_node(NULL, NULL, "mmio-sram"); ++ if (!node) { ++ dev_err(dev, "%s: failed to find ocram node\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ ocram_dev = of_find_device_by_node(node); ++ if (!ocram_dev) { ++ dev_err(dev, "failed to find ocram device!\n"); ++ return -EINVAL; ++ } ++ ++ iram_pool = dev_get_gen_pool(&ocram_dev->dev); ++ if (!iram_pool) { ++ dev_err(dev, "iram pool unavailable!\n"); ++ return -EINVAL; ++ } ++ ++ iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q); ++ iram_iomux_settings = gen_pool_alloc(iram_pool, ++ (iomux_settings_size * 8) + 8); ++ if (!iram_iomux_settings) { ++ dev_err(dev, "unable to alloc iram for IOMUX settings!\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * Allocate extra space to store the number of entries in the ++ * ddr_settings plus 4 extra regsiter information that needs ++ * to be passed to the frequency change code. ++ * sizeof(iram_ddr_settings) = sizeof(ddr_settings) + ++ * entries in ddr_settings + 16. ++ * The last 4 enties store the addresses of the registers: ++ * CCM_BASE_ADDR ++ * MMDC_BASE_ADDR ++ * IOMUX_BASE_ADDR ++ * L2X0_BASE_ADDR ++ */ ++ iram_ddr_settings = gen_pool_alloc(iram_pool, ++ (ddr_settings_size * 8) + 8 + 32); ++ if (!iram_ddr_settings) { ++ dev_err(dev, "unable to alloc iram for ddr settings!\n"); ++ return -ENOMEM; ++ } ++ i = ddr_settings_size + 1; ++ iram_ddr_settings[i][0] = (unsigned long)mmdc_base; ++ iram_ddr_settings[i+1][0] = (unsigned long)ccm_base; ++ iram_ddr_settings[i+2][0] = (unsigned long)iomux_base; ++ iram_ddr_settings[i+3][0] = (unsigned long)l2_base; ++ ++ if (cpu_is_imx6q()) { ++ /* store the IOMUX settings at boot. */ ++ for (i = 0; i < iomux_settings_size; i++) { ++ iomux_offsets_mx6q[i][1] = ++ readl_relaxed(iomux_base + ++ iomux_offsets_mx6q[i][0]); ++ iram_iomux_settings[i+1][0] = iomux_offsets_mx6q[i][0]; ++ iram_iomux_settings[i+1][1] = iomux_offsets_mx6q[i][1]; ++ } ++ } ++ ++ if (cpu_is_imx6dl()) { ++ for (i = 0; i < iomux_settings_size; i++) { ++ iomux_offsets_mx6dl[i][1] = ++ readl_relaxed(iomux_base + ++ iomux_offsets_mx6dl[i][0]); ++ iram_iomux_settings[i+1][0] = iomux_offsets_mx6dl[i][0]; ++ iram_iomux_settings[i+1][1] = iomux_offsets_mx6dl[i][1]; ++ } ++ } ++ ++ ddr_freq_change_iram_base = gen_pool_alloc(iram_pool, ++ DDR_FREQ_CHANGE_SIZE); ++ if (!ddr_freq_change_iram_base) { ++ dev_err(dev, "Cannot alloc iram for ddr freq change code!\n"); ++ return -ENOMEM; ++ } ++ ++ iram_paddr = gen_pool_virt_to_phys(iram_pool, ++ (unsigned long)ddr_freq_change_iram_base); ++ /* ++ * Need to remap the area here since we want ++ * the memory region to be executable. ++ */ ++ ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, ++ DDR_FREQ_CHANGE_SIZE, ++ MT_MEMORY_RWX_NONCACHED); ++ mx6_change_ddr_freq = (void *)fncpy(ddr_freq_change_iram_base, ++ &mx6_ddr3_freq_change, DDR_FREQ_CHANGE_SIZE); ++ ++ curr_ddr_rate = ddr_normal_rate; ++ ++ return 0; ++} +diff -Nur linux-3.14.36/arch/arm/mach-imx/busfreq-imx6.c linux-openelec/arch/arm/mach-imx/busfreq-imx6.c +--- linux-3.14.36/arch/arm/mach-imx/busfreq-imx6.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/busfreq-imx6.c 2015-07-24 18:03:30.408842002 -0500 +@@ -0,0 +1,953 @@ ++/* ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++/*! ++ * @file busfreq-imx6.c ++ * ++ * @brief A common API for the Freescale Semiconductor iMX6 Busfreq API ++ * ++ * The APIs are for setting bus frequency to different values based on the ++ * highest freqeuncy requested. ++ * ++ * @ingroup PM ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "hardware.h" ++ ++#define LPAPM_CLK 24000000 ++#define DDR3_AUDIO_CLK 50000000 ++#define LPDDR2_AUDIO_CLK 100000000 ++ ++int high_bus_freq_mode; ++int med_bus_freq_mode; ++int audio_bus_freq_mode; ++int low_bus_freq_mode; ++int ultra_low_bus_freq_mode; ++unsigned int ddr_med_rate; ++unsigned int ddr_normal_rate; ++ ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++static int bus_freq_scaling_initialized; ++static struct device *busfreq_dev; ++static int busfreq_suspended; ++static u32 org_arm_rate; ++static int bus_freq_scaling_is_active; ++static int high_bus_count, med_bus_count, audio_bus_count, low_bus_count; ++static unsigned int ddr_low_rate; ++ ++extern int init_mmdc_lpddr2_settings(struct platform_device *dev); ++extern int init_mmdc_ddr3_settings(struct platform_device *dev); ++extern int update_ddr_freq(int ddr_rate); ++extern int update_lpddr2_freq(int ddr_rate); ++ ++DEFINE_MUTEX(bus_freq_mutex); ++static DEFINE_SPINLOCK(freq_lock); ++ ++static struct clk *pll2_400; ++static struct clk *periph_clk; ++static struct clk *periph_pre_clk; ++static struct clk *periph_clk2_sel; ++static struct clk *periph_clk2; ++static struct clk *osc_clk; ++static struct clk *cpu_clk; ++static struct clk *pll3; ++static struct clk *pll2; ++static struct clk *pll2_200; ++static struct clk *pll1_sys; ++static struct clk *periph2_clk; ++static struct clk *ocram_clk; ++static struct clk *ahb_clk; ++static struct clk *pll1_sw_clk; ++static struct clk *periph2_pre_clk; ++static struct clk *periph2_clk2_sel; ++static struct clk *periph2_clk2; ++static struct clk *step_clk; ++static struct clk *axi_sel_clk; ++static struct clk *pll3_pfd1_540m; ++ ++static u32 pll2_org_rate; ++static struct delayed_work low_bus_freq_handler; ++static struct delayed_work bus_freq_daemon; ++ ++static void enter_lpm_imx6sl(void) ++{ ++ unsigned long flags; ++ ++ if (high_bus_freq_mode) { ++ pll2_org_rate = clk_get_rate(pll2); ++ /* Set periph_clk to be sourced from OSC_CLK */ ++ clk_set_parent(periph_clk2_sel, osc_clk); ++ clk_set_parent(periph_clk, periph_clk2); ++ /* Ensure AHB/AXI clks are at 24MHz. */ ++ clk_set_rate(ahb_clk, LPAPM_CLK); ++ clk_set_rate(ocram_clk, LPAPM_CLK); ++ } ++ if (audio_bus_count) { ++ /* Set AHB to 8MHz to lower pwer.*/ ++ clk_set_rate(ahb_clk, LPAPM_CLK / 3); ++ ++ /* Set up DDR to 100MHz. */ ++ spin_lock_irqsave(&freq_lock, flags); ++ update_lpddr2_freq(LPDDR2_AUDIO_CLK); ++ spin_unlock_irqrestore(&freq_lock, flags); ++ ++ /* Fix the clock tree in kernel */ ++ clk_set_rate(pll2, pll2_org_rate); ++ clk_set_parent(periph2_pre_clk, pll2_200); ++ clk_set_parent(periph2_clk, periph2_pre_clk); ++ ++ if (low_bus_freq_mode || ultra_low_bus_freq_mode) { ++ /* ++ * Swtich ARM to run off PLL2_PFD2_400MHz ++ * since DDR is anyway at 100MHz. ++ */ ++ clk_set_parent(step_clk, pll2_400); ++ clk_set_parent(pll1_sw_clk, step_clk); ++ /* ++ * Ensure that the clock will be ++ * at original speed. ++ */ ++ clk_set_rate(cpu_clk, org_arm_rate); ++ } ++ low_bus_freq_mode = 0; ++ ultra_low_bus_freq_mode = 0; ++ audio_bus_freq_mode = 1; ++ } else { ++ u32 arm_div, pll1_rate; ++ org_arm_rate = clk_get_rate(cpu_clk); ++ if (low_bus_freq_mode && low_bus_count == 0) { ++ /* ++ * We are already in DDR @ 24MHz state, but ++ * no one but ARM needs the DDR. In this case, ++ * we can lower the DDR freq to 1MHz when ARM ++ * enters WFI in this state. Keep track of this state. ++ */ ++ ultra_low_bus_freq_mode = 1; ++ low_bus_freq_mode = 0; ++ audio_bus_freq_mode = 0; ++ } else { ++ if (!ultra_low_bus_freq_mode && !low_bus_freq_mode) { ++ /* ++ * Set DDR to 24MHz. ++ * Since we are going to bypass PLL2, ++ * we need to move ARM clk off PLL2_PFD2 ++ * to PLL1. Make sure the PLL1 is running ++ * at the lowest possible freq. ++ */ ++ clk_set_rate(pll1_sys, ++ clk_round_rate(pll1_sys, org_arm_rate)); ++ pll1_rate = clk_get_rate(pll1_sys); ++ arm_div = pll1_rate / org_arm_rate + 1; ++ /* ++ * Ensure ARM CLK is lower before ++ * changing the parent. ++ */ ++ clk_set_rate(cpu_clk, org_arm_rate / arm_div); ++ /* Now set the ARM clk parent to PLL1_SYS. */ ++ clk_set_parent(pll1_sw_clk, pll1_sys); ++ ++ /* ++ * Set STEP_CLK back to OSC to save power and ++ * also to maintain the parent.The WFI iram code ++ * will switch step_clk to osc, but the clock API ++ * is not aware of the change and when a new request ++ * to change the step_clk parent to pll2_pfd2_400M ++ * is requested sometime later, the change is ignored. ++ */ ++ clk_set_parent(step_clk, osc_clk); ++ /* Now set DDR to 24MHz. */ ++ spin_lock_irqsave(&freq_lock, flags); ++ update_lpddr2_freq(LPAPM_CLK); ++ spin_unlock_irqrestore(&freq_lock, flags); ++ ++ /* ++ * Fix the clock tree in kernel. ++ * Make sure PLL2 rate is updated as it gets ++ * bypassed in the DDR freq change code. ++ */ ++ clk_set_rate(pll2, LPAPM_CLK); ++ clk_set_parent(periph2_clk2_sel, pll2); ++ clk_set_parent(periph2_clk, periph2_clk2_sel); ++ ++ } ++ if (low_bus_count == 0) { ++ ultra_low_bus_freq_mode = 1; ++ low_bus_freq_mode = 0; ++ } else { ++ ultra_low_bus_freq_mode = 0; ++ low_bus_freq_mode = 1; ++ } ++ audio_bus_freq_mode = 0; ++ } ++ } ++} ++ ++static void exit_lpm_imx6sl(void) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&freq_lock, flags); ++ /* Change DDR freq in IRAM. */ ++ update_lpddr2_freq(ddr_normal_rate); ++ spin_unlock_irqrestore(&freq_lock, flags); ++ ++ /* ++ * Fix the clock tree in kernel. ++ * Make sure PLL2 rate is updated as it gets ++ * un-bypassed in the DDR freq change code. ++ */ ++ clk_set_rate(pll2, pll2_org_rate); ++ clk_set_parent(periph2_pre_clk, pll2_400); ++ clk_set_parent(periph2_clk, periph2_pre_clk); ++ ++ /* Ensure that periph_clk is sourced from PLL2_400. */ ++ clk_set_parent(periph_pre_clk, pll2_400); ++ /* ++ * Before switching the perhiph_clk, ensure that the ++ * AHB/AXI will not be too fast. ++ */ ++ clk_set_rate(ahb_clk, LPAPM_CLK / 3); ++ clk_set_rate(ocram_clk, LPAPM_CLK / 2); ++ clk_set_parent(periph_clk, periph_pre_clk); ++ ++ if (low_bus_freq_mode || ultra_low_bus_freq_mode) { ++ /* Move ARM from PLL1_SW_CLK to PLL2_400. */ ++ clk_set_parent(step_clk, pll2_400); ++ clk_set_parent(pll1_sw_clk, step_clk); ++ clk_set_rate(cpu_clk, org_arm_rate); ++ ultra_low_bus_freq_mode = 0; ++ } ++} ++ ++int reduce_bus_freq(void) ++{ ++ int ret = 0; ++ clk_prepare_enable(pll3); ++ if (cpu_is_imx6sl()) ++ enter_lpm_imx6sl(); ++ else { ++ if (cpu_is_imx6dl() && (clk_get_parent(axi_sel_clk) ++ != periph_clk)) ++ /* Set axi to periph_clk */ ++ clk_set_parent(axi_sel_clk, periph_clk); ++ ++ if (audio_bus_count) { ++ /* Need to ensure that PLL2_PFD_400M is kept ON. */ ++ clk_prepare_enable(pll2_400); ++ update_ddr_freq(DDR3_AUDIO_CLK); ++ /* Make sure periph clk's parent also got updated */ ++ ret = clk_set_parent(periph_clk2_sel, pll3); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ ret = clk_set_parent(periph_pre_clk, pll2_200); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ ret = clk_set_parent(periph_clk, periph_pre_clk); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ audio_bus_freq_mode = 1; ++ low_bus_freq_mode = 0; ++ } else { ++ update_ddr_freq(LPAPM_CLK); ++ /* Make sure periph clk's parent also got updated */ ++ ret = clk_set_parent(periph_clk2_sel, osc_clk); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ /* Set periph_clk parent to OSC via periph_clk2_sel */ ++ ret = clk_set_parent(periph_clk, periph_clk2); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ if (audio_bus_freq_mode) ++ clk_disable_unprepare(pll2_400); ++ low_bus_freq_mode = 1; ++ audio_bus_freq_mode = 0; ++ } ++ } ++ clk_disable_unprepare(pll3); ++ ++ med_bus_freq_mode = 0; ++ high_bus_freq_mode = 0; ++ ++ if (audio_bus_freq_mode) ++ dev_dbg(busfreq_dev, "Bus freq set to audio mode. Count:\ ++ high %d, med %d, audio %d\n", ++ high_bus_count, med_bus_count, audio_bus_count); ++ if (low_bus_freq_mode) ++ dev_dbg(busfreq_dev, "Bus freq set to low mode. Count:\ ++ high %d, med %d, audio %d\n", ++ high_bus_count, med_bus_count, audio_bus_count); ++ ++ return ret; ++} ++ ++static void reduce_bus_freq_handler(struct work_struct *work) ++{ ++ mutex_lock(&bus_freq_mutex); ++ ++ reduce_bus_freq(); ++ ++ mutex_unlock(&bus_freq_mutex); ++} ++ ++/* ++ * Set the DDR, AHB to 24MHz. ++ * This mode will be activated only when none of the modules that ++ * need a higher DDR or AHB frequency are active. ++ */ ++int set_low_bus_freq(void) ++{ ++ if (busfreq_suspended) ++ return 0; ++ ++ if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) ++ return 0; ++ ++ /* ++ * Check to see if we need to got from ++ * low bus freq mode to audio bus freq mode. ++ * If so, the change needs to be done immediately. ++ */ ++ if (audio_bus_count && (low_bus_freq_mode || ultra_low_bus_freq_mode)) ++ reduce_bus_freq(); ++ else ++ /* ++ * Don't lower the frequency immediately. Instead ++ * scheduled a delayed work and drop the freq if ++ * the conditions still remain the same. ++ */ ++ schedule_delayed_work(&low_bus_freq_handler, ++ usecs_to_jiffies(3000000)); ++ return 0; ++} ++ ++/* ++ * Set the DDR to either 528MHz or 400MHz for iMX6qd ++ * or 400MHz for iMX6dl. ++ */ ++int set_high_bus_freq(int high_bus_freq) ++{ ++ int ret = 0; ++ struct clk *periph_clk_parent; ++ ++ if (bus_freq_scaling_initialized && bus_freq_scaling_is_active) ++ cancel_delayed_work_sync(&low_bus_freq_handler); ++ ++ if (busfreq_suspended) ++ return 0; ++ ++ if (cpu_is_imx6q()) ++ periph_clk_parent = pll2; ++ else ++ periph_clk_parent = pll2_400; ++ ++ if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) ++ return 0; ++ ++ if (high_bus_freq_mode) ++ return 0; ++ ++ /* medium bus freq is only supported for MX6DQ */ ++ if (med_bus_freq_mode && !high_bus_freq) ++ return 0; ++ ++ clk_prepare_enable(pll3); ++ if (cpu_is_imx6sl()) ++ exit_lpm_imx6sl(); ++ else { ++ if (high_bus_freq) { ++ update_ddr_freq(ddr_normal_rate); ++ /* Make sure periph clk's parent also got updated */ ++ ret = clk_set_parent(periph_clk2_sel, pll3); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ ret = clk_set_parent(periph_pre_clk, periph_clk_parent); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ ret = clk_set_parent(periph_clk, periph_pre_clk); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ if (cpu_is_imx6dl() && (clk_get_parent(axi_sel_clk) ++ != pll3_pfd1_540m)) ++ /* Set axi to pll3_pfd1_540m */ ++ clk_set_parent(axi_sel_clk, pll3_pfd1_540m); ++ } else { ++ update_ddr_freq(ddr_med_rate); ++ /* Make sure periph clk's parent also got updated */ ++ ret = clk_set_parent(periph_clk2_sel, pll3); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ ret = clk_set_parent(periph_pre_clk, pll2_400); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ ret = clk_set_parent(periph_clk, periph_pre_clk); ++ if (ret) ++ dev_WARN(busfreq_dev, ++ "%s: %d: clk set parent fail!\n", ++ __func__, __LINE__); ++ } ++ if (audio_bus_freq_mode) ++ clk_disable_unprepare(pll2_400); ++ } ++ ++ high_bus_freq_mode = 1; ++ med_bus_freq_mode = 0; ++ low_bus_freq_mode = 0; ++ audio_bus_freq_mode = 0; ++ ++ clk_disable_unprepare(pll3); ++ ++ if (high_bus_freq_mode) ++ dev_dbg(busfreq_dev, "Bus freq set to high mode. Count:\ ++ high %d, med %d, audio %d\n", ++ high_bus_count, med_bus_count, audio_bus_count); ++ if (med_bus_freq_mode) ++ dev_dbg(busfreq_dev, "Bus freq set to med mode. Count:\ ++ high %d, med %d, audio %d\n", ++ high_bus_count, med_bus_count, audio_bus_count); ++ ++ return 0; ++} ++#endif ++ ++void request_bus_freq(enum bus_freq_mode mode) ++{ ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++ mutex_lock(&bus_freq_mutex); ++ ++ if (mode == BUS_FREQ_HIGH) ++ high_bus_count++; ++ else if (mode == BUS_FREQ_MED) ++ med_bus_count++; ++ else if (mode == BUS_FREQ_AUDIO) ++ audio_bus_count++; ++ else if (mode == BUS_FREQ_LOW) ++ low_bus_count++; ++ ++ if (busfreq_suspended || !bus_freq_scaling_initialized || ++ !bus_freq_scaling_is_active) { ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ cancel_delayed_work_sync(&low_bus_freq_handler); ++ ++ if (cpu_is_imx6dl()) { ++ /* No support for medium setpoint on MX6DL. */ ++ if (mode == BUS_FREQ_MED) { ++ high_bus_count++; ++ mode = BUS_FREQ_HIGH; ++ } ++ } ++ ++ if ((mode == BUS_FREQ_HIGH) && (!high_bus_freq_mode)) { ++ set_high_bus_freq(1); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ ++ if ((mode == BUS_FREQ_MED) && (!high_bus_freq_mode) && ++ (!med_bus_freq_mode)) { ++ set_high_bus_freq(0); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ if ((mode == BUS_FREQ_AUDIO) && (!high_bus_freq_mode) && ++ (!med_bus_freq_mode) && (!audio_bus_freq_mode)) { ++ set_low_bus_freq(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ mutex_unlock(&bus_freq_mutex); ++#endif ++ return; ++} ++EXPORT_SYMBOL(request_bus_freq); ++ ++void release_bus_freq(enum bus_freq_mode mode) ++{ ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++ mutex_lock(&bus_freq_mutex); ++ ++ if (mode == BUS_FREQ_HIGH) { ++ if (high_bus_count == 0) { ++ dev_err(busfreq_dev, "high bus count mismatch!\n"); ++ dump_stack(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ high_bus_count--; ++ } else if (mode == BUS_FREQ_MED) { ++ if (med_bus_count == 0) { ++ dev_err(busfreq_dev, "med bus count mismatch!\n"); ++ dump_stack(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ med_bus_count--; ++ } else if (mode == BUS_FREQ_AUDIO) { ++ if (audio_bus_count == 0) { ++ dev_err(busfreq_dev, "audio bus count mismatch!\n"); ++ dump_stack(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ audio_bus_count--; ++ } else if (mode == BUS_FREQ_LOW) { ++ if (low_bus_count == 0) { ++ dev_err(busfreq_dev, "low bus count mismatch!\n"); ++ dump_stack(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ low_bus_count--; ++ } ++ ++ if (busfreq_suspended || !bus_freq_scaling_initialized || ++ !bus_freq_scaling_is_active) { ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ ++ if (cpu_is_imx6dl()) { ++ /* No support for medium setpoint on MX6DL. */ ++ if (mode == BUS_FREQ_MED) { ++ high_bus_count--; ++ mode = BUS_FREQ_HIGH; ++ } ++ } ++ ++ if ((!audio_bus_freq_mode) && (high_bus_count == 0) && ++ (med_bus_count == 0) && (audio_bus_count != 0)) { ++ set_low_bus_freq(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ if ((!low_bus_freq_mode) && (high_bus_count == 0) && ++ (med_bus_count == 0) && (audio_bus_count == 0) && ++ (low_bus_count != 0)) { ++ set_low_bus_freq(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ if ((!ultra_low_bus_freq_mode) && (high_bus_count == 0) && ++ (med_bus_count == 0) && (audio_bus_count == 0) && ++ (low_bus_count == 0)) { ++ set_low_bus_freq(); ++ mutex_unlock(&bus_freq_mutex); ++ return; ++ } ++ ++ mutex_unlock(&bus_freq_mutex); ++#endif ++ return; ++} ++EXPORT_SYMBOL(release_bus_freq); ++ ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++static void bus_freq_daemon_handler(struct work_struct *work) ++{ ++ mutex_lock(&bus_freq_mutex); ++ if ((!low_bus_freq_mode) && (high_bus_count == 0) && ++ (med_bus_count == 0) && (audio_bus_count == 0)) ++ set_low_bus_freq(); ++ mutex_unlock(&bus_freq_mutex); ++} ++ ++static ssize_t bus_freq_scaling_enable_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ if (bus_freq_scaling_is_active) ++ return sprintf(buf, "Bus frequency scaling is enabled\n"); ++ else ++ return sprintf(buf, "Bus frequency scaling is disabled\n"); ++} ++ ++static ssize_t bus_freq_scaling_enable_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ if (strncmp(buf, "1", 1) == 0) { ++ bus_freq_scaling_is_active = 1; ++ set_high_bus_freq(1); ++ /* ++ * We set bus freq to highest at the beginning, ++ * so we use this daemon thread to make sure system ++ * can enter low bus mode if ++ * there is no high bus request pending ++ */ ++ schedule_delayed_work(&bus_freq_daemon, ++ usecs_to_jiffies(5000000)); ++ } else if (strncmp(buf, "0", 1) == 0) { ++ if (bus_freq_scaling_is_active) ++ set_high_bus_freq(1); ++ bus_freq_scaling_is_active = 0; ++ } ++ return size; ++} ++ ++static int bus_freq_pm_notify(struct notifier_block *nb, unsigned long event, ++ void *dummy) ++{ ++ mutex_lock(&bus_freq_mutex); ++ ++ if (event == PM_SUSPEND_PREPARE) { ++ high_bus_count++; ++ set_high_bus_freq(1); ++ busfreq_suspended = 1; ++ } else if (event == PM_POST_SUSPEND) { ++ busfreq_suspended = 0; ++ high_bus_count--; ++ schedule_delayed_work(&bus_freq_daemon, ++ usecs_to_jiffies(5000000)); ++ } ++ ++ mutex_unlock(&bus_freq_mutex); ++ ++ return NOTIFY_OK; ++} ++ ++static int busfreq_reboot_notifier_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ /* System is rebooting. Set the system into high_bus_freq_mode. */ ++ request_bus_freq(BUS_FREQ_HIGH); ++ ++ return 0; ++} ++ ++static struct notifier_block imx_bus_freq_pm_notifier = { ++ .notifier_call = bus_freq_pm_notify, ++}; ++ ++static struct notifier_block imx_busfreq_reboot_notifier = { ++ .notifier_call = busfreq_reboot_notifier_event, ++}; ++ ++ ++static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show, ++ bus_freq_scaling_enable_store); ++#endif ++ ++/*! ++ * This is the probe routine for the bus frequency driver. ++ * ++ * @param pdev The platform device structure ++ * ++ * @return The function returns 0 on success ++ * ++ */ ++ ++static int busfreq_probe(struct platform_device *pdev) ++{ ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++ u32 err; ++ ++ busfreq_dev = &pdev->dev; ++ ++ pll2_400 = devm_clk_get(&pdev->dev, "pll2_pfd2_396m"); ++ if (IS_ERR(pll2_400)) { ++ dev_err(busfreq_dev, "%s: failed to get pll2_pfd2_396m\n", ++ __func__); ++ return PTR_ERR(pll2_400); ++ } ++ ++ pll2_200 = devm_clk_get(&pdev->dev, "pll2_198m"); ++ if (IS_ERR(pll2_200)) { ++ dev_err(busfreq_dev, "%s: failed to get pll2_198m\n", ++ __func__); ++ return PTR_ERR(pll2_200); ++ } ++ ++ pll2 = devm_clk_get(&pdev->dev, "pll2_bus"); ++ if (IS_ERR(pll2)) { ++ dev_err(busfreq_dev, "%s: failed to get pll2_bus\n", ++ __func__); ++ return PTR_ERR(pll2); ++ } ++ ++ cpu_clk = devm_clk_get(&pdev->dev, "arm"); ++ if (IS_ERR(cpu_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get cpu_clk\n", ++ __func__); ++ return PTR_ERR(cpu_clk); ++ } ++ ++ pll3 = devm_clk_get(&pdev->dev, "pll3_usb_otg"); ++ if (IS_ERR(pll3)) { ++ dev_err(busfreq_dev, "%s: failed to get pll3_usb_otg\n", ++ __func__); ++ return PTR_ERR(pll3); ++ } ++ ++ periph_clk = devm_clk_get(&pdev->dev, "periph"); ++ if (IS_ERR(periph_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get periph\n", ++ __func__); ++ return PTR_ERR(periph_clk); ++ } ++ ++ periph_pre_clk = devm_clk_get(&pdev->dev, "periph_pre"); ++ if (IS_ERR(periph_pre_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get periph_pre\n", ++ __func__); ++ return PTR_ERR(periph_pre_clk); ++ } ++ ++ periph_clk2 = devm_clk_get(&pdev->dev, "periph_clk2"); ++ if (IS_ERR(periph_clk2)) { ++ dev_err(busfreq_dev, "%s: failed to get periph_clk2\n", ++ __func__); ++ return PTR_ERR(periph_clk2); ++ } ++ ++ periph_clk2_sel = devm_clk_get(&pdev->dev, "periph_clk2_sel"); ++ if (IS_ERR(periph_clk2_sel)) { ++ dev_err(busfreq_dev, "%s: failed to get periph_clk2_sel\n", ++ __func__); ++ return PTR_ERR(periph_clk2_sel); ++ } ++ ++ osc_clk = devm_clk_get(&pdev->dev, "osc"); ++ if (IS_ERR(osc_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get osc_clk\n", ++ __func__); ++ return PTR_ERR(osc_clk); ++ } ++ ++ if (cpu_is_imx6dl()) { ++ axi_sel_clk = devm_clk_get(&pdev->dev, "axi_sel"); ++ if (IS_ERR(axi_sel_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get axi_sel_clk\n", ++ __func__); ++ return PTR_ERR(axi_sel_clk); ++ } ++ ++ pll3_pfd1_540m = devm_clk_get(&pdev->dev, "pll3_pfd1_540m"); ++ if (IS_ERR(pll3_pfd1_540m)) { ++ dev_err(busfreq_dev, ++ "%s: failed to get pll3_pfd1_540m\n", __func__); ++ return PTR_ERR(pll3_pfd1_540m); ++ } ++ } ++ ++ if (cpu_is_imx6sl()) { ++ pll1_sys = devm_clk_get(&pdev->dev, "pll1_sys"); ++ if (IS_ERR(pll1_sys)) { ++ dev_err(busfreq_dev, "%s: failed to get pll1_sys\n", ++ __func__); ++ return PTR_ERR(pll1_sys); ++ } ++ ++ ahb_clk = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(ahb_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get ahb_clk\n", ++ __func__); ++ return PTR_ERR(ahb_clk); ++ } ++ ++ ocram_clk = devm_clk_get(&pdev->dev, "ocram"); ++ if (IS_ERR(ocram_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get ocram_clk\n", ++ __func__); ++ return PTR_ERR(ocram_clk); ++ } ++ ++ pll1_sw_clk = devm_clk_get(&pdev->dev, "pll1_sw"); ++ if (IS_ERR(pll1_sw_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get pll1_sw_clk\n", ++ __func__); ++ return PTR_ERR(pll1_sw_clk); ++ } ++ ++ periph2_clk = devm_clk_get(&pdev->dev, "periph2"); ++ if (IS_ERR(periph2_clk)) { ++ dev_err(busfreq_dev, "%s: failed to get periph2\n", ++ __func__); ++ return PTR_ERR(periph2_clk); ++ } ++ ++ periph2_pre_clk = devm_clk_get(&pdev->dev, "periph2_pre"); ++ if (IS_ERR(periph2_pre_clk)) { ++ dev_err(busfreq_dev, ++ "%s: failed to get periph2_pre_clk\n", ++ __func__); ++ return PTR_ERR(periph2_pre_clk); ++ } ++ ++ periph2_clk2 = devm_clk_get(&pdev->dev, "periph2_clk2"); ++ if (IS_ERR(periph2_clk2)) { ++ dev_err(busfreq_dev, ++ "%s: failed to get periph2_clk2\n", ++ __func__); ++ return PTR_ERR(periph2_clk2); ++ } ++ ++ periph2_clk2_sel = devm_clk_get(&pdev->dev, "periph2_clk2_sel"); ++ if (IS_ERR(periph2_clk2_sel)) { ++ dev_err(busfreq_dev, ++ "%s: failed to get periph2_clk2_sel\n", ++ __func__); ++ return PTR_ERR(periph2_clk2_sel); ++ } ++ ++ step_clk = devm_clk_get(&pdev->dev, "step"); ++ if (IS_ERR(step_clk)) { ++ dev_err(busfreq_dev, ++ "%s: failed to get step_clk\n", ++ __func__); ++ return PTR_ERR(periph2_clk2_sel); ++ } ++ ++ } ++ ++ err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr); ++ if (err) { ++ dev_err(busfreq_dev, ++ "Unable to register sysdev entry for BUSFREQ"); ++ return err; ++ } ++ ++ if (of_property_read_u32(pdev->dev.of_node, "fsl,max_ddr_freq", ++ &ddr_normal_rate)) { ++ dev_err(busfreq_dev, "max_ddr_freq entry missing\n"); ++ return -EINVAL; ++ } ++#endif ++ ++ high_bus_freq_mode = 1; ++ med_bus_freq_mode = 0; ++ low_bus_freq_mode = 0; ++ audio_bus_freq_mode = 0; ++ ultra_low_bus_freq_mode = 0; ++ ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++ bus_freq_scaling_is_active = 1; ++ bus_freq_scaling_initialized = 1; ++ ++ ddr_low_rate = LPAPM_CLK; ++ if (cpu_is_imx6q()) { ++ if (of_property_read_u32(pdev->dev.of_node, "fsl,med_ddr_freq", ++ &ddr_med_rate)) { ++ dev_info(busfreq_dev, ++ "DDR medium rate not supported.\n"); ++ ddr_med_rate = ddr_normal_rate; ++ } ++ } ++ ++ INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler); ++ INIT_DELAYED_WORK(&bus_freq_daemon, bus_freq_daemon_handler); ++ register_pm_notifier(&imx_bus_freq_pm_notifier); ++ register_reboot_notifier(&imx_busfreq_reboot_notifier); ++ ++ if (cpu_is_imx6sl()) ++ err = init_mmdc_lpddr2_settings(pdev); ++ else ++ err = init_mmdc_ddr3_settings(pdev); ++ if (err) { ++ dev_err(busfreq_dev, "Busfreq init of MMDC failed\n"); ++ return err; ++ } ++#endif ++ return 0; ++} ++ ++static const struct of_device_id imx6_busfreq_ids[] = { ++ { .compatible = "fsl,imx6_busfreq", }, ++ { /* sentinel */ } ++}; ++ ++static struct platform_driver busfreq_driver = { ++ .driver = { ++ .name = "imx6_busfreq", ++ .owner = THIS_MODULE, ++ .of_match_table = imx6_busfreq_ids, ++ }, ++ .probe = busfreq_probe, ++}; ++ ++/*! ++ * Initialise the busfreq_driver. ++ * ++ * @return The function always returns 0. ++ */ ++ ++static int __init busfreq_init(void) ++{ ++#ifndef CONFIG_MX6_VPU_352M ++ if (platform_driver_register(&busfreq_driver) != 0) ++ return -ENODEV; ++ ++ printk(KERN_INFO "Bus freq driver module loaded\n"); ++#endif ++ return 0; ++} ++ ++static void __exit busfreq_cleanup(void) ++{ ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++ sysfs_remove_file(&busfreq_dev->kobj, &dev_attr_enable.attr); ++ ++ bus_freq_scaling_initialized = 0; ++#endif ++ /* Unregister the device structure */ ++ platform_driver_unregister(&busfreq_driver); ++} ++ ++module_init(busfreq_init); ++module_exit(busfreq_cleanup); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("BusFreq driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/arch/arm/mach-imx/busfreq_lpddr2.c linux-openelec/arch/arm/mach-imx/busfreq_lpddr2.c +--- linux-3.14.36/arch/arm/mach-imx/busfreq_lpddr2.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/busfreq_lpddr2.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,183 @@ ++/* ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file busfreq_lpddr2.c ++ * ++ * @brief iMX6 LPDDR2 frequency change specific file. ++ * ++ * @ingroup PM ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hardware.h" ++ ++/* DDR settings */ ++static void __iomem *mmdc_base; ++static void __iomem *anatop_base; ++static void __iomem *ccm_base; ++static void __iomem *l2_base; ++static struct device *busfreq_dev; ++static void *ddr_freq_change_iram_base; ++static int curr_ddr_rate; ++ ++unsigned long reg_addrs[4]; ++ ++void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode, ++ void *iram_addr) = NULL; ++ ++extern unsigned int ddr_normal_rate; ++extern int low_bus_freq_mode; ++extern int ultra_low_bus_freq_mode; ++extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode, ++ void *iram_addr); ++ ++ ++#define LPDDR2_FREQ_CHANGE_SIZE 0x1000 ++ ++ ++/* change the DDR frequency. */ ++int update_lpddr2_freq(int ddr_rate) ++{ ++ if (ddr_rate == curr_ddr_rate) ++ return 0; ++ ++ dev_dbg(busfreq_dev, "\nBus freq set to %d start...\n", ddr_rate); ++ ++ /* ++ * Flush the TLB, to ensure no TLB maintenance occurs ++ * when DDR is in self-refresh. ++ */ ++ local_flush_tlb_all(); ++ /* Now change DDR frequency. */ ++ mx6_change_lpddr2_freq(ddr_rate, ++ (low_bus_freq_mode | ultra_low_bus_freq_mode), ++ reg_addrs); ++ ++ curr_ddr_rate = ddr_rate; ++ ++ dev_dbg(busfreq_dev, "\nBus freq set to %d done...\n", ddr_rate); ++ ++ return 0; ++} ++ ++int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev) ++{ ++ struct platform_device *ocram_dev; ++ unsigned int iram_paddr; ++ struct device_node *node; ++ struct gen_pool *iram_pool; ++ ++ busfreq_dev = &busfreq_pdev->dev; ++ node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-mmdc"); ++ if (!node) { ++ printk(KERN_ERR "failed to find imx6sl-mmdc device tree data!\n"); ++ return -EINVAL; ++ } ++ mmdc_base = of_iomap(node, 0); ++ WARN(!mmdc_base, "unable to map mmdc registers\n"); ++ ++ node = NULL; ++ node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-ccm"); ++ if (!node) { ++ printk(KERN_ERR "failed to find imx6sl-ccm device tree data!\n"); ++ return -EINVAL; ++ } ++ ccm_base = of_iomap(node, 0); ++ WARN(!ccm_base, "unable to map ccm registers\n"); ++ ++ node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); ++ if (!node) { ++ printk(KERN_ERR "failed to find imx6sl-pl310-cache device tree data!\n"); ++ return -EINVAL; ++ } ++ l2_base = of_iomap(node, 0); ++ WARN(!l2_base, "unable to map PL310 registers\n"); ++ ++ node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop"); ++ if (!node) { ++ printk(KERN_ERR "failed to find imx6sl-pl310-cache device tree data!\n"); ++ return -EINVAL; ++ } ++ anatop_base = of_iomap(node, 0); ++ WARN(!anatop_base, "unable to map anatop registers\n"); ++ ++ node = NULL; ++ node = of_find_compatible_node(NULL, NULL, "mmio-sram"); ++ if (!node) { ++ dev_err(busfreq_dev, "%s: failed to find ocram node\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ ocram_dev = of_find_device_by_node(node); ++ if (!ocram_dev) { ++ dev_err(busfreq_dev, "failed to find ocram device!\n"); ++ return -EINVAL; ++ } ++ ++ iram_pool = dev_get_gen_pool(&ocram_dev->dev); ++ if (!iram_pool) { ++ dev_err(busfreq_dev, "iram pool unavailable!\n"); ++ return -EINVAL; ++ } ++ ++ reg_addrs[0] = (unsigned long)anatop_base; ++ reg_addrs[1] = (unsigned long)ccm_base; ++ reg_addrs[2] = (unsigned long)mmdc_base; ++ reg_addrs[3] = (unsigned long)l2_base; ++ ++ ddr_freq_change_iram_base = (void *)gen_pool_alloc(iram_pool, ++ LPDDR2_FREQ_CHANGE_SIZE); ++ if (!ddr_freq_change_iram_base) { ++ dev_err(busfreq_dev, ++ "Cannot alloc iram for ddr freq change code!\n"); ++ return -ENOMEM; ++ } ++ ++ iram_paddr = gen_pool_virt_to_phys(iram_pool, ++ (unsigned long)ddr_freq_change_iram_base); ++ /* ++ * Need to remap the area here since we want ++ * the memory region to be executable. ++ */ ++ ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, ++ LPDDR2_FREQ_CHANGE_SIZE, ++ MT_MEMORY_RWX_NONCACHED); ++ mx6_change_lpddr2_freq = (void *)fncpy(ddr_freq_change_iram_base, ++ &mx6_lpddr2_freq_change, LPDDR2_FREQ_CHANGE_SIZE); ++ ++ curr_ddr_rate = ddr_normal_rate; ++ ++ return 0; ++} +diff -Nur linux-3.14.36/arch/arm/mach-imx/clk.h linux-openelec/arch/arm/mach-imx/clk.h +--- linux-3.14.36/arch/arm/mach-imx/clk.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/clk.h 2015-05-06 12:05:43.000000000 -0500 +@@ -23,7 +23,8 @@ + }; + + struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, +- const char *parent_name, void __iomem *base, u32 div_mask); ++ const char *parent_name, void __iomem *base, ++ u32 div_mask, bool always_on); + + struct clk *clk_register_gate2(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, +diff -Nur linux-3.14.36/arch/arm/mach-imx/clk-imx6q.c linux-openelec/arch/arm/mach-imx/clk-imx6q.c +--- linux-3.14.36/arch/arm/mach-imx/clk-imx6q.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/clk-imx6q.c 2015-07-24 18:03:30.408842002 -0500 +@@ -1,5 +1,5 @@ + /* +- * Copyright 2011-2013 Freescale Semiconductor, Inc. ++ * Copyright 2011-2014 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public +@@ -24,6 +24,8 @@ + #include "common.h" + #include "hardware.h" + ++#define CCM_CCGR_OFFSET(index) (index * 2) ++ + static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; + static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; + static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +@@ -39,6 +41,8 @@ + static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll3_pfd0_720m", }; + static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; + static const char *ldb_di_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_usb_otg", }; ++static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; ++static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; + static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", }; + static const char *ipu1_di0_sels[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; + static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; +@@ -72,6 +76,10 @@ + "pll4_audio", "pll5_video", "pll8_mlb", "enet_ref", + "pcie_ref", "sata_ref", + }; ++static const char *pll_av_sels[] = { "osc", "lvds1_in", "lvds2_in", "dummy", }; ++static void __iomem *anatop_base; ++static void __iomem *ccm_base; ++ + + enum mx6q_clks { + dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m, +@@ -88,11 +96,11 @@ + periph_clk2, periph2_clk2, ipg, ipg_per, esai_pred, esai_podf, + asrc_pred, asrc_podf, spdif_pred, spdif_podf, can_root, ecspi_root, + gpu2d_core_podf, gpu3d_core_podf, gpu3d_shader, ipu1_podf, ipu2_podf, +- ldb_di0_podf, ldb_di1_podf, ipu1_di0_pre, ipu1_di1_pre, ipu2_di0_pre, +- ipu2_di1_pre, hsi_tx_podf, ssi1_pred, ssi1_podf, ssi2_pred, ssi2_podf, +- ssi3_pred, ssi3_podf, uart_serial_podf, usdhc1_podf, usdhc2_podf, +- usdhc3_podf, usdhc4_podf, enfc_pred, enfc_podf, emi_podf, +- emi_slow_podf, vpu_axi_podf, cko1_podf, axi, mmdc_ch0_axi_podf, ++ ldb_di0_podf_unused, ldb_di1_podf_unused, ipu1_di0_pre, ipu1_di1_pre, ++ ipu2_di0_pre, ipu2_di1_pre, hsi_tx_podf, ssi1_pred, ssi1_podf, ++ ssi2_pred, ssi2_podf, ssi3_pred, ssi3_podf, uart_serial_podf, ++ usdhc1_podf, usdhc2_podf, usdhc3_podf, usdhc4_podf, enfc_pred, enfc_podf, ++ emi_podf, emi_slow_podf, vpu_axi_podf, cko1_podf, axi, mmdc_ch0_axi_podf, + mmdc_ch1_axi_podf, arm, ahb, apbh_dma, asrc, can1_ipg, can1_serial, + can2_ipg, can2_serial, ecspi1, ecspi2, ecspi3, ecspi4, ecspi5, enet, + esai, gpt_ipg, gpt_ipg_per, gpu2d_core, gpu3d_core, hdmi_iahb, +@@ -107,7 +115,10 @@ + sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate, + usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, eim_slow, + spdif, cko2_sel, cko2_podf, cko2, cko, vdoa, pll4_audio_div, +- lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, clk_max ++ lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, gpt_3m, video_27m, ++ ldb_di0_div_7, ldb_di1_div_7, ldb_di0_div_sel, ldb_di1_div_sel, ++ caam_mem, caam_aclk, caam_ipg, epit1, epit2, tzasc2, lvds1_in, lvds1_out, ++ pll4_sel, lvds2_in, lvds2_out, anaclk1, anaclk2, clk_max + }; + + static struct clk *clk[clk_max]; +@@ -140,20 +151,131 @@ + { /* sentinel */ } + }; + ++static void init_ldb_clks(enum mx6q_clks new_parent) ++{ ++ u32 reg; ++ ++ /* ++ * Need to follow a strict procedure when changing the LDB ++ * clock, else we can introduce a glitch. Things to keep in ++ * mind: ++ * 1. The current and new parent clocks must be disabled. ++ * 2. The default clock for ldb_dio_clk is mmdc_ch1 which has ++ * no CG bit. ++ * 3. In the RTL implementation of the LDB_DI_CLK_SEL mux ++ * the top four options are in one mux and the PLL3 option along ++ * with another option is in the second mux. There is third mux ++ * used to decide between the first and second mux. ++ * The code below switches the parent to the bottom mux first ++ * and then manipulates the top mux. This ensures that no glitch ++ * will enter the divider. ++ * ++ * Need to disable MMDC_CH1 clock manually as there is no CG bit ++ * for this clock. The only way to disable this clock is to move ++ * it topll3_sw_clk and then to disable pll3_sw_clk ++ * Make sure periph2_clk2_sel is set to pll3_sw_clk ++ */ ++ reg = readl_relaxed(ccm_base + 0x18); ++ reg &= ~(1 << 20); ++ writel_relaxed(reg, ccm_base + 0x18); ++ ++ /* ++ * Set MMDC_CH1 mask bit. ++ */ ++ reg = readl_relaxed(ccm_base + 0x4); ++ reg |= 1 << 16; ++ writel_relaxed(reg, ccm_base + 0x4); ++ ++ /* ++ * Set the periph2_clk_sel to the top mux so that ++ * mmdc_ch1 is from pll3_sw_clk. ++ */ ++ reg = readl_relaxed(ccm_base + 0x14); ++ reg |= 1 << 26; ++ writel_relaxed(reg, ccm_base + 0x14); ++ ++ /* ++ * Wait for the clock switch. ++ */ ++ while (readl_relaxed(ccm_base + 0x48)) ++ ; ++ ++ /* ++ * Disable pll3_sw_clk by selecting the bypass clock source. ++ */ ++ reg = readl_relaxed(ccm_base + 0xc); ++ reg |= 1 << 0; ++ writel_relaxed(reg, ccm_base + 0xc); ++ ++ /* ++ * Set the ldb_di0_clk and ldb_di1_clk to 111b. ++ */ ++ reg = readl_relaxed(ccm_base + 0x2c); ++ reg |= ((7 << 9) | (7 << 12)); ++ writel_relaxed(reg, ccm_base + 0x2c); ++ ++ /* ++ * Set the ldb_di0_clk and ldb_di1_clk to 100b. ++ */ ++ reg = readl_relaxed(ccm_base + 0x2c); ++ reg &= ~((7 << 9) | (7 << 12)); ++ reg |= ((4 << 9) | (4 << 12)); ++ writel_relaxed(reg, ccm_base + 0x2c); ++ ++ /* ++ * Perform the LDB parent clock switch. ++ */ ++ clk_set_parent(clk[ldb_di0_sel], clk[new_parent]); ++ clk_set_parent(clk[ldb_di1_sel], clk[new_parent]); ++ ++ /* ++ * Unbypass pll3_sw_clk. ++ */ ++ reg = readl_relaxed(ccm_base + 0xc); ++ reg &= ~(1 << 0); ++ writel_relaxed(reg, ccm_base + 0xc); ++ ++ /* ++ * Set the periph2_clk_sel back to the bottom mux so that ++ * mmdc_ch1 is from its original parent. ++ */ ++ reg = readl_relaxed(ccm_base + 0x14); ++ reg &= ~(1 << 26); ++ writel_relaxed(reg, ccm_base + 0x14); ++ ++ /* ++ * Wait for the clock switch. ++ */ ++ while (readl_relaxed(ccm_base + 0x48)) ++ ; ++ ++ /* ++ * Clear MMDC_CH1 mask bit. ++ */ ++ reg = readl_relaxed(ccm_base + 0x4); ++ reg &= ~(1 << 16); ++ writel_relaxed(reg, ccm_base + 0x4); ++ ++} ++ + static void __init imx6q_clocks_init(struct device_node *ccm_node) + { + struct device_node *np; + void __iomem *base; + int i, irq; + int ret; ++ u32 reg; + + clk[dummy] = imx_clk_fixed("dummy", 0); + clk[ckil] = imx_obtain_fixed_clock("ckil", 0); + clk[ckih] = imx_obtain_fixed_clock("ckih1", 0); + clk[osc] = imx_obtain_fixed_clock("osc", 0); ++ /* Clock source from external clock via ANACLK1/2 PADs */ ++ clk[anaclk1] = imx_obtain_fixed_clock("anaclk1", 0); ++ clk[anaclk2] = imx_obtain_fixed_clock("anaclk2", 0); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); +- base = of_iomap(np, 0); ++ anatop_base = base = of_iomap(np, 0); + WARN_ON(!base); + + /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ +@@ -165,13 +287,18 @@ + } + + /* type name parent_name base div_mask */ +- clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f); +- clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1); +- clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3); +- clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f); +- clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f); +- clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3); +- clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x3); ++ clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f, false); ++ clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1, false); ++ clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3, false); ++ clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "pll4_sel", base + 0x70, 0x7f, false); ++ clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f, false); ++ clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3, false); ++ clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3, false); ++ ++ /* name reg shift width parent_names num_parents */ ++ clk[lvds1_sel] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); ++ clk[lvds2_sel] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); ++ clk[pll4_sel] = imx_clk_mux("pll4_sel", base + 0x70, 14, 2, pll_av_sels, ARRAY_SIZE(pll_av_sels)); + + /* + * Bit 20 is the reserved and read-only bit, we do this only for: +@@ -191,6 +318,11 @@ + + clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); + clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); ++ /* NOTICE: The gate of the lvds1/2 in/out is used to select the clk direction */ ++ clk[lvds1_in] = imx_clk_gate("lvds1_in", "anaclk1", base + 0x160, 12); ++ clk[lvds2_in] = imx_clk_gate("lvds2_in", "anaclk2", base + 0x160, 13); ++ clk[lvds1_out] = imx_clk_gate("lvds1_out", "lvds1_sel", base + 0x160, 10); ++ clk[lvds2_out] = imx_clk_gate("lvds2_out", "lvds2_sel", base + 0x160, 11); + + clk[sata_ref_100m] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); + clk[pcie_ref_125m] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); +@@ -199,18 +331,6 @@ + base + 0xe0, 0, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + +- clk[lvds1_sel] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); +- clk[lvds2_sel] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); +- +- /* +- * lvds1_gate and lvds2_gate are pseudo-gates. Both can be +- * independently configured as clock inputs or outputs. We treat +- * the "output_enable" bit as a gate, even though it's really just +- * enabling clock output. +- */ +- clk[lvds1_gate] = imx_clk_gate("lvds1_gate", "dummy", base + 0x160, 10); +- clk[lvds2_gate] = imx_clk_gate("lvds2_gate", "dummy", base + 0x160, 11); +- + /* name parent_name reg idx */ + clk[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); + clk[pll2_pfd1_594m] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); +@@ -226,6 +346,8 @@ + clk[pll3_80m] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + clk[pll3_60m] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + clk[twd] = imx_clk_fixed_factor("twd", "arm", 1, 2); ++ clk[gpt_3m] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); ++ clk[video_27m] = imx_clk_fixed_factor("video_27m", "pll3_pfd1_540m", 1, 20); + + clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clk[pll4_audio_div] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); +@@ -233,7 +355,7 @@ + clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + + np = ccm_node; +- base = of_iomap(np, 0); ++ ccm_base = base = of_iomap(np, 0); + WARN_ON(!base); + + imx6q_pm_set_ccm_base(base); +@@ -258,14 +380,16 @@ + clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); + clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); +- clk[ipu1_di0_pre_sel] = imx_clk_mux("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); +- clk[ipu1_di1_pre_sel] = imx_clk_mux("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); +- clk[ipu2_di0_pre_sel] = imx_clk_mux("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); +- clk[ipu2_di1_pre_sel] = imx_clk_mux("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); +- clk[ipu1_di0_sel] = imx_clk_mux("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels)); +- clk[ipu1_di1_sel] = imx_clk_mux("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels)); +- clk[ipu2_di0_sel] = imx_clk_mux("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels)); +- clk[ipu2_di1_sel] = imx_clk_mux("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels)); ++ clk[ldb_di0_div_sel] = imx_clk_mux_flags("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels), CLK_SET_RATE_PARENT); ++ clk[ldb_di1_div_sel] = imx_clk_mux_flags("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di0_pre_sel] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di1_pre_sel] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di0_pre_sel] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di1_pre_sel] = imx_clk_mux_flags("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di0_sel] = imx_clk_mux_flags("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di1_sel] = imx_clk_mux_flags("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di0_sel] = imx_clk_mux_flags("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di1_sel] = imx_clk_mux_flags("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels), CLK_SET_RATE_PARENT); + clk[hsi_tx_sel] = imx_clk_mux("hsi_tx_sel", base + 0x30, 28, 1, hsi_tx_sels, ARRAY_SIZE(hsi_tx_sels)); + clk[pcie_axi_sel] = imx_clk_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); + clk[ssi1_sel] = imx_clk_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); +@@ -307,9 +431,9 @@ + clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); + clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); + clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); +- clk[ldb_di0_podf] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0); ++ clk[ldb_di0_div_7] = imx_clk_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); + clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); +- clk[ldb_di1_podf] = imx_clk_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0); ++ clk[ldb_di1_div_7] = imx_clk_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7); + clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3); + clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3); + clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3); +@@ -344,6 +468,9 @@ + /* name parent_name reg shift */ + clk[apbh_dma] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4); + clk[asrc] = imx_clk_gate2("asrc", "asrc_podf", base + 0x68, 6); ++ clk[caam_mem] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8); ++ clk[caam_aclk] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10); ++ clk[caam_ipg] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12); + clk[can1_ipg] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); + clk[can1_serial] = imx_clk_gate2("can1_serial", "can_root", base + 0x68, 16); + clk[can2_ipg] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); +@@ -354,6 +481,8 @@ + clk[ecspi4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); + clk[ecspi5] = imx_clk_gate2("ecspi5", "ecspi_root", base + 0x6c, 8); + clk[enet] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10); ++ clk[epit1] = imx_clk_gate2("epit1", "ipg", base + 0x6c, 12); ++ clk[epit2] = imx_clk_gate2("epit2", "ipg", base + 0x6c, 14); + clk[esai] = imx_clk_gate2("esai", "esai_podf", base + 0x6c, 16); + clk[gpt_ipg] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20); + clk[gpt_ipg_per] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22); +@@ -373,15 +502,16 @@ + clk[i2c3] = imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10); + clk[iim] = imx_clk_gate2("iim", "ipg", base + 0x70, 12); + clk[enfc] = imx_clk_gate2("enfc", "enfc_podf", base + 0x70, 14); ++ clk[tzasc2] = imx_clk_gate2("tzasc2", "mmdc_ch0_axi_podf", base + 0x70, 24); + clk[vdoa] = imx_clk_gate2("vdoa", "vdo_axi", base + 0x70, 26); + clk[ipu1] = imx_clk_gate2("ipu1", "ipu1_podf", base + 0x74, 0); + clk[ipu1_di0] = imx_clk_gate2("ipu1_di0", "ipu1_di0_sel", base + 0x74, 2); + clk[ipu1_di1] = imx_clk_gate2("ipu1_di1", "ipu1_di1_sel", base + 0x74, 4); + clk[ipu2] = imx_clk_gate2("ipu2", "ipu2_podf", base + 0x74, 6); + clk[ipu2_di0] = imx_clk_gate2("ipu2_di0", "ipu2_di0_sel", base + 0x74, 8); +- clk[ldb_di0] = imx_clk_gate2("ldb_di0", "ldb_di0_podf", base + 0x74, 12); +- clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); + clk[ipu2_di1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); ++ clk[ldb_di0] = imx_clk_gate2("ldb_di0", "ldb_di0_div_sel", base + 0x74, 12); ++ clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_div_sel", base + 0x74, 14); + clk[hsi_tx] = imx_clk_gate2("hsi_tx", "hsi_tx_podf", base + 0x74, 16); + if (cpu_is_imx6dl()) + /* +@@ -413,6 +543,9 @@ + clk[ssi1_ipg] = imx_clk_gate2("ssi1_ipg", "ipg", base + 0x7c, 18); + clk[ssi2_ipg] = imx_clk_gate2("ssi2_ipg", "ipg", base + 0x7c, 20); + clk[ssi3_ipg] = imx_clk_gate2("ssi3_ipg", "ipg", base + 0x7c, 22); ++ clk[ssi1] = imx_clk_gate2("ssi1", "ssi1_podf", base + 0x7c, 18); ++ clk[ssi2] = imx_clk_gate2("ssi2", "ssi2_podf", base + 0x7c, 20); ++ clk[ssi3] = imx_clk_gate2("ssi3", "ssi3_podf", base + 0x7c, 22); + clk[uart_ipg] = imx_clk_gate2("uart_ipg", "ipg", base + 0x7c, 24); + clk[uart_serial] = imx_clk_gate2("uart_serial", "uart_serial_podf", base + 0x7c, 26); + clk[usboh3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); +@@ -431,25 +564,79 @@ + pr_err("i.MX6q clk %d: register failed with %ld\n", + i, PTR_ERR(clk[i])); + ++ /* Initialize clock gate status */ ++ writel_relaxed(1 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(1) | ++ 3 << CCM_CCGR_OFFSET(0), base + 0x68); ++ if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) ++ writel_relaxed(3 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10), base + 0x6c); ++ else ++ writel_relaxed(3 << CCM_CCGR_OFFSET(10), base + 0x6c); ++ writel_relaxed(1 << CCM_CCGR_OFFSET(12) | ++ 3 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10) | ++ 3 << CCM_CCGR_OFFSET(9) | ++ 3 << CCM_CCGR_OFFSET(8), base + 0x70); ++ writel_relaxed(3 << CCM_CCGR_OFFSET(14) | ++ 1 << CCM_CCGR_OFFSET(13) | ++ 3 << CCM_CCGR_OFFSET(12) | ++ 1 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10), base + 0x74); ++ writel_relaxed(3 << CCM_CCGR_OFFSET(7) | ++ 3 << CCM_CCGR_OFFSET(6) | ++ 3 << CCM_CCGR_OFFSET(4), base + 0x78); ++ writel_relaxed(1 << CCM_CCGR_OFFSET(0), base + 0x7c); ++ writel_relaxed(0, base + 0x80); ++ ++ /* Make sure PFDs are disabled at boot. */ ++ reg = readl_relaxed(anatop_base + 0x100); ++ /* Cannot disable pll2_pfd2_396M, as it is the MMDC clock in iMX6DL */ ++ if (cpu_is_imx6dl()) ++ reg |= 0x80008080; ++ else ++ reg |= 0x80808080; ++ writel_relaxed(reg, anatop_base + 0x100); ++ ++ /* Disable PLL3 PFDs. */ ++ reg = readl_relaxed(anatop_base + 0xF0); ++ reg |= 0x80808080; ++ writel_relaxed(reg, anatop_base + 0xF0); ++ ++ /* Make sure PLLs is disabled */ ++ reg = readl_relaxed(anatop_base + 0xA0); ++ reg &= ~(1 << 13); ++ writel_relaxed(reg, anatop_base + 0xA0); ++ + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + clk_register_clkdev(clk[gpt_ipg], "ipg", "imx-gpt.0"); + clk_register_clkdev(clk[gpt_ipg_per], "per", "imx-gpt.0"); ++ clk_register_clkdev(clk[gpt_3m], "gpt_3m", "imx-gpt.0"); + clk_register_clkdev(clk[cko1_sel], "cko1_sel", NULL); + clk_register_clkdev(clk[ahb], "ahb", NULL); + clk_register_clkdev(clk[cko1], "cko1", NULL); + clk_register_clkdev(clk[arm], NULL, "cpu0"); +- clk_register_clkdev(clk[pll4_post_div], "pll4_post_div", NULL); +- clk_register_clkdev(clk[pll4_audio], "pll4_audio", NULL); ++ clk_register_clkdev(clk[pll4_audio_div], "pll4_audio_div", NULL); ++ clk_register_clkdev(clk[pll4_sel], "pll4_sel", NULL); ++ clk_register_clkdev(clk[lvds2_in], "lvds2_in", NULL); ++ clk_register_clkdev(clk[esai], "esai", NULL); + +- if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) || +- cpu_is_imx6dl()) { +- clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]); +- clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]); ++ if (cpu_is_imx6dl()) { ++ clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]); + } + ++ clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di0_sel], clk[ipu1_di0_pre]); ++ clk_set_parent(clk[ipu1_di1_sel], clk[ipu1_di1_pre]); ++ clk_set_parent(clk[ipu2_di0_sel], clk[ipu2_di0_pre]); ++ clk_set_parent(clk[ipu2_di1_sel], clk[ipu2_di1_pre]); ++ + /* + * The gpmi needs 100MHz frequency in the EDO/Sync mode, + * We can not get the 100MHz from the pll2_pfd0_352m. +@@ -457,6 +644,19 @@ + */ + clk_set_parent(clk[enfc_sel], clk[pll2_pfd2_396m]); + ++ /* Set the parent clks of PCIe lvds1 and pcie_axi to be sata ref, axi */ ++ if (clk_set_parent(clk[lvds1_sel], clk[sata_ref])) ++ pr_err("Failed to set PCIe bus parent clk.\n"); ++ if (clk_set_parent(clk[pcie_axi_sel], clk[axi])) ++ pr_err("Failed to set PCIe parent clk.\n"); ++ ++ /* gpu clock initilazation */ ++ clk_set_parent(clk[gpu3d_shader_sel], clk[pll2_pfd1_594m]); ++ clk_set_rate(clk[gpu3d_shader], 594000000); ++ clk_set_parent(clk[gpu3d_core_sel], clk[mmdc_ch0_axi]); ++ clk_set_rate(clk[gpu3d_core], 528000000); ++ clk_set_parent(clk[gpu2d_core_sel], clk[pll3_usb_otg]); ++ + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_prepare_enable(clk[clks_init_on[i]]); + +@@ -465,6 +665,25 @@ + clk_prepare_enable(clk[usbphy2_gate]); + } + ++ /* ipu clock initialization */ ++ init_ldb_clks(pll2_pfd0_352m); ++ clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di0_sel], clk[ipu1_di0_pre]); ++ clk_set_parent(clk[ipu1_di1_sel], clk[ipu1_di1_pre]); ++ clk_set_parent(clk[ipu2_di0_sel], clk[ipu2_di0_pre]); ++ clk_set_parent(clk[ipu2_di1_sel], clk[ipu2_di1_pre]); ++ if (cpu_is_imx6dl()) { ++ clk_set_rate(clk[pll3_pfd1_540m], 540000000); ++ clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]); ++ clk_set_parent(clk[axi_sel], clk[pll3_pfd1_540m]); ++ } else if (cpu_is_imx6q()) { ++ clk_set_parent(clk[ipu1_sel], clk[mmdc_ch0_axi]); ++ clk_set_parent(clk[ipu2_sel], clk[mmdc_ch0_axi]); ++ } ++ + /* + * Let's initially set up CLKO with OSC24M, since this configuration + * is widely used by imx6q board designs to clock audio codec. +@@ -482,6 +701,34 @@ + if (IS_ENABLED(CONFIG_PCI_IMX6)) + clk_set_parent(clk[lvds1_sel], clk[sata_ref]); + ++ /* Audio clocks */ ++ clk_set_parent(clk[ssi1_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[ssi2_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[ssi3_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[esai_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[spdif_sel], clk[pll3_pfd3_454m]); ++ clk_set_parent(clk[asrc_sel], clk[pll3_usb_otg]); ++ clk_set_rate(clk[asrc_sel], 7500000); ++ ++ /* Set pll4_audio to a value that can derive 5K-88.2KHz and 8K-96KHz */ ++ clk_set_rate(clk[pll4_audio_div], 541900800); ++ ++#ifdef CONFIG_MX6_VPU_352M ++ /* ++ * If VPU 352M is enabled, then PLL2_PDF2 need to be ++ * set to 352M, cpufreq will be disabled as VDDSOC/PU ++ * need to be at highest voltage, scaling cpu freq is ++ * not saving any power, and busfreq will be also disabled ++ * as the PLL2_PFD2 is not at default freq, in a word, ++ * all modules that sourceing clk from PLL2_PFD2 will ++ * be impacted. ++ */ ++ clk_set_rate(clk[pll2_pfd2_396m], 352000000); ++ clk_set_parent(clk[vpu_axi_sel], clk[pll2_pfd2_396m]); ++ pr_info("VPU 352M is enabled!\n"); ++#endif ++ ++ + /* Set initial power mode */ + imx6q_set_lpm(WAIT_CLOCKED); + +diff -Nur linux-3.14.36/arch/arm/mach-imx/clk-imx6q.c.orig linux-openelec/arch/arm/mach-imx/clk-imx6q.c.orig +--- linux-3.14.36/arch/arm/mach-imx/clk-imx6q.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/clk-imx6q.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,725 @@ ++/* ++ * Copyright 2011-2014 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk.h" ++#include "common.h" ++#include "hardware.h" ++ ++#define CCM_CCGR_OFFSET(index) (index * 2) ++ ++static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; ++static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; ++static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; ++static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; ++static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; ++static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; ++static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; ++static const char *axi_sels[] = { "periph", "pll2_pfd2_396m", "periph", "pll3_pfd1_540m", }; ++static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", }; ++static const char *gpu_axi_sels[] = { "axi", "ahb", }; ++static const char *gpu2d_core_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", }; ++static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", }; ++static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll3_pfd0_720m", }; ++static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; ++static const char *ldb_di_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_usb_otg", }; ++static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; ++static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; ++static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", }; ++static const char *ipu1_di0_sels[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; ++static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; ++static const char *ipu2_di0_sels[] = { "ipu2_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; ++static const char *ipu2_di1_sels[] = { "ipu2_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; ++static const char *hsi_tx_sels[] = { "pll3_120m", "pll2_pfd2_396m", }; ++static const char *pcie_axi_sels[] = { "axi", "ahb", }; ++static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio_div", }; ++static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; ++static const char *enfc_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", }; ++static const char *emi_sels[] = { "pll2_pfd2_396m", "pll3_usb_otg", "axi", "pll2_pfd0_352m", }; ++static const char *emi_slow_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", }; ++static const char *vdo_axi_sels[] = { "axi", "ahb", }; ++static const char *vpu_axi_sels[] = { "axi", "pll2_pfd2_396m", "pll2_pfd0_352m", }; ++static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div", ++ "dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0", ++ "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio_div", }; ++static const char *cko2_sels[] = { ++ "mmdc_ch0_axi", "mmdc_ch1_axi", "usdhc4", "usdhc1", ++ "gpu2d_axi", "dummy", "ecspi_root", "gpu3d_axi", ++ "usdhc3", "dummy", "arm", "ipu1", ++ "ipu2", "vdo_axi", "osc", "gpu2d_core", ++ "gpu3d_core", "usdhc2", "ssi1", "ssi2", ++ "ssi3", "gpu3d_shader", "vpu_axi", "can_root", ++ "ldb_di0", "ldb_di1", "esai", "eim_slow", ++ "uart_serial", "spdif", "asrc", "hsi_tx", ++}; ++static const char *cko_sels[] = { "cko1", "cko2", }; ++static const char *lvds_sels[] = { ++ "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", ++ "pll4_audio", "pll5_video", "pll8_mlb", "enet_ref", ++ "pcie_ref", "sata_ref", ++}; ++static const char *pll_av_sels[] = { "osc", "lvds1_in", "lvds2_in", "dummy", }; ++static void __iomem *anatop_base; ++static void __iomem *ccm_base; ++ ++ ++enum mx6q_clks { ++ dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m, ++ pll3_pfd0_720m, pll3_pfd1_540m, pll3_pfd2_508m, pll3_pfd3_454m, ++ pll2_198m, pll3_120m, pll3_80m, pll3_60m, twd, step, pll1_sw, ++ periph_pre, periph2_pre, periph_clk2_sel, periph2_clk2_sel, axi_sel, ++ esai_sel, asrc_sel, spdif_sel, gpu2d_axi, gpu3d_axi, gpu2d_core_sel, ++ gpu3d_core_sel, gpu3d_shader_sel, ipu1_sel, ipu2_sel, ldb_di0_sel, ++ ldb_di1_sel, ipu1_di0_pre_sel, ipu1_di1_pre_sel, ipu2_di0_pre_sel, ++ ipu2_di1_pre_sel, ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, ++ ipu2_di1_sel, hsi_tx_sel, pcie_axi_sel, ssi1_sel, ssi2_sel, ssi3_sel, ++ usdhc1_sel, usdhc2_sel, usdhc3_sel, usdhc4_sel, enfc_sel, emi_sel, ++ emi_slow_sel, vdo_axi_sel, vpu_axi_sel, cko1_sel, periph, periph2, ++ periph_clk2, periph2_clk2, ipg, ipg_per, esai_pred, esai_podf, ++ asrc_pred, asrc_podf, spdif_pred, spdif_podf, can_root, ecspi_root, ++ gpu2d_core_podf, gpu3d_core_podf, gpu3d_shader, ipu1_podf, ipu2_podf, ++ ldb_di0_podf_unused, ldb_di1_podf_unused, ipu1_di0_pre, ipu1_di1_pre, ++ ipu2_di0_pre, ipu2_di1_pre, hsi_tx_podf, ssi1_pred, ssi1_podf, ++ ssi2_pred, ssi2_podf, ssi3_pred, ssi3_podf, uart_serial_podf, ++ usdhc1_podf, usdhc2_podf, usdhc3_podf, usdhc4_podf, enfc_pred, enfc_podf, ++ emi_podf, emi_slow_podf, vpu_axi_podf, cko1_podf, axi, mmdc_ch0_axi_podf, ++ mmdc_ch1_axi_podf, arm, ahb, apbh_dma, asrc, can1_ipg, can1_serial, ++ can2_ipg, can2_serial, ecspi1, ecspi2, ecspi3, ecspi4, ecspi5, enet, ++ esai, gpt_ipg, gpt_ipg_per, gpu2d_core, gpu3d_core, hdmi_iahb, ++ hdmi_isfr, i2c1, i2c2, i2c3, iim, enfc, ipu1, ipu1_di0, ipu1_di1, ipu2, ++ ipu2_di0, ldb_di0, ldb_di1, ipu2_di1, hsi_tx, mlb, mmdc_ch0_axi, ++ mmdc_ch1_axi, ocram, openvg_axi, pcie_axi, pwm1, pwm2, pwm3, pwm4, per1_bch, ++ gpmi_bch_apb, gpmi_bch, gpmi_io, gpmi_apb, sata, sdma, spba, ssi1, ++ ssi2, ssi3, uart_ipg, uart_serial, usboh3, usdhc1, usdhc2, usdhc3, ++ usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg, ++ pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, ++ ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, ++ sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate, ++ usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, eim_slow, ++ spdif, cko2_sel, cko2_podf, cko2, cko, vdoa, pll4_audio_div, ++ lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, gpt_3m, video_27m, ++ ldb_di0_div_7, ldb_di1_div_7, ldb_di0_div_sel, ldb_di1_div_sel, ++ caam_mem, caam_aclk, caam_ipg, epit1, epit2, tzasc2, lvds1_in, lvds1_out, ++ pll4_sel, lvds2_in, lvds2_out, anaclk1, anaclk2, clk_max ++}; ++ ++static struct clk *clk[clk_max]; ++static struct clk_onecell_data clk_data; ++ ++static enum mx6q_clks const clks_init_on[] __initconst = { ++ mmdc_ch0_axi, rom, arm, ++}; ++ ++static struct clk_div_table clk_enet_ref_table[] = { ++ { .val = 0, .div = 20, }, ++ { .val = 1, .div = 10, }, ++ { .val = 2, .div = 5, }, ++ { .val = 3, .div = 4, }, ++ { /* sentinel */ } ++}; ++ ++static struct clk_div_table post_div_table[] = { ++ { .val = 2, .div = 1, }, ++ { .val = 1, .div = 2, }, ++ { .val = 0, .div = 4, }, ++ { /* sentinel */ } ++}; ++ ++static struct clk_div_table video_div_table[] = { ++ { .val = 0, .div = 1, }, ++ { .val = 1, .div = 2, }, ++ { .val = 2, .div = 1, }, ++ { .val = 3, .div = 4, }, ++ { /* sentinel */ } ++}; ++ ++static void init_ldb_clks(enum mx6q_clks new_parent) ++{ ++ u32 reg; ++ ++ /* ++ * Need to follow a strict procedure when changing the LDB ++ * clock, else we can introduce a glitch. Things to keep in ++ * mind: ++ * 1. The current and new parent clocks must be disabled. ++ * 2. The default clock for ldb_dio_clk is mmdc_ch1 which has ++ * no CG bit. ++ * 3. In the RTL implementation of the LDB_DI_CLK_SEL mux ++ * the top four options are in one mux and the PLL3 option along ++ * with another option is in the second mux. There is third mux ++ * used to decide between the first and second mux. ++ * The code below switches the parent to the bottom mux first ++ * and then manipulates the top mux. This ensures that no glitch ++ * will enter the divider. ++ * ++ * Need to disable MMDC_CH1 clock manually as there is no CG bit ++ * for this clock. The only way to disable this clock is to move ++ * it topll3_sw_clk and then to disable pll3_sw_clk ++ * Make sure periph2_clk2_sel is set to pll3_sw_clk ++ */ ++ reg = readl_relaxed(ccm_base + 0x18); ++ reg &= ~(1 << 20); ++ writel_relaxed(reg, ccm_base + 0x18); ++ ++ /* ++ * Set MMDC_CH1 mask bit. ++ */ ++ reg = readl_relaxed(ccm_base + 0x4); ++ reg |= 1 << 16; ++ writel_relaxed(reg, ccm_base + 0x4); ++ ++ /* ++ * Set the periph2_clk_sel to the top mux so that ++ * mmdc_ch1 is from pll3_sw_clk. ++ */ ++ reg = readl_relaxed(ccm_base + 0x14); ++ reg |= 1 << 26; ++ writel_relaxed(reg, ccm_base + 0x14); ++ ++ /* ++ * Wait for the clock switch. ++ */ ++ while (readl_relaxed(ccm_base + 0x48)) ++ ; ++ ++ /* ++ * Disable pll3_sw_clk by selecting the bypass clock source. ++ */ ++ reg = readl_relaxed(ccm_base + 0xc); ++ reg |= 1 << 0; ++ writel_relaxed(reg, ccm_base + 0xc); ++ ++ /* ++ * Set the ldb_di0_clk and ldb_di1_clk to 111b. ++ */ ++ reg = readl_relaxed(ccm_base + 0x2c); ++ reg |= ((7 << 9) | (7 << 12)); ++ writel_relaxed(reg, ccm_base + 0x2c); ++ ++ /* ++ * Set the ldb_di0_clk and ldb_di1_clk to 100b. ++ */ ++ reg = readl_relaxed(ccm_base + 0x2c); ++ reg &= ~((7 << 9) | (7 << 12)); ++ reg |= ((4 << 9) | (4 << 12)); ++ writel_relaxed(reg, ccm_base + 0x2c); ++ ++ /* ++ * Perform the LDB parent clock switch. ++ */ ++ clk_set_parent(clk[ldb_di0_sel], clk[new_parent]); ++ clk_set_parent(clk[ldb_di1_sel], clk[new_parent]); ++ ++ /* ++ * Unbypass pll3_sw_clk. ++ */ ++ reg = readl_relaxed(ccm_base + 0xc); ++ reg &= ~(1 << 0); ++ writel_relaxed(reg, ccm_base + 0xc); ++ ++ /* ++ * Set the periph2_clk_sel back to the bottom mux so that ++ * mmdc_ch1 is from its original parent. ++ */ ++ reg = readl_relaxed(ccm_base + 0x14); ++ reg &= ~(1 << 26); ++ writel_relaxed(reg, ccm_base + 0x14); ++ ++ /* ++ * Wait for the clock switch. ++ */ ++ while (readl_relaxed(ccm_base + 0x48)) ++ ; ++ ++ /* ++ * Clear MMDC_CH1 mask bit. ++ */ ++ reg = readl_relaxed(ccm_base + 0x4); ++ reg &= ~(1 << 16); ++ writel_relaxed(reg, ccm_base + 0x4); ++ ++} ++ ++static void __init imx6q_clocks_init(struct device_node *ccm_node) ++{ ++ struct device_node *np; ++ void __iomem *base; ++ int i, irq; ++ int ret; ++ u32 reg; ++ ++ clk[dummy] = imx_clk_fixed("dummy", 0); ++ clk[ckil] = imx_obtain_fixed_clock("ckil", 0); ++ clk[ckih] = imx_obtain_fixed_clock("ckih1", 0); ++ clk[osc] = imx_obtain_fixed_clock("osc", 0); ++ /* Clock source from external clock via ANACLK1/2 PADs */ ++ clk[anaclk1] = imx_obtain_fixed_clock("anaclk1", 0); ++ clk[anaclk2] = imx_obtain_fixed_clock("anaclk2", 0); ++ ++ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); ++ anatop_base = base = of_iomap(np, 0); ++ WARN_ON(!base); ++ ++ /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ ++ if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) { ++ post_div_table[1].div = 1; ++ post_div_table[2].div = 1; ++ video_div_table[1].div = 1; ++ video_div_table[2].div = 1; ++ }; ++ ++ /* type name parent_name base div_mask */ ++ clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f, false); ++ clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1, false); ++ clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3, false); ++ clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "pll4_sel", base + 0x70, 0x7f, false); ++ clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f, false); ++ clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3, false); ++ clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3, false); ++ ++ /* name reg shift width parent_names num_parents */ ++ clk[lvds1_sel] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); ++ clk[lvds2_sel] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); ++ clk[pll4_sel] = imx_clk_mux("pll4_sel", base + 0x70, 14, 2, pll_av_sels, ARRAY_SIZE(pll_av_sels)); ++ ++ /* ++ * Bit 20 is the reserved and read-only bit, we do this only for: ++ * - Do nothing for usbphy clk_enable/disable ++ * - Keep refcount when do usbphy clk_enable/disable, in that case, ++ * the clk framework may need to enable/disable usbphy's parent ++ */ ++ clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); ++ clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); ++ ++ /* ++ * usbphy*_gate needs to be on after system boots up, and software ++ * never needs to control it anymore. ++ */ ++ clk[usbphy1_gate] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); ++ clk[usbphy2_gate] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); ++ ++ clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); ++ clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); ++ /* NOTICE: The gate of the lvds1/2 in/out is used to select the clk direction */ ++ clk[lvds1_in] = imx_clk_gate("lvds1_in", "anaclk1", base + 0x160, 12); ++ clk[lvds2_in] = imx_clk_gate("lvds2_in", "anaclk2", base + 0x160, 13); ++ clk[lvds1_out] = imx_clk_gate("lvds1_out", "lvds1_sel", base + 0x160, 10); ++ clk[lvds2_out] = imx_clk_gate("lvds2_out", "lvds2_sel", base + 0x160, 11); ++ ++ clk[sata_ref_100m] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); ++ clk[pcie_ref_125m] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); ++ ++ clk[enet_ref] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, ++ base + 0xe0, 0, 2, 0, clk_enet_ref_table, ++ &imx_ccm_lock); ++ ++ /* name parent_name reg idx */ ++ clk[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); ++ clk[pll2_pfd1_594m] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); ++ clk[pll2_pfd2_396m] = imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); ++ clk[pll3_pfd0_720m] = imx_clk_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); ++ clk[pll3_pfd1_540m] = imx_clk_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); ++ clk[pll3_pfd2_508m] = imx_clk_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); ++ clk[pll3_pfd3_454m] = imx_clk_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); ++ ++ /* name parent_name mult div */ ++ clk[pll2_198m] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); ++ clk[pll3_120m] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); ++ clk[pll3_80m] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); ++ clk[pll3_60m] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); ++ clk[twd] = imx_clk_fixed_factor("twd", "arm", 1, 2); ++ clk[gpt_3m] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); ++ clk[video_27m] = imx_clk_fixed_factor("video_27m", "pll3_pfd1_540m", 1, 20); ++ ++ clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); ++ clk[pll4_audio_div] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); ++ clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); ++ clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); ++ ++ np = ccm_node; ++ ccm_base = base = of_iomap(np, 0); ++ WARN_ON(!base); ++ ++ imx6q_pm_set_ccm_base(base); ++ ++ /* name reg shift width parent_names num_parents */ ++ clk[step] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); ++ clk[pll1_sw] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); ++ clk[periph_pre] = imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); ++ clk[periph2_pre] = imx_clk_mux("periph2_pre", base + 0x18, 21, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); ++ clk[periph_clk2_sel] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); ++ clk[periph2_clk2_sel] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); ++ clk[axi_sel] = imx_clk_mux("axi_sel", base + 0x14, 6, 2, axi_sels, ARRAY_SIZE(axi_sels)); ++ clk[esai_sel] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); ++ clk[asrc_sel] = imx_clk_mux("asrc_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); ++ clk[spdif_sel] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); ++ clk[gpu2d_axi] = imx_clk_mux("gpu2d_axi", base + 0x18, 0, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); ++ clk[gpu3d_axi] = imx_clk_mux("gpu3d_axi", base + 0x18, 1, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); ++ clk[gpu2d_core_sel] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels)); ++ clk[gpu3d_core_sel] = imx_clk_mux("gpu3d_core_sel", base + 0x18, 4, 2, gpu3d_core_sels, ARRAY_SIZE(gpu3d_core_sels)); ++ clk[gpu3d_shader_sel] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); ++ clk[ipu1_sel] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); ++ clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); ++ clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); ++ clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); ++ clk[ldb_di0_div_sel] = imx_clk_mux_flags("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels), CLK_SET_RATE_PARENT); ++ clk[ldb_di1_div_sel] = imx_clk_mux_flags("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di0_pre_sel] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di1_pre_sel] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di0_pre_sel] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di1_pre_sel] = imx_clk_mux_flags("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di0_sel] = imx_clk_mux_flags("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels), CLK_SET_RATE_PARENT); ++ clk[ipu1_di1_sel] = imx_clk_mux_flags("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di0_sel] = imx_clk_mux_flags("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels), CLK_SET_RATE_PARENT); ++ clk[ipu2_di1_sel] = imx_clk_mux_flags("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels), CLK_SET_RATE_PARENT); ++ clk[hsi_tx_sel] = imx_clk_mux("hsi_tx_sel", base + 0x30, 28, 1, hsi_tx_sels, ARRAY_SIZE(hsi_tx_sels)); ++ clk[pcie_axi_sel] = imx_clk_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); ++ clk[ssi1_sel] = imx_clk_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); ++ clk[ssi2_sel] = imx_clk_fixup_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); ++ clk[ssi3_sel] = imx_clk_fixup_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); ++ clk[usdhc1_sel] = imx_clk_fixup_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); ++ clk[usdhc2_sel] = imx_clk_fixup_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); ++ clk[usdhc3_sel] = imx_clk_fixup_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); ++ clk[usdhc4_sel] = imx_clk_fixup_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); ++ clk[enfc_sel] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels)); ++ clk[emi_sel] = imx_clk_fixup_mux("emi_sel", base + 0x1c, 27, 2, emi_sels, ARRAY_SIZE(emi_sels), imx_cscmr1_fixup); ++ clk[emi_slow_sel] = imx_clk_fixup_mux("emi_slow_sel", base + 0x1c, 29, 2, emi_slow_sels, ARRAY_SIZE(emi_slow_sels), imx_cscmr1_fixup); ++ clk[vdo_axi_sel] = imx_clk_mux("vdo_axi_sel", base + 0x18, 11, 1, vdo_axi_sels, ARRAY_SIZE(vdo_axi_sels)); ++ clk[vpu_axi_sel] = imx_clk_mux("vpu_axi_sel", base + 0x18, 14, 2, vpu_axi_sels, ARRAY_SIZE(vpu_axi_sels)); ++ clk[cko1_sel] = imx_clk_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); ++ clk[cko2_sel] = imx_clk_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); ++ clk[cko] = imx_clk_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); ++ ++ /* name reg shift width busy: reg, shift parent_names num_parents */ ++ clk[periph] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); ++ clk[periph2] = imx_clk_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); ++ ++ /* name parent_name reg shift width */ ++ clk[periph_clk2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); ++ clk[periph2_clk2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); ++ clk[ipg] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); ++ clk[ipg_per] = imx_clk_fixup_divider("ipg_per", "ipg", base + 0x1c, 0, 6, imx_cscmr1_fixup); ++ clk[esai_pred] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); ++ clk[esai_podf] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); ++ clk[asrc_pred] = imx_clk_divider("asrc_pred", "asrc_sel", base + 0x30, 12, 3); ++ clk[asrc_podf] = imx_clk_divider("asrc_podf", "asrc_pred", base + 0x30, 9, 3); ++ clk[spdif_pred] = imx_clk_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); ++ clk[spdif_podf] = imx_clk_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); ++ clk[can_root] = imx_clk_divider("can_root", "pll3_60m", base + 0x20, 2, 6); ++ clk[ecspi_root] = imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6); ++ clk[gpu2d_core_podf] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3); ++ clk[gpu3d_core_podf] = imx_clk_divider("gpu3d_core_podf", "gpu3d_core_sel", base + 0x18, 26, 3); ++ clk[gpu3d_shader] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3); ++ clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); ++ clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); ++ clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); ++ clk[ldb_di0_div_7] = imx_clk_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); ++ clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); ++ clk[ldb_di1_div_7] = imx_clk_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7); ++ clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3); ++ clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3); ++ clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3); ++ clk[ipu2_di1_pre] = imx_clk_divider("ipu2_di1_pre", "ipu2_di1_pre_sel", base + 0x38, 12, 3); ++ clk[hsi_tx_podf] = imx_clk_divider("hsi_tx_podf", "hsi_tx_sel", base + 0x30, 29, 3); ++ clk[ssi1_pred] = imx_clk_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); ++ clk[ssi1_podf] = imx_clk_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); ++ clk[ssi2_pred] = imx_clk_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); ++ clk[ssi2_podf] = imx_clk_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); ++ clk[ssi3_pred] = imx_clk_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); ++ clk[ssi3_podf] = imx_clk_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); ++ clk[uart_serial_podf] = imx_clk_divider("uart_serial_podf", "pll3_80m", base + 0x24, 0, 6); ++ clk[usdhc1_podf] = imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); ++ clk[usdhc2_podf] = imx_clk_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); ++ clk[usdhc3_podf] = imx_clk_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); ++ clk[usdhc4_podf] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); ++ clk[enfc_pred] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); ++ clk[enfc_podf] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); ++ clk[emi_podf] = imx_clk_fixup_divider("emi_podf", "emi_sel", base + 0x1c, 20, 3, imx_cscmr1_fixup); ++ clk[emi_slow_podf] = imx_clk_fixup_divider("emi_slow_podf", "emi_slow_sel", base + 0x1c, 23, 3, imx_cscmr1_fixup); ++ clk[vpu_axi_podf] = imx_clk_divider("vpu_axi_podf", "vpu_axi_sel", base + 0x24, 25, 3); ++ clk[cko1_podf] = imx_clk_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); ++ clk[cko2_podf] = imx_clk_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); ++ ++ /* name parent_name reg shift width busy: reg, shift */ ++ clk[axi] = imx_clk_busy_divider("axi", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0); ++ clk[mmdc_ch0_axi_podf] = imx_clk_busy_divider("mmdc_ch0_axi_podf", "periph", base + 0x14, 19, 3, base + 0x48, 4); ++ clk[mmdc_ch1_axi_podf] = imx_clk_busy_divider("mmdc_ch1_axi_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); ++ clk[arm] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); ++ clk[ahb] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); ++ ++ /* name parent_name reg shift */ ++ clk[apbh_dma] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4); ++ clk[asrc] = imx_clk_gate2("asrc", "asrc_podf", base + 0x68, 6); ++ clk[caam_mem] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8); ++ clk[caam_aclk] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10); ++ clk[caam_ipg] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12); ++ clk[can1_ipg] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); ++ clk[can1_serial] = imx_clk_gate2("can1_serial", "can_root", base + 0x68, 16); ++ clk[can2_ipg] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); ++ clk[can2_serial] = imx_clk_gate2("can2_serial", "can_root", base + 0x68, 20); ++ clk[ecspi1] = imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); ++ clk[ecspi2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); ++ clk[ecspi3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); ++ clk[ecspi4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); ++ clk[ecspi5] = imx_clk_gate2("ecspi5", "ecspi_root", base + 0x6c, 8); ++ clk[enet] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10); ++ clk[epit1] = imx_clk_gate2("epit1", "ipg", base + 0x6c, 12); ++ clk[epit2] = imx_clk_gate2("epit2", "ipg", base + 0x6c, 14); ++ clk[esai] = imx_clk_gate2("esai", "esai_podf", base + 0x6c, 16); ++ clk[gpt_ipg] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20); ++ clk[gpt_ipg_per] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22); ++ if (cpu_is_imx6dl()) ++ /* ++ * The multiplexer and divider of imx6q clock gpu3d_shader get ++ * redefined/reused as gpu2d_core_sel and gpu2d_core_podf on imx6dl. ++ */ ++ clk[gpu2d_core] = imx_clk_gate2("gpu2d_core", "gpu3d_shader", base + 0x6c, 24); ++ else ++ clk[gpu2d_core] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); ++ clk[gpu3d_core] = imx_clk_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26); ++ clk[hdmi_iahb] = imx_clk_gate2("hdmi_iahb", "ahb", base + 0x70, 0); ++ clk[hdmi_isfr] = imx_clk_gate2("hdmi_isfr", "pll3_pfd1_540m", base + 0x70, 4); ++ clk[i2c1] = imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6); ++ clk[i2c2] = imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8); ++ clk[i2c3] = imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10); ++ clk[iim] = imx_clk_gate2("iim", "ipg", base + 0x70, 12); ++ clk[enfc] = imx_clk_gate2("enfc", "enfc_podf", base + 0x70, 14); ++ clk[tzasc2] = imx_clk_gate2("tzasc2", "mmdc_ch0_axi_podf", base + 0x70, 24); ++ clk[vdoa] = imx_clk_gate2("vdoa", "vdo_axi", base + 0x70, 26); ++ clk[ipu1] = imx_clk_gate2("ipu1", "ipu1_podf", base + 0x74, 0); ++ clk[ipu1_di0] = imx_clk_gate2("ipu1_di0", "ipu1_di0_sel", base + 0x74, 2); ++ clk[ipu1_di1] = imx_clk_gate2("ipu1_di1", "ipu1_di1_sel", base + 0x74, 4); ++ clk[ipu2] = imx_clk_gate2("ipu2", "ipu2_podf", base + 0x74, 6); ++ clk[ipu2_di0] = imx_clk_gate2("ipu2_di0", "ipu2_di0_sel", base + 0x74, 8); ++ clk[ipu2_di1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); ++ clk[ldb_di0] = imx_clk_gate2("ldb_di0", "ldb_di0_div_sel", base + 0x74, 12); ++ clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_div_sel", base + 0x74, 14); ++ clk[hsi_tx] = imx_clk_gate2("hsi_tx", "hsi_tx_podf", base + 0x74, 16); ++ if (cpu_is_imx6dl()) ++ /* ++ * The multiplexer and divider of the imx6q clock gpu2d get ++ * redefined/reused as mlb_sys_sel and mlb_sys_clk_podf on imx6dl. ++ */ ++ clk[mlb] = imx_clk_gate2("mlb", "gpu2d_core_podf", base + 0x74, 18); ++ else ++ clk[mlb] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); ++ clk[mmdc_ch0_axi] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); ++ clk[mmdc_ch1_axi] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); ++ clk[ocram] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); ++ clk[openvg_axi] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30); ++ clk[pcie_axi] = imx_clk_gate2("pcie_axi", "pcie_axi_sel", base + 0x78, 0); ++ clk[per1_bch] = imx_clk_gate2("per1_bch", "usdhc3", base + 0x78, 12); ++ clk[pwm1] = imx_clk_gate2("pwm1", "ipg_per", base + 0x78, 16); ++ clk[pwm2] = imx_clk_gate2("pwm2", "ipg_per", base + 0x78, 18); ++ clk[pwm3] = imx_clk_gate2("pwm3", "ipg_per", base + 0x78, 20); ++ clk[pwm4] = imx_clk_gate2("pwm4", "ipg_per", base + 0x78, 22); ++ clk[gpmi_bch_apb] = imx_clk_gate2("gpmi_bch_apb", "usdhc3", base + 0x78, 24); ++ clk[gpmi_bch] = imx_clk_gate2("gpmi_bch", "usdhc4", base + 0x78, 26); ++ clk[gpmi_io] = imx_clk_gate2("gpmi_io", "enfc", base + 0x78, 28); ++ clk[gpmi_apb] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); ++ clk[rom] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0); ++ clk[sata] = imx_clk_gate2("sata", "ipg", base + 0x7c, 4); ++ clk[sdma] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); ++ clk[spba] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); ++ clk[spdif] = imx_clk_gate2("spdif", "spdif_podf", base + 0x7c, 14); ++ clk[ssi1_ipg] = imx_clk_gate2("ssi1_ipg", "ipg", base + 0x7c, 18); ++ clk[ssi2_ipg] = imx_clk_gate2("ssi2_ipg", "ipg", base + 0x7c, 20); ++ clk[ssi3_ipg] = imx_clk_gate2("ssi3_ipg", "ipg", base + 0x7c, 22); ++ clk[ssi1] = imx_clk_gate2("ssi1", "ssi1_podf", base + 0x7c, 18); ++ clk[ssi2] = imx_clk_gate2("ssi2", "ssi2_podf", base + 0x7c, 20); ++ clk[ssi3] = imx_clk_gate2("ssi3", "ssi3_podf", base + 0x7c, 22); ++ clk[uart_ipg] = imx_clk_gate2("uart_ipg", "ipg", base + 0x7c, 24); ++ clk[uart_serial] = imx_clk_gate2("uart_serial", "uart_serial_podf", base + 0x7c, 26); ++ clk[usboh3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); ++ clk[usdhc1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); ++ clk[usdhc2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); ++ clk[usdhc3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); ++ clk[usdhc4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); ++ clk[eim_slow] = imx_clk_gate2("eim_slow", "emi_slow_podf", base + 0x80, 10); ++ clk[vdo_axi] = imx_clk_gate2("vdo_axi", "vdo_axi_sel", base + 0x80, 12); ++ clk[vpu_axi] = imx_clk_gate2("vpu_axi", "vpu_axi_podf", base + 0x80, 14); ++ clk[cko1] = imx_clk_gate("cko1", "cko1_podf", base + 0x60, 7); ++ clk[cko2] = imx_clk_gate("cko2", "cko2_podf", base + 0x60, 24); ++ ++ for (i = 0; i < ARRAY_SIZE(clk); i++) ++ if (IS_ERR(clk[i])) ++ pr_err("i.MX6q clk %d: register failed with %ld\n", ++ i, PTR_ERR(clk[i])); ++ ++ /* Initialize clock gate status */ ++ writel_relaxed(1 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(1) | ++ 3 << CCM_CCGR_OFFSET(0), base + 0x68); ++ if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) ++ writel_relaxed(3 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10), base + 0x6c); ++ else ++ writel_relaxed(3 << CCM_CCGR_OFFSET(10), base + 0x6c); ++ writel_relaxed(1 << CCM_CCGR_OFFSET(12) | ++ 3 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10) | ++ 3 << CCM_CCGR_OFFSET(9) | ++ 3 << CCM_CCGR_OFFSET(8), base + 0x70); ++ writel_relaxed(3 << CCM_CCGR_OFFSET(14) | ++ 1 << CCM_CCGR_OFFSET(13) | ++ 3 << CCM_CCGR_OFFSET(12) | ++ 1 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10), base + 0x74); ++ writel_relaxed(3 << CCM_CCGR_OFFSET(7) | ++ 3 << CCM_CCGR_OFFSET(6) | ++ 3 << CCM_CCGR_OFFSET(4), base + 0x78); ++ writel_relaxed(1 << CCM_CCGR_OFFSET(0), base + 0x7c); ++ writel_relaxed(0, base + 0x80); ++ ++ /* Make sure PFDs are disabled at boot. */ ++ reg = readl_relaxed(anatop_base + 0x100); ++ /* Cannot disable pll2_pfd2_396M, as it is the MMDC clock in iMX6DL */ ++ if (cpu_is_imx6dl()) ++ reg |= 0x80008080; ++ else ++ reg |= 0x80808080; ++ writel_relaxed(reg, anatop_base + 0x100); ++ ++ /* Disable PLL3 PFDs. */ ++ reg = readl_relaxed(anatop_base + 0xF0); ++ reg |= 0x80808080; ++ writel_relaxed(reg, anatop_base + 0xF0); ++ ++ /* Make sure PLLs is disabled */ ++ reg = readl_relaxed(anatop_base + 0xA0); ++ reg &= ~(1 << 13); ++ writel_relaxed(reg, anatop_base + 0xA0); ++ ++ clk_data.clks = clk; ++ clk_data.clk_num = ARRAY_SIZE(clk); ++ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); ++ ++ clk_register_clkdev(clk[gpt_ipg], "ipg", "imx-gpt.0"); ++ clk_register_clkdev(clk[gpt_ipg_per], "per", "imx-gpt.0"); ++ clk_register_clkdev(clk[gpt_3m], "gpt_3m", "imx-gpt.0"); ++ clk_register_clkdev(clk[cko1_sel], "cko1_sel", NULL); ++ clk_register_clkdev(clk[ahb], "ahb", NULL); ++ clk_register_clkdev(clk[cko1], "cko1", NULL); ++ clk_register_clkdev(clk[arm], NULL, "cpu0"); ++ clk_register_clkdev(clk[pll4_audio_div], "pll4_audio_div", NULL); ++ clk_register_clkdev(clk[pll4_sel], "pll4_sel", NULL); ++ clk_register_clkdev(clk[lvds2_in], "lvds2_in", NULL); ++ clk_register_clkdev(clk[esai], "esai", NULL); ++ ++ if (cpu_is_imx6dl()) { ++ clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]); ++ } ++ ++ clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di0_sel], clk[ipu1_di0_pre]); ++ clk_set_parent(clk[ipu1_di1_sel], clk[ipu1_di1_pre]); ++ clk_set_parent(clk[ipu2_di0_sel], clk[ipu2_di0_pre]); ++ clk_set_parent(clk[ipu2_di1_sel], clk[ipu2_di1_pre]); ++ ++ /* ++ * The gpmi needs 100MHz frequency in the EDO/Sync mode, ++ * We can not get the 100MHz from the pll2_pfd0_352m. ++ * So choose pll2_pfd2_396m as enfc_sel's parent. ++ */ ++ clk_set_parent(clk[enfc_sel], clk[pll2_pfd2_396m]); ++ ++ /* Set the parent clks of PCIe lvds1 and pcie_axi to be sata ref, axi */ ++ if (clk_set_parent(clk[lvds1_sel], clk[sata_ref])) ++ pr_err("Failed to set PCIe bus parent clk.\n"); ++ if (clk_set_parent(clk[pcie_axi_sel], clk[axi])) ++ pr_err("Failed to set PCIe parent clk.\n"); ++ ++ /* gpu clock initilazation */ ++ clk_set_parent(clk[gpu3d_shader_sel], clk[pll2_pfd1_594m]); ++ clk_set_rate(clk[gpu3d_shader], 594000000); ++ clk_set_parent(clk[gpu3d_core_sel], clk[mmdc_ch0_axi]); ++ clk_set_rate(clk[gpu3d_core], 528000000); ++ clk_set_parent(clk[gpu2d_core_sel], clk[pll3_usb_otg]); ++ ++ for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) ++ clk_prepare_enable(clk[clks_init_on[i]]); ++ ++ if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { ++ clk_prepare_enable(clk[usbphy1_gate]); ++ clk_prepare_enable(clk[usbphy2_gate]); ++ } ++ ++ /* ipu clock initialization */ ++ init_ldb_clks(pll2_pfd0_352m); ++ clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu2_di1_pre_sel], clk[pll5_video_div]); ++ clk_set_parent(clk[ipu1_di0_sel], clk[ipu1_di0_pre]); ++ clk_set_parent(clk[ipu1_di1_sel], clk[ipu1_di1_pre]); ++ clk_set_parent(clk[ipu2_di0_sel], clk[ipu2_di0_pre]); ++ clk_set_parent(clk[ipu2_di1_sel], clk[ipu2_di1_pre]); ++ if (cpu_is_imx6dl()) { ++ clk_set_rate(clk[pll3_pfd1_540m], 540000000); ++ clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]); ++ clk_set_parent(clk[axi_sel], clk[pll3_pfd1_540m]); ++ } else if (cpu_is_imx6q()) { ++ clk_set_parent(clk[ipu1_sel], clk[mmdc_ch0_axi]); ++ clk_set_parent(clk[ipu2_sel], clk[mmdc_ch0_axi]); ++ } ++ ++ /* ++ * Let's initially set up CLKO with OSC24M, since this configuration ++ * is widely used by imx6q board designs to clock audio codec. ++ */ ++ ret = clk_set_parent(clk[cko2_sel], clk[osc]); ++ if (!ret) ++ ret = clk_set_parent(clk[cko], clk[cko2]); ++ if (ret) ++ pr_warn("failed to set up CLKO: %d\n", ret); ++ ++ /* Audio-related clocks configuration */ ++ clk_set_parent(clk[spdif_sel], clk[pll3_pfd3_454m]); ++ ++ /* All existing boards with PCIe use LVDS1 */ ++ if (IS_ENABLED(CONFIG_PCI_IMX6)) ++ clk_set_parent(clk[lvds1_sel], clk[sata_ref]); ++ ++ /* Audio clocks */ ++ clk_set_parent(clk[ssi1_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[ssi2_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[ssi3_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[esai_sel], clk[pll4_audio_div]); ++ clk_set_parent(clk[spdif_sel], clk[pll3_pfd3_454m]); ++ clk_set_parent(clk[asrc_sel], clk[pll3_usb_otg]); ++ clk_set_rate(clk[asrc_sel], 7500000); ++ ++ /* Set pll4_audio to a value that can derive 5K-88.2KHz and 8K-96KHz */ ++ clk_set_rate(clk[pll4_audio_div], 541900800); ++ ++ /* Set initial power mode */ ++ imx6q_set_lpm(WAIT_CLOCKED); ++ ++ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpt"); ++ base = of_iomap(np, 0); ++ WARN_ON(!base); ++ irq = irq_of_parse_and_map(np, 0); ++ mxc_timer_init(base, irq); ++} ++CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init); +diff -Nur linux-3.14.36/arch/arm/mach-imx/clk-imx6sl.c linux-openelec/arch/arm/mach-imx/clk-imx6sl.c +--- linux-3.14.36/arch/arm/mach-imx/clk-imx6sl.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/clk-imx6sl.c 2015-05-06 12:05:43.000000000 -0500 +@@ -7,9 +7,29 @@ + * + */ + ++#define CCM_CCDR_OFFSET 0x4 ++#define ANATOP_PLL_USB1 0x10 ++#define ANATOP_PLL_USB2 0x20 ++#define ANATOP_PLL_ENET 0xE0 ++#define ANATOP_PLL_BYPASS_OFFSET (1 << 16) ++#define ANATOP_PLL_ENABLE_OFFSET (1 << 13) ++#define ANATOP_PLL_POWER_OFFSET (1 << 12) ++#define ANATOP_PFD_480n_OFFSET 0xf0 ++#define ANATOP_PFD_528n_OFFSET 0x100 ++#define PFD0_CLKGATE (1 << 7) ++#define PFD1_CLK_GATE (1 << 15) ++#define PFD2_CLK_GATE (1 << 23) ++#define PFD3_CLK_GATE (1 << 31) ++#define CCDR_CH0_HS_BYP 17 ++#define OSC_RATE 24000000 ++ ++#define CCM_CCGR_OFFSET(index) (index * 2) ++ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -18,6 +38,7 @@ + #include "clk.h" + #include "common.h" + ++static bool uart_from_osc; + static const char const *step_sels[] = { "osc", "pll2_pfd2", }; + static const char const *pll1_sw_sels[] = { "pll1_sys", "step", }; + static const char const *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", }; +@@ -25,8 +46,8 @@ + static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", }; + static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; + static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; +-static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", }; +-static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", }; ++static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2", }; ++static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2", }; + static const char const *csi_lcdif_sels[] = { "mmdc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", }; + static const char const *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", }; + static const char const *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", }; +@@ -38,7 +59,7 @@ + static const char const *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", }; + static const char const *audio_sels[] = { "pll4_audio_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", }; + static const char const *ecspi_sels[] = { "pll3_60m", "osc", }; +-static const char const *uart_sels[] = { "pll3_80m", "osc", }; ++static const char const *uart_sels[] = { "pll3_80m", "uart_osc_4M", }; + + static struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, +@@ -65,6 +86,80 @@ + + static struct clk *clks[IMX6SL_CLK_END]; + static struct clk_onecell_data clk_data; ++static u32 cur_arm_podf; ++static u32 pll1_org_rate; ++ ++extern int low_bus_freq_mode; ++extern int audio_bus_freq_mode; ++ ++/* ++ * On MX6SL, need to ensure that the ARM:IPG clock ratio is maintained ++ * within 12:5 when the clocks to ARM are gated when the SOC enters ++ * WAIT mode. This is necessary to avoid WAIT mode issue (an early ++ * interrupt waking up the ARM). ++ * This function will set the ARM clk to max value within the 12:5 limit. ++ */ ++void imx6sl_set_wait_clk(bool enter) ++{ ++ u32 parent_rate; ++ ++ if (enter) { ++ u32 wait_podf; ++ u32 new_parent_rate = OSC_RATE; ++ u32 ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); ++ u32 max_arm_wait_clk = (12 * ipg_rate) / 5; ++ parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); ++ cur_arm_podf = parent_rate / clk_get_rate(clks[IMX6SL_CLK_ARM]); ++ if (low_bus_freq_mode) { ++ /* ++ * IPG clk is at 12MHz at this point, we can only run ++ * ARM at a max of 28.8MHz. So we need to set ARM ++ * to run from the 24MHz OSC, as there is no way to ++ * get 28.8MHz when ARM is sourced from PLL1. ++ */ ++ clk_set_parent(clks[IMX6SL_CLK_STEP], ++ clks[IMX6SL_CLK_OSC]); ++ clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], ++ clks[IMX6SL_CLK_STEP]); ++ } else if (audio_bus_freq_mode) { ++ /* ++ * In this mode ARM is from PLL2_PFD2 (396MHz), ++ * but IPG is at 12MHz. Need to switch ARM to run ++ * from the bypassed PLL1 clocks so that we can run ++ * ARM at 24MHz. ++ */ ++ pll1_org_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SYS]); ++ /* Ensure PLL1 is at 24MHz. */ ++ clk_set_rate(clks[IMX6SL_CLK_PLL1_SYS], OSC_RATE); ++ clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], clks[IMX6SL_CLK_PLL1_SYS]); ++ } else ++ new_parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); ++ wait_podf = (new_parent_rate + max_arm_wait_clk - 1) / ++ max_arm_wait_clk; ++ ++ clk_set_rate(clks[IMX6SL_CLK_ARM], new_parent_rate / wait_podf); ++ } else { ++ if (low_bus_freq_mode) ++ /* Move ARM back to PLL1. */ ++ clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], ++ clks[IMX6SL_CLK_PLL1_SYS]); ++ else if (audio_bus_freq_mode) { ++ /* Move ARM back to PLL2_PFD2 via STEP_CLK. */ ++ clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], clks[IMX6SL_CLK_STEP]); ++ clk_set_rate(clks[IMX6SL_CLK_PLL1_SYS], pll1_org_rate); ++ } ++ parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); ++ clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / cur_arm_podf); ++ } ++} ++ ++static int __init setup_uart_clk(char *uart_rate) ++{ ++ uart_from_osc = true; ++ return 1; ++} ++ ++__setup("uart_at_4M", setup_uart_clk); + + static void __init imx6sl_clocks_init(struct device_node *ccm_node) + { +@@ -72,6 +167,8 @@ + void __iomem *base; + int irq; + int i; ++ int ret; ++ u32 reg; + + clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clks[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); +@@ -82,13 +179,18 @@ + WARN_ON(!base); + + /* type name parent base div_mask */ +- clks[IMX6SL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f); +- clks[IMX6SL_CLK_PLL2_BUS] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1); +- clks[IMX6SL_CLK_PLL3_USB_OTG] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3); +- clks[IMX6SL_CLK_PLL4_AUDIO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f); +- clks[IMX6SL_CLK_PLL5_VIDEO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f); +- clks[IMX6SL_CLK_PLL6_ENET] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3); +- clks[IMX6SL_CLK_PLL7_USB_HOST] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3); ++ clks[IMX6SL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f, true); ++ clks[IMX6SL_CLK_PLL2_BUS] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1, true); ++ clks[IMX6SL_CLK_PLL3_USB_OTG] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3, false); ++ clks[IMX6SL_CLK_PLL4_AUDIO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f, false); ++ clks[IMX6SL_CLK_PLL5_VIDEO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f, false); ++ clks[IMX6SL_CLK_PLL6_ENET] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3, false); ++ clks[IMX6SL_CLK_PLL7_USB_HOST] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3, false); ++ ++ /* Ensure the AHB clk is at 132MHz. */ ++ ret = clk_set_rate(clks[IMX6SL_CLK_AHB], 132000000); ++ if (ret) ++ pr_warn("%s: failed to set AHB clock rate %d\n", __func__, ret); + + /* + * usbphy1 and usbphy2 are implemented as dummy gates using reserve +@@ -118,11 +220,36 @@ + clks[IMX6SL_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_usb_otg", base + 0xf0, 2); + clks[IMX6SL_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_usb_otg", base + 0xf0, 3); + +- /* name parent_name mult div */ +- clks[IMX6SL_CLK_PLL2_198M] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2", 1, 2); +- clks[IMX6SL_CLK_PLL3_120M] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); +- clks[IMX6SL_CLK_PLL3_80M] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); +- clks[IMX6SL_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); ++ /* name parent_name mult div */ ++ clks[IMX6SL_CLK_PLL2_198M] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2", 1, 2); ++ clks[IMX6SL_CLK_PLL3_120M] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); ++ clks[IMX6SL_CLK_PLL3_80M] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); ++ clks[IMX6SL_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); ++ clks[IMX6SL_CLK_UART_OSC_4M] = imx_clk_fixed_factor("uart_osc_4M", "osc", 1, 6); ++ ++ /* Ensure all PFDs but PLL2_PFD2 are disabled. */ ++ reg = readl_relaxed(base + ANATOP_PFD_480n_OFFSET); ++ reg |= (PFD0_CLKGATE | PFD1_CLK_GATE | PFD2_CLK_GATE | PFD3_CLK_GATE); ++ writel_relaxed(reg, base + ANATOP_PFD_480n_OFFSET); ++ reg = readl_relaxed(base + ANATOP_PFD_528n_OFFSET); ++ reg |= (PFD0_CLKGATE | PFD1_CLK_GATE); ++ writel_relaxed(reg, base + ANATOP_PFD_528n_OFFSET); ++ ++ /* Ensure Unused PLLs are disabled. */ ++ reg = readl_relaxed(base + ANATOP_PLL_USB1); ++ reg |= ANATOP_PLL_BYPASS_OFFSET; ++ reg &= ~(ANATOP_PLL_ENABLE_OFFSET | ANATOP_PLL_POWER_OFFSET); ++ writel_relaxed(reg, base + ANATOP_PLL_USB1); ++ ++ reg = readl_relaxed(base + ANATOP_PLL_USB2); ++ reg |= ANATOP_PLL_BYPASS_OFFSET; ++ reg &= ~(ANATOP_PLL_ENABLE_OFFSET | ANATOP_PLL_POWER_OFFSET); ++ writel_relaxed(reg, base + ANATOP_PLL_USB2); ++ ++ reg = readl_relaxed(base + ANATOP_PLL_ENET); ++ reg |= (ANATOP_PLL_BYPASS_OFFSET | ANATOP_PLL_POWER_OFFSET); ++ reg &= ~ANATOP_PLL_ENABLE_OFFSET; ++ writel_relaxed(reg, base + ANATOP_PLL_ENET); + + np = ccm_node; + base = of_iomap(np, 0); +@@ -158,7 +285,7 @@ + clks[IMX6SL_CLK_EPDC_PIX_SEL] = imx_clk_mux("epdc_pix_sel", base + 0x38, 15, 3, epdc_pix_sels, ARRAY_SIZE(epdc_pix_sels)); + clks[IMX6SL_CLK_SPDIF0_SEL] = imx_clk_mux("spdif0_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SL_CLK_SPDIF1_SEL] = imx_clk_mux("spdif1_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); +- clks[IMX6SL_CLK_EXTERN_AUDIO_SEL] = imx_clk_mux("extern_audio_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); ++ clks[IMX6SL_CLK_EXTERN_AUDIO_SEL] = imx_clk_mux_flags("extern_audio_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels), CLK_SET_RATE_PARENT); + clks[IMX6SL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + clks[IMX6SL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + +@@ -168,8 +295,8 @@ + + /* name parent_name reg shift width */ + clks[IMX6SL_CLK_OCRAM_PODF] = imx_clk_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3); +- clks[IMX6SL_CLK_PERIPH_CLK2_PODF] = imx_clk_divider("periph_clk2_podf", "periph_clk2_sel", base + 0x14, 27, 3); +- clks[IMX6SL_CLK_PERIPH2_CLK2_PODF] = imx_clk_divider("periph2_clk2_podf", "periph2_clk2_sel", base + 0x14, 0, 3); ++ clks[IMX6SL_CLK_PERIPH_CLK2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); ++ clks[IMX6SL_CLK_PERIPH2_CLK2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + clks[IMX6SL_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); + clks[IMX6SL_CLK_CSI_PODF] = imx_clk_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); + clks[IMX6SL_CLK_LCDIF_AXI_PODF] = imx_clk_divider("lcdif_axi_podf", "lcdif_axi_sel", base + 0x3c, 16, 3); +@@ -251,6 +378,25 @@ + pr_err("i.MX6SL clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + ++ /* Initialize clock gate status */ ++ writel_relaxed(1 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(1) | ++ 3 << CCM_CCGR_OFFSET(0), base + 0x68); ++ writel_relaxed(3 << CCM_CCGR_OFFSET(10), base + 0x6c); ++ writel_relaxed(1 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10) | ++ 3 << CCM_CCGR_OFFSET(9) | ++ 3 << CCM_CCGR_OFFSET(8), base + 0x70); ++ writel_relaxed(3 << CCM_CCGR_OFFSET(14) | ++ 3 << CCM_CCGR_OFFSET(13) | ++ 3 << CCM_CCGR_OFFSET(12) | ++ 3 << CCM_CCGR_OFFSET(11) | ++ 3 << CCM_CCGR_OFFSET(10), base + 0x74); ++ writel_relaxed(3 << CCM_CCGR_OFFSET(7) | ++ 3 << CCM_CCGR_OFFSET(4), base + 0x78); ++ writel_relaxed(1 << CCM_CCGR_OFFSET(0), base + 0x7c); ++ writel_relaxed(0, base + 0x80); ++ + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +@@ -258,17 +404,58 @@ + clk_register_clkdev(clks[IMX6SL_CLK_GPT], "ipg", "imx-gpt.0"); + clk_register_clkdev(clks[IMX6SL_CLK_GPT_SERIAL], "per", "imx-gpt.0"); + ++ /* ++ * Make sure the ARM clk is enabled to maintain the correct usecount ++ * and enabling/disabling of parent PLLs. ++ */ ++ ret = clk_prepare_enable(clks[IMX6SL_CLK_ARM]); ++ if (ret) ++ pr_warn("%s: failed to enable ARM core clock %d\n", ++ __func__, ret); ++ ++ /* ++ * Make sure the MMDC clk is enabled to maintain the correct usecount ++ * and enabling/disabling of parent PLLs. ++ */ ++ ret = clk_prepare_enable(clks[IMX6SL_CLK_MMDC_ROOT]); ++ if (ret) ++ pr_warn("%s: failed to enable MMDC clock %d\n", ++ __func__, ret); ++ + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]); + clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]); + } + ++ clk_set_parent(clks[IMX6SL_CLK_GPU2D_OVG_SEL], ++ clks[IMX6SL_CLK_PLL2_BUS]); ++ clk_set_parent(clks[IMX6SL_CLK_GPU2D_SEL], clks[IMX6SL_CLK_PLL2_BUS]); ++ + /* Audio-related clocks configuration */ + clk_set_parent(clks[IMX6SL_CLK_SPDIF0_SEL], clks[IMX6SL_CLK_PLL3_PFD3]); + ++ /* set extern_audio to be sourced from PLL4/audio PLL */ ++ clk_set_parent(clks[IMX6SL_CLK_EXTERN_AUDIO_SEL], clks[IMX6SL_CLK_PLL4_AUDIO_DIV]); ++ /* set extern_audio to 24MHz */ ++ clk_set_rate(clks[IMX6SL_CLK_PLL4_AUDIO], 24000000); ++ clk_set_rate(clks[IMX6SL_CLK_EXTERN_AUDIO], 24000000); ++ ++ /* set SSI2 parent to PLL4 */ ++ clk_set_parent(clks[IMX6SL_CLK_SSI2_SEL], clks[IMX6SL_CLK_PLL4_AUDIO_DIV]); ++ clk_set_rate(clks[IMX6SL_CLK_SSI2], 24000000); ++ + /* Set initial power mode */ + imx6q_set_lpm(WAIT_CLOCKED); + ++ /* Ensure that CH0 handshake is bypassed. */ ++ reg = readl_relaxed(base + CCM_CCDR_OFFSET); ++ reg |= 1 << CCDR_CH0_HS_BYP; ++ writel_relaxed(reg, base + CCM_CCDR_OFFSET); ++ ++ /* Set the UART parent if needed. */ ++ if (uart_from_osc) ++ ret = clk_set_parent(clks[IMX6SL_CLK_UART_SEL], clks[IMX6SL_CLK_UART_OSC_4M]); ++ + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-gpt"); + base = of_iomap(np, 0); + WARN_ON(!base); +diff -Nur linux-3.14.36/arch/arm/mach-imx/clk-pfd.c linux-openelec/arch/arm/mach-imx/clk-pfd.c +--- linux-3.14.36/arch/arm/mach-imx/clk-pfd.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/clk-pfd.c 2015-05-06 12:05:43.000000000 -0500 +@@ -1,5 +1,5 @@ + /* +- * Copyright 2012 Freescale Semiconductor, Inc. ++ * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public +@@ -17,6 +17,8 @@ + #include + #include "clk.h" + ++#define BYPASS_RATE 24000000 ++ + /** + * struct clk_pfd - IMX PFD clock + * @clk_hw: clock source +@@ -62,9 +64,14 @@ + u64 tmp = parent_rate; + u8 frac = (readl_relaxed(pfd->reg) >> (pfd->idx * 8)) & 0x3f; + +- tmp *= 18; +- do_div(tmp, frac); +- ++ /* ++ * If the parent PLL is in bypass state, the PFDs ++ * are also in bypass state. ++ */ ++ if (tmp != BYPASS_RATE) { ++ tmp *= 18; ++ do_div(tmp, frac); ++ } + return tmp; + } + +@@ -74,17 +81,22 @@ + u64 tmp = *prate; + u8 frac; + +- tmp = tmp * 18 + rate / 2; +- do_div(tmp, rate); +- frac = tmp; +- if (frac < 12) +- frac = 12; +- else if (frac > 35) +- frac = 35; +- tmp = *prate; +- tmp *= 18; +- do_div(tmp, frac); +- ++ /* ++ * If the parent PLL is in bypass state, the PFDs ++ * are also in bypass state. ++ */ ++ if (tmp != BYPASS_RATE) { ++ tmp = tmp * 18 + rate / 2; ++ do_div(tmp, rate); ++ frac = tmp; ++ if (frac < 12) ++ frac = 12; ++ else if (frac > 35) ++ frac = 35; ++ tmp = *prate; ++ tmp *= 18; ++ do_div(tmp, frac); ++ } + return tmp; + } + +@@ -95,6 +107,9 @@ + u64 tmp = parent_rate; + u8 frac; + ++ if (tmp == BYPASS_RATE) ++ return 0; ++ + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; +diff -Nur linux-3.14.36/arch/arm/mach-imx/clk-pllv3.c linux-openelec/arch/arm/mach-imx/clk-pllv3.c +--- linux-3.14.36/arch/arm/mach-imx/clk-pllv3.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/clk-pllv3.c 2015-05-06 12:05:43.000000000 -0500 +@@ -26,12 +26,15 @@ + #define BM_PLL_ENABLE (0x1 << 13) + #define BM_PLL_BYPASS (0x1 << 16) + #define BM_PLL_LOCK (0x1 << 31) ++#define BYPASS_RATE 24000000 ++#define BYPASS_MASK 0x10000 + + /** + * struct clk_pllv3 - IMX PLL clock version 3 + * @clk_hw: clock source + * @base: base address of PLL registers + * @powerup_set: set POWER bit to power up the PLL ++ * @always_on : Leave the PLL powered up all the time. + * @div_mask: mask of divider bits + * + * IMX PLL clock version 3, found on i.MX6 series. Divider for pllv3 +@@ -41,7 +44,9 @@ + struct clk_hw hw; + void __iomem *base; + bool powerup_set; ++ bool always_on; + u32 div_mask; ++ u32 rate_req; + }; + + #define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw) +@@ -61,54 +66,53 @@ + break; + if (time_after(jiffies, timeout)) + break; +- usleep_range(50, 500); ++ udelay(100); + } while (1); + + return readl_relaxed(pll->base) & BM_PLL_LOCK ? 0 : -ETIMEDOUT; + } + +-static int clk_pllv3_prepare(struct clk_hw *hw) ++static int clk_pllv3_power_up_down(struct clk_hw *hw, bool enable) + { + struct clk_pllv3 *pll = to_clk_pllv3(hw); +- u32 val; +- int ret; +- +- val = readl_relaxed(pll->base); +- if (pll->powerup_set) +- val |= BM_PLL_POWER; +- else +- val &= ~BM_PLL_POWER; +- writel_relaxed(val, pll->base); +- +- ret = clk_pllv3_wait_lock(pll); +- if (ret) +- return ret; ++ u32 val, ret = 0; + +- val = readl_relaxed(pll->base); +- val &= ~BM_PLL_BYPASS; +- writel_relaxed(val, pll->base); +- +- return 0; +-} ++ if (enable) { ++ val = readl_relaxed(pll->base); ++ val &= ~BM_PLL_BYPASS; ++ if (pll->powerup_set) ++ val |= BM_PLL_POWER; ++ else ++ val &= ~BM_PLL_POWER; ++ writel_relaxed(val, pll->base); ++ ++ ret = clk_pllv3_wait_lock(pll); ++ } else { ++ val = readl_relaxed(pll->base); ++ val |= BM_PLL_BYPASS; ++ if (pll->powerup_set) ++ val &= ~BM_PLL_POWER; ++ else ++ val |= BM_PLL_POWER; ++ writel_relaxed(val, pll->base); ++ } + +-static void clk_pllv3_unprepare(struct clk_hw *hw) +-{ +- struct clk_pllv3 *pll = to_clk_pllv3(hw); +- u32 val; ++ if (!ret) { ++ val = readl_relaxed(pll->base); ++ val &= ~BM_PLL_BYPASS; ++ writel_relaxed(val, pll->base); ++ } + +- val = readl_relaxed(pll->base); +- val |= BM_PLL_BYPASS; +- if (pll->powerup_set) +- val &= ~BM_PLL_POWER; +- else +- val |= BM_PLL_POWER; +- writel_relaxed(val, pll->base); ++ return ret; + } + + static int clk_pllv3_enable(struct clk_hw *hw) + { + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 val; ++ ++ if (pll->rate_req != BYPASS_RATE) ++ clk_pllv3_power_up_down(hw, true); + + val = readl_relaxed(pll->base); + val |= BM_PLL_ENABLE; +@@ -123,8 +127,12 @@ + u32 val; + + val = readl_relaxed(pll->base); +- val &= ~BM_PLL_ENABLE; ++ if (!pll->always_on) ++ val &= ~BM_PLL_ENABLE; + writel_relaxed(val, pll->base); ++ ++ if (pll->rate_req != BYPASS_RATE) ++ clk_pllv3_power_up_down(hw, false); + } + + static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw, +@@ -132,8 +140,15 @@ + { + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 div = readl_relaxed(pll->base) & pll->div_mask; ++ u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; ++ u32 rate; ++ ++ if (pll->rate_req == BYPASS_RATE && bypass) ++ rate = BYPASS_RATE; ++ else ++ rate = (div == 1) ? parent_rate * 22 : parent_rate * 20; + +- return (div == 1) ? parent_rate * 22 : parent_rate * 20; ++ return rate; + } + + static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned long rate, +@@ -141,6 +156,10 @@ + { + unsigned long parent_rate = *prate; + ++ /* If the PLL is bypassed, its rate is 24MHz. */ ++ if (rate == BYPASS_RATE) ++ return BYPASS_RATE; ++ + return (rate >= parent_rate * 22) ? parent_rate * 22 : + parent_rate * 20; + } +@@ -151,6 +170,22 @@ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 val, div; + ++ pll->rate_req = rate; ++ val = readl_relaxed(pll->base); ++ ++ /* If the PLL is bypassed, its rate is 24MHz. */ ++ if (rate == BYPASS_RATE) { ++ /* Set the bypass bit. */ ++ val |= BM_PLL_BYPASS; ++ /* Power down the PLL. */ ++ if (pll->powerup_set) ++ val &= ~BM_PLL_POWER; ++ else ++ val |= BM_PLL_POWER; ++ writel_relaxed(val, pll->base); ++ ++ return 0; ++ } + if (rate == parent_rate * 22) + div = 1; + else if (rate == parent_rate * 20) +@@ -167,8 +202,6 @@ + } + + static const struct clk_ops clk_pllv3_ops = { +- .prepare = clk_pllv3_prepare, +- .unprepare = clk_pllv3_unprepare, + .enable = clk_pllv3_enable, + .disable = clk_pllv3_disable, + .recalc_rate = clk_pllv3_recalc_rate, +@@ -181,6 +214,10 @@ + { + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 div = readl_relaxed(pll->base) & pll->div_mask; ++ u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; ++ ++ if (pll->rate_req == BYPASS_RATE && bypass) ++ return BYPASS_RATE; + + return parent_rate * div / 2; + } +@@ -193,6 +230,9 @@ + unsigned long max_rate = parent_rate * 108 / 2; + u32 div; + ++ if (rate == BYPASS_RATE) ++ return BYPASS_RATE; ++ + if (rate > max_rate) + rate = max_rate; + else if (rate < min_rate) +@@ -210,9 +250,26 @@ + unsigned long max_rate = parent_rate * 108 / 2; + u32 val, div; + +- if (rate < min_rate || rate > max_rate) ++ if (rate != BYPASS_RATE && (rate < min_rate || rate > max_rate)) + return -EINVAL; + ++ pll->rate_req = rate; ++ val = readl_relaxed(pll->base); ++ ++ if (rate == BYPASS_RATE) { ++ /* ++ * Set the PLL in bypass mode if rate requested is ++ * BYPASS_RATE. ++ */ ++ val |= BM_PLL_BYPASS; ++ /* Power down the PLL. */ ++ if (pll->powerup_set) ++ val &= ~BM_PLL_POWER; ++ else ++ val |= BM_PLL_POWER; ++ writel_relaxed(val, pll->base); ++ return 0; ++ } + div = rate * 2 / parent_rate; + val = readl_relaxed(pll->base); + val &= ~pll->div_mask; +@@ -223,8 +280,6 @@ + } + + static const struct clk_ops clk_pllv3_sys_ops = { +- .prepare = clk_pllv3_prepare, +- .unprepare = clk_pllv3_unprepare, + .enable = clk_pllv3_enable, + .disable = clk_pllv3_disable, + .recalc_rate = clk_pllv3_sys_recalc_rate, +@@ -239,6 +294,10 @@ + u32 mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET); + u32 mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET); + u32 div = readl_relaxed(pll->base) & pll->div_mask; ++ u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; ++ ++ if (pll->rate_req == BYPASS_RATE && bypass) ++ return BYPASS_RATE; + + return (parent_rate * div) + ((parent_rate / mfd) * mfn); + } +@@ -253,6 +312,9 @@ + u32 mfn, mfd = 1000000; + s64 temp64; + ++ if (rate == BYPASS_RATE) ++ return BYPASS_RATE; ++ + if (rate > max_rate) + rate = max_rate; + else if (rate < min_rate) +@@ -273,13 +335,36 @@ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + unsigned long min_rate = parent_rate * 27; + unsigned long max_rate = parent_rate * 54; +- u32 val, div; ++ u32 val, newval, div; + u32 mfn, mfd = 1000000; + s64 temp64; ++ int ret; + +- if (rate < min_rate || rate > max_rate) ++ if (rate != BYPASS_RATE && (rate < min_rate || rate > max_rate)) + return -EINVAL; + ++ pll->rate_req = rate; ++ val = readl_relaxed(pll->base); ++ ++ if (rate == BYPASS_RATE) { ++ /* ++ * Set the PLL in bypass mode if rate requested is ++ * BYPASS_RATE. ++ */ ++ /* Bypass the PLL */ ++ val |= BM_PLL_BYPASS; ++ /* Power down the PLL. */ ++ if (pll->powerup_set) ++ val &= ~BM_PLL_POWER; ++ else ++ val |= BM_PLL_POWER; ++ writel_relaxed(val, pll->base); ++ return 0; ++ } ++ /* Else clear the bypass bit. */ ++ val &= ~BM_PLL_BYPASS; ++ writel_relaxed(val, pll->base); ++ + div = rate / parent_rate; + temp64 = (u64) (rate - div * parent_rate); + temp64 *= mfd; +@@ -287,18 +372,30 @@ + mfn = temp64; + + val = readl_relaxed(pll->base); +- val &= ~pll->div_mask; +- val |= div; +- writel_relaxed(val, pll->base); ++ ++ /* set the PLL into bypass mode */ ++ newval = val | BM_PLL_BYPASS; ++ writel_relaxed(newval, pll->base); ++ ++ /* configure the new frequency */ ++ newval &= ~pll->div_mask; ++ newval |= div; ++ writel_relaxed(newval, pll->base); + writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET); +- writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET); ++ writel(mfd, pll->base + PLL_DENOM_OFFSET); + +- return clk_pllv3_wait_lock(pll); ++ ret = clk_pllv3_wait_lock(pll); ++ if (ret == 0 && val & BM_PLL_POWER) { ++ /* only if it locked can we switch back to the PLL */ ++ newval &= ~BM_PLL_BYPASS; ++ newval |= val & BM_PLL_BYPASS; ++ writel(newval, pll->base); ++ } ++ ++ return ret; + } + + static const struct clk_ops clk_pllv3_av_ops = { +- .prepare = clk_pllv3_prepare, +- .unprepare = clk_pllv3_unprepare, + .enable = clk_pllv3_enable, + .disable = clk_pllv3_disable, + .recalc_rate = clk_pllv3_av_recalc_rate, +@@ -313,8 +410,6 @@ + } + + static const struct clk_ops clk_pllv3_enet_ops = { +- .prepare = clk_pllv3_prepare, +- .unprepare = clk_pllv3_unprepare, + .enable = clk_pllv3_enable, + .disable = clk_pllv3_disable, + .recalc_rate = clk_pllv3_enet_recalc_rate, +@@ -322,7 +417,7 @@ + + struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, +- u32 div_mask) ++ u32 div_mask, bool always_on) + { + struct clk_pllv3 *pll; + const struct clk_ops *ops; +@@ -352,6 +447,7 @@ + } + pll->base = base; + pll->div_mask = div_mask; ++ pll->always_on = always_on; + + init.name = name; + init.ops = ops; +diff -Nur linux-3.14.36/arch/arm/mach-imx/common.h linux-openelec/arch/arm/mach-imx/common.h +--- linux-3.14.36/arch/arm/mach-imx/common.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/common.h 2015-05-06 12:05:43.000000000 -0500 +@@ -1,5 +1,5 @@ + /* +- * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. + */ + + /* +@@ -116,7 +116,6 @@ + void imx_set_cpu_jump(int cpu, void *jump_addr); + u32 imx_get_cpu_arg(int cpu); + void imx_set_cpu_arg(int cpu, u32 arg); +-void v7_cpu_resume(void); + #ifdef CONFIG_SMP + void v7_secondary_startup(void); + void imx_scu_map_io(void); +@@ -129,7 +128,7 @@ + #endif + void imx_src_init(void); + void imx_gpc_init(void); +-void imx_gpc_pre_suspend(void); ++void imx_gpc_pre_suspend(bool arm_power_off); + void imx_gpc_post_resume(void); + void imx_gpc_mask_all(void); + void imx_gpc_restore_all(void); +@@ -138,14 +137,28 @@ + void imx_anatop_init(void); + void imx_anatop_pre_suspend(void); + void imx_anatop_post_resume(void); ++void imx_anatop_pu_enable(bool enable); + int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); +-void imx6q_set_chicken_bit(void); ++void imx6q_set_cache_lpm_in_wait(bool enable); ++void imx6sl_set_wait_clk(bool enter); ++void imx6_enet_mac_init(const char *compatible); + + void imx_cpu_die(unsigned int cpu); + int imx_cpu_kill(unsigned int cpu); + ++#ifdef CONFIG_SUSPEND ++void v7_cpu_resume(void); ++void imx6_suspend(void __iomem *ocram_vbase); ++#else ++static inline void v7_cpu_resume(void) {} ++static inline void imx6_suspend(void __iomem *ocram_vbase) {} ++#endif ++ + void imx6q_pm_init(void); ++void imx6dl_pm_init(void); ++void imx6sl_pm_init(void); + void imx6q_pm_set_ccm_base(void __iomem *base); ++ + #ifdef CONFIG_PM + void imx5_pm_init(void); + #else +diff -Nur linux-3.14.36/arch/arm/mach-imx/cpuidle.h linux-openelec/arch/arm/mach-imx/cpuidle.h +--- linux-3.14.36/arch/arm/mach-imx/cpuidle.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/cpuidle.h 2015-05-06 12:05:43.000000000 -0500 +@@ -1,5 +1,5 @@ + /* +- * Copyright 2012 Freescale Semiconductor, Inc. ++ * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public +@@ -13,6 +13,7 @@ + #ifdef CONFIG_CPU_IDLE + extern int imx5_cpuidle_init(void); + extern int imx6q_cpuidle_init(void); ++extern int imx6sl_cpuidle_init(void); + #else + static inline int imx5_cpuidle_init(void) + { +@@ -22,4 +23,8 @@ + { + return 0; + } ++static inline int imx6sl_cpuidle_init(void) ++{ ++ return 0; ++} + #endif +diff -Nur linux-3.14.36/arch/arm/mach-imx/cpuidle-imx6q.c linux-openelec/arch/arm/mach-imx/cpuidle-imx6q.c +--- linux-3.14.36/arch/arm/mach-imx/cpuidle-imx6q.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/cpuidle-imx6q.c 2015-05-06 12:05:43.000000000 -0500 +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2012 Freescale Semiconductor, Inc. ++ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. + * + * 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 +@@ -68,8 +68,8 @@ + /* Need to enable SCU standby for entering WAIT modes */ + imx_scu_standby_enable(); + +- /* Set chicken bit to get a reliable WAIT mode support */ +- imx6q_set_chicken_bit(); ++ /* Set cache lpm bit for reliable WAIT mode support */ ++ imx6q_set_cache_lpm_in_wait(true); + + return cpuidle_register(&imx6q_cpuidle_driver, NULL); + } +diff -Nur linux-3.14.36/arch/arm/mach-imx/cpuidle-imx6sl.c linux-openelec/arch/arm/mach-imx/cpuidle-imx6sl.c +--- linux-3.14.36/arch/arm/mach-imx/cpuidle-imx6sl.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/cpuidle-imx6sl.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,149 @@ ++/* ++ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "common.h" ++#include "cpuidle.h" ++ ++extern u32 audio_bus_freq_mode; ++extern u32 ultra_low_bus_freq_mode; ++extern unsigned long reg_addrs[]; ++extern void imx6sl_low_power_wfi(void); ++ ++static void __iomem *iomux_base; ++static void *wfi_iram_base; ++ ++void (*imx6sl_wfi_in_iram_fn)(void *wfi_iram_base, ++ void *iomux_addr, void *regs_addr, u32 audio_mode) = NULL; ++ ++#define WFI_IN_IRAM_SIZE 0x1000 ++ ++static int imx6sl_enter_wait(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int index) ++{ ++ imx6q_set_lpm(WAIT_UNCLOCKED); ++#ifdef CONFIG_ARM_IMX6_CPUFREQ ++ if (ultra_low_bus_freq_mode || audio_bus_freq_mode) { ++ /* ++ * Flush the TLB, to ensure no TLB maintenance occurs ++ * when DDR is in self-refresh. ++ */ ++ local_flush_tlb_all(); ++ /* ++ * Run WFI code from IRAM. ++ * Drop the DDR freq to 1MHz and AHB to 3MHz ++ * Also float DDR IO pads. ++ */ ++ imx6sl_wfi_in_iram_fn(wfi_iram_base, iomux_base, reg_addrs, audio_bus_freq_mode); ++ } ++ else ++#endif ++ { ++ imx6sl_set_wait_clk(true); ++ cpu_do_idle(); ++ imx6sl_set_wait_clk(false); ++ } ++ imx6q_set_lpm(WAIT_CLOCKED); ++ ++ return index; ++} ++ ++static struct cpuidle_driver imx6sl_cpuidle_driver = { ++ .name = "imx6sl_cpuidle", ++ .owner = THIS_MODULE, ++ .states = { ++ /* WFI */ ++ ARM_CPUIDLE_WFI_STATE, ++ /* WAIT */ ++ { ++ .exit_latency = 50, ++ .target_residency = 75, ++ .flags = CPUIDLE_FLAG_TIME_VALID | ++ CPUIDLE_FLAG_TIMER_STOP, ++ .enter = imx6sl_enter_wait, ++ .name = "WAIT", ++ .desc = "Clock off", ++ }, ++ }, ++ .state_count = 2, ++ .safe_state_index = 0, ++}; ++ ++int __init imx6sl_cpuidle_init(void) ++{ ++ struct platform_device *ocram_dev; ++ unsigned int iram_paddr; ++ struct device_node *node; ++ struct gen_pool *iram_pool; ++ ++ node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-iomuxc"); ++ if (!node) { ++ pr_err("failed to find imx6sl-iomuxc device tree data!\n"); ++ return -EINVAL; ++ } ++ iomux_base = of_iomap(node, 0); ++ WARN(!iomux_base, "unable to map iomux registers\n"); ++ ++ node = NULL; ++ node = of_find_compatible_node(NULL, NULL, "mmio-sram"); ++ if (!node) { ++ pr_err("%s: failed to find ocram node\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ ocram_dev = of_find_device_by_node(node); ++ if (!ocram_dev) { ++ pr_err("failed to find ocram device!\n"); ++ return -EINVAL; ++ } ++ ++ iram_pool = dev_get_gen_pool(&ocram_dev->dev); ++ if (!iram_pool) { ++ pr_err("iram pool unavailable!\n"); ++ return -EINVAL; ++ } ++ /* ++ * Allocate IRAM memory when ARM executes WFI in ++ * ultra_low_power_mode. ++ */ ++ wfi_iram_base = (void *)gen_pool_alloc(iram_pool, ++ WFI_IN_IRAM_SIZE); ++ if (!wfi_iram_base) { ++ pr_err("Cannot alloc iram for wfi code!\n"); ++ return -ENOMEM; ++ } ++ ++ iram_paddr = gen_pool_virt_to_phys(iram_pool, ++ (unsigned long)wfi_iram_base); ++ /* ++ * Need to remap the area here since we want ++ * the memory region to be executable. ++ */ ++ wfi_iram_base = __arm_ioremap(iram_paddr, ++ WFI_IN_IRAM_SIZE, ++ MT_MEMORY_RWX_NONCACHED); ++ if (!wfi_iram_base) ++ pr_err("wfi_ram_base NOT remapped\n"); ++ ++ imx6sl_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base, ++ &imx6sl_low_power_wfi, WFI_IN_IRAM_SIZE); ++ ++ return cpuidle_register(&imx6sl_cpuidle_driver, NULL); ++} +diff -Nur linux-3.14.36/arch/arm/mach-imx/ddr3_freq_imx6.S linux-openelec/arch/arm/mach-imx/ddr3_freq_imx6.S +--- linux-3.14.36/arch/arm/mach-imx/ddr3_freq_imx6.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/ddr3_freq_imx6.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,893 @@ ++/* ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++ ++#define MMDC0_MDPDC 0x4 ++#define MMDC0_MDCF0 0x0c ++#define MMDC0_MDCF1 0x10 ++#define MMDC0_MDMISC 0x18 ++#define MMDC0_MDSCR 0x1c ++#define MMDC0_MAPSR 0x404 ++#define MMDC0_MADPCR0 0x410 ++#define MMDC0_MPZQHWCTRL 0x800 ++#define MMDC1_MPZQHWCTRL 0x4800 ++#define MMDC0_MPODTCTRL 0x818 ++#define MMDC1_MPODTCTRL 0x4818 ++#define MMDC0_MPDGCTRL0 0x83c ++#define MMDC1_MPDGCTRL0 0x483c ++#define MMDC0_MPMUR0 0x8b8 ++#define MMDC1_MPMUR0 0x48b8 ++ ++#define CCM_CBCDR 0x14 ++#define CCM_CBCMR 0x18 ++#define CCM_CSCMR1 0x1c ++#define CCM_CDHIPR 0x48 ++ ++#define L2_CACHE_SYNC 0x730 ++ ++ .align 3 ++ ++ .macro switch_to_528MHz ++ ++ /* check if periph_clk_sel is already set */ ++ ldr r0, [r6, #CCM_CBCDR] ++ and r0, r0, #(1 << 25) ++ cmp r0, #(1 << 25) ++ beq set_ahb_podf_before_switch ++ ++ /* change periph_clk to be sourced from pll3_clk. */ ++ ldr r0, [r6, #CCM_CBCMR] ++ bic r0, r0, #(3 << 12) ++ str r0, [r6, #CCM_CBCMR] ++ ++ ldr r0, [r6, #CCM_CBCDR] ++ bic r0, r0, #(0x38 << 20) ++ str r0, [r6, #CCM_CBCDR] ++ ++ /* ++ * set the AHB dividers before the switch, ++ * don't change AXI clock divider, ++ * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4, ++ */ ++ ldr r0, [r6, #CCM_CBCDR] ++ ldr r2, =0x3f1f00 ++ bic r0, r0, r2 ++ orr r0, r0, #0xd00 ++ orr r0, r0, #(1 << 16) ++ str r0, [r6, #CCM_CBCDR] ++ ++wait_div_update528: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne wait_div_update528 ++ ++ /* now switch periph_clk to pll3_main_clk. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ orr r0, r0, #(1 << 25) ++ str r0, [r6, #CCM_CBCDR] ++ ++periph_clk_switch3: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne periph_clk_switch3 ++ ++ b switch_pre_periph_clk_528 ++ ++set_ahb_podf_before_switch: ++ /* ++ * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4, ++ */ ++ ldr r0, [r6, #CCM_CBCDR] ++ ldr r2, =0x3f1f00 ++ bic r0, r0, r2 ++ orr r0, r0, #0xd00 ++ orr r0, r0, #(1 << 16) ++ str r0, [r6, #CCM_CBCDR] ++ ++wait_div_update528_1: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne wait_div_update528_1 ++ ++switch_pre_periph_clk_528: ++ ++ /* now switch pre_periph_clk to PLL2_528MHz. */ ++ ldr r0, [r6, #CCM_CBCMR] ++ bic r0, r0, #(0xc << 16) ++ str r0, [r6, #CCM_CBCMR] ++ ++ /* now switch periph_clk back. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ bic r0, r0, #(1 << 25) ++ str r0, [r6, #CCM_CBCDR] ++ ++periph_clk_switch4: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne periph_clk_switch4 ++ ++ .endm ++ ++ .macro switch_to_400MHz ++ ++ /* check if periph_clk_sel is already set. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ and r0, r0, #(1 << 25) ++ cmp r0, #(1 << 25) ++ beq set_ahb_podf_before_switch1 ++ ++ /* change periph_clk to be sourced from pll3_clk. */ ++ ldr r0, [r6, #CCM_CBCMR] ++ bic r0, r0, #(3 << 12) ++ str r0, [r6, #CCM_CBCMR] ++ ++ ldr r0, [r6, #CCM_CBCDR] ++ bic r0, r0, #(0x38 << 24) ++ str r0, [r6, #CCM_CBCDR] ++ ++ /* now switch periph_clk to pll3_main_clk. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ orr r0, r0, #(1 << 25) ++ str r0, [r6, #CCM_CBCDR] ++ ++periph_clk_switch5: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne periph_clk_switch5 ++ ++ b switch_pre_periph_clk_400 ++ ++set_ahb_podf_before_switch1: ++ /* ++ * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4, ++ */ ++ ldr r0, [r6, #CCM_CBCDR] ++ ldr r2, =0x3f1f00 ++ bic r0, r0, r2 ++ orr r0, r0, #(0x9 << 8) ++ orr r0, r0, #(1 << 16) ++ str r0, [r6, #CCM_CBCDR] ++ ++wait_div_update400_1: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne wait_div_update400_1 ++ ++switch_pre_periph_clk_400: ++ ++ /* now switch pre_periph_clk to PFD_400MHz. */ ++ ldr r0, [r6, #CCM_CBCMR] ++ bic r0, r0, #(0xc << 16) ++ orr r0, r0, #(0x4 << 16) ++ str r0, [r6, #CCM_CBCMR] ++ ++ /* now switch periph_clk back. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ bic r0, r0, #(1 << 25) ++ str r0, [r6, #CCM_CBCDR] ++ ++periph_clk_switch6: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne periph_clk_switch6 ++ ++ /* ++ * change AHB divider so that we are at 400/3=133MHz. ++ * don't change AXI clock divider. ++ * set the MMDC_DIV=1, AXI_DIV=2, AHB_DIV=3, ++ */ ++ ldr r0, [r6, #CCM_CBCDR] ++ ldr r2, =0x3f1f00 ++ bic r0, r0, r2 ++ orr r0, r0, #(0x9 << 8) ++ orr r0, r0, #(1 << 16) ++ str r0, [r6, #CCM_CBCDR] ++ ++wait_div_update400_2: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne wait_div_update400_2 ++ ++ .endm ++ ++ .macro switch_to_50MHz ++ ++ /* check if periph_clk_sel is already set. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ and r0, r0, #(1 << 25) ++ cmp r0, #(1 << 25) ++ beq switch_pre_periph_clk_50 ++ ++ /* ++ * set the periph_clk to be sourced from PLL2_PFD_200M ++ * change periph_clk to be sourced from pll3_clk. ++ * ensure PLL3 is the source and set the divider to 1. ++ */ ++ ldr r0, [r6, #CCM_CBCMR] ++ bic r0, r0, #(0x3 << 12) ++ str r0, [r6, #CCM_CBCMR] ++ ++ ldr r0, [r6, #CCM_CBCDR] ++ bic r0, r0, #(0x38 << 24) ++ str r0, [r6, #CCM_CBCDR] ++ ++ /* now switch periph_clk to pll3_main_clk. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ orr r0, r0, #(1 << 25) ++ str r0, [r6, #CCM_CBCDR] ++ ++periph_clk_switch_50: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne periph_clk_switch_50 ++ ++switch_pre_periph_clk_50: ++ ++ /* now switch pre_periph_clk to PFD_200MHz. */ ++ ldr r0, [r6, #CCM_CBCMR] ++ orr r0, r0, #(0xc << 16) ++ str r0, [r6, #CCM_CBCMR] ++ ++ /* ++ * set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=8, ++ */ ++ ldr r0, [r6, #CCM_CBCDR] ++ ldr r2, =0x3f1f00 ++ bic r0, r0, r2 ++ orr r0, r0, #(0x18 << 16) ++ orr r0, r0, #(0x3 << 16) ++ ++ /* ++ * if changing AHB divider remember to change ++ * the IPGPER divider too below. ++ */ ++ orr r0, r0, #0x1d00 ++ str r0, [r6, #CCM_CBCDR] ++ ++wait_div_update_50: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne wait_div_update_50 ++ ++ /* now switch periph_clk back. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ bic r0, r0, #(1 << 25) ++ str r0, [r6, #CCM_CBCDR] ++ ++periph_clk_switch2: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne periph_clk_switch2 ++ ++ .endm ++ ++ .macro switch_to_24MHz ++ /* ++ * change the freq now try setting DDR to 24MHz. ++ * source it from the periph_clk2 ensure the ++ * periph_clk2 is sourced from 24MHz and the ++ * divider is 1. ++ */ ++ ++ ldr r0, [r6, #CCM_CBCMR] ++ bic r0, r0, #(0x3 << 12) ++ orr r0, r0, #(1 << 12) ++ str r0, [r6, #CCM_CBCMR] ++ ++ ldr r0, [r6, #CCM_CBCDR] ++ bic r0, r0, #(0x38 << 24) ++ str r0, [r6, #CCM_CBCDR] ++ ++ /* now switch periph_clk to 24MHz. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ orr r0, r0, #(1 << 25) ++ str r0, [r6, #CCM_CBCDR] ++ ++periph_clk_switch1: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne periph_clk_switch1 ++ ++ /* change all the dividers to 1. */ ++ ldr r0, [r6, #CCM_CBCDR] ++ ldr r2, =0x3f1f00 ++ bic r0, r0, r2 ++ orr r0, r0, #(1 << 8) ++ str r0, [r6, #CCM_CBCDR] ++ ++ /* Wait for the divider to change. */ ++wait_div_update: ++ ldr r0, [r6, #CCM_CDHIPR] ++ cmp r0, #0 ++ bne wait_div_update ++ ++ .endm ++ ++/* ++ * mx6_ddr3_freq_change ++ * ++ * idle the processor (eg, wait for interrupt). ++ * make sure DDR is in self-refresh. ++ * IRQs are already disabled. ++ */ ++ENTRY(mx6_ddr3_freq_change) ++ ++ stmfd sp!, {r4-r12} ++ ++ /* ++ * r5 -> mmdc_base ++ * r6 -> ccm_base ++ * r7 -> iomux_base ++ * r12 -> l2_base ++ */ ++ mov r4, r0 ++ mov r8, r1 ++ mov r9, r2 ++ mov r11, r3 ++ ++ /* ++ * Get the addresses of the registers. ++ * They are last few entries in the ++ * ddr_settings parameter. ++ * The first entry contains the count, ++ * and each entry is 2 words. ++ */ ++ ldr r0, [r1] ++ add r0, r0, #1 ++ lsl r0, r0, #3 ++ add r1, r0, r1 ++ /* mmdc_base. */ ++ ldr r5, [r1] ++ add r1, #8 ++ /* ccm_base */ ++ ldr r6, [r1] ++ add r1, #8 ++ /*iomux_base */ ++ ldr r7, [r1] ++ add r1, #8 ++ /*l2_base */ ++ ldr r12, [r1] ++ ++ddr_freq_change: ++ /* ++ * make sure no TLB miss will occur when ++ * the DDR is in self refresh. invalidate ++ * TLB single entry to ensure that the ++ * address is not already in the TLB. ++ */ ++ ++ adr r10, ddr_freq_change ++ ++ ldr r2, [r6] ++ ldr r2, [r5] ++ ldr r2, [r7] ++ ldr r2, [r8] ++ ldr r2, [r10] ++ ldr r2, [r11] ++ ldr r2, [r12] ++ ++#ifdef CONFIG_CACHE_L2X0 ++ /* ++ * Make sure the L2 buffers are drained. ++ * Sync operation on L2 drains the buffers. ++ */ ++ mov r1, #0x0 ++ str r1, [r12, #L2_CACHE_SYNC] ++#endif ++ ++ /* disable automatic power saving. */ ++ ldr r0, [r5, #MMDC0_MAPSR] ++ orr r0, r0, #0x01 ++ str r0, [r5, #MMDC0_MAPSR] ++ ++ /* disable MMDC power down timer. */ ++ ldr r0, [r5, #MMDC0_MDPDC] ++ bic r0, r0, #(0xff << 8) ++ str r0, [r5, #MMDC0_MDPDC] ++ ++ /* delay for a while */ ++ ldr r1, =4 ++delay1: ++ ldr r2, =0 ++cont1: ++ ldr r0, [r5, r2] ++ add r2, r2, #4 ++ cmp r2, #16 ++ bne cont1 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt delay1 ++ ++ /* set CON_REG */ ++ ldr r0, =0x8000 ++ str r0, [r5, #MMDC0_MDSCR] ++poll_conreq_set_1: ++ ldr r0, [r5, #MMDC0_MDSCR] ++ and r0, r0, #(0x4 << 12) ++ cmp r0, #(0x4 << 12) ++ bne poll_conreq_set_1 ++ ++ ldr r0, =0x00008010 ++ str r0, [r5, #MMDC0_MDSCR] ++ ldr r0, =0x00008018 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ /* ++ * if requested frequency is greater than ++ * 300MHz go to DLL on mode. ++ */ ++ ldr r1, =300000000 ++ cmp r4, r1 ++ bge dll_on_mode ++ ++dll_off_mode: ++ ++ /* if DLL is currently on, turn it off. */ ++ cmp r9, #1 ++ beq continue_dll_off_1 ++ ++ ldr r0, =0x00018031 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x00018039 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r1, =10 ++delay1a: ++ ldr r2, =0 ++cont1a: ++ ldr r0, [r5, r2] ++ add r2, r2, #4 ++ cmp r2, #16 ++ bne cont1a ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt delay1a ++ ++continue_dll_off_1: ++ /* set DVFS - enter self refresh mode */ ++ ldr r0, [r5, #MMDC0_MAPSR] ++ orr r0, r0, #(1 << 21) ++ str r0, [r5, #MMDC0_MAPSR] ++ ++ /* de-assert con_req */ ++ mov r0, #0x0 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++poll_dvfs_set_1: ++ ldr r0, [r5, #MMDC0_MAPSR] ++ and r0, r0, #(1 << 25) ++ cmp r0, #(1 << 25) ++ bne poll_dvfs_set_1 ++ ++ ldr r1, =24000000 ++ cmp r4, r1 ++ beq switch_freq_24 ++ ++ switch_to_50MHz ++ b continue_dll_off_2 ++ ++switch_freq_24: ++ switch_to_24MHz ++ ++continue_dll_off_2: ++ ++ /* set SBS - block ddr accesses */ ++ ldr r0, [r5, #MMDC0_MADPCR0] ++ orr r0, r0, #(1 << 8) ++ str r0, [r5, #MMDC0_MADPCR0] ++ ++ /* clear DVFS - exit from self refresh mode */ ++ ldr r0, [r5, #MMDC0_MAPSR] ++ bic r0, r0, #(1 << 21) ++ str r0, [r5, #MMDC0_MAPSR] ++ ++poll_dvfs_clear_1: ++ ldr r0, [r5, #MMDC0_MAPSR] ++ and r0, r0, #(1 << 25) ++ cmp r0, #(1 << 25) ++ beq poll_dvfs_clear_1 ++ ++ /* if DLL was previously on, continue DLL off routine. */ ++ cmp r9, #1 ++ beq continue_dll_off_3 ++ ++ ldr r0, =0x00018031 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x00018039 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x08208030 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x08208038 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x00088032 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x0008803A ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ /* delay for a while. */ ++ ldr r1, =4 ++delay_1: ++ ldr r2, =0 ++cont_1: ++ ldr r0, [r5, r2] ++ add r2, r2, #4 ++ cmp r2, #16 ++ bne cont_1 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt delay_1 ++ ++ ldr r0, [r5, #MMDC0_MDCF0] ++ bic r0, r0, #0xf ++ orr r0, r0, #0x3 ++ str r0, [r5, #MMDC0_MDCF0] ++ ++ ldr r0, [r5, #MMDC0_MDCF1] ++ bic r0, r0, #0x7 ++ orr r0, r0, #0x4 ++ str r0, [r5, #MMDC0_MDCF1] ++ ++ ldr r0, =0x00091680 ++ str r0, [r5, #MMDC0_MDMISC] ++ ++ /* enable dqs pull down in the IOMUX. */ ++ ldr r1, [r11] ++ add r11, r11, #8 ++ ldr r2, =0x3028 ++update_iomux: ++ ldr r0, [r11, #0x0] ++ ldr r3, [r7, r0] ++ bic r3, r3, r2 ++ orr r3, r3, #(0x3 << 12) ++ orr r3, r3, #0x28 ++ str r3, [r7, r0] ++ add r11, r11, #8 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt update_iomux ++ ++ /* ODT disabled. */ ++ ldr r0, =0x0 ++ ldr r2, =MMDC0_MPODTCTRL ++ str r0, [r5, r2] ++ ldr r2, =MMDC1_MPODTCTRL ++ str r0, [r5, r2] ++ ++ /* DQS gating disabled. */ ++ ldr r2, =MMDC0_MPDGCTRL0 ++ ldr r0, [r5, r2] ++ orr r0, r0, #(1 << 29) ++ str r0, [r5, r2] ++ ++ ldr r2, =MMDC1_MPDGCTRL0 ++ ldr r0, [r5, r2] ++ orr r0, r0, #(0x1 << 29) ++ str r0, [r5, r2] ++ ++ /* MMDC0_MAPSR adopt power down enable. */ ++ ldr r0, [r5, #MMDC0_MAPSR] ++ bic r0, r0, #0x01 ++ str r0, [r5, #MMDC0_MAPSR] ++ ++ /* frc_msr + mu bypass */ ++ ldr r0, =0x00000060 ++ str r0, [r5, #MMDC0_MPMUR0] ++ ldr r2, =MMDC1_MPMUR0 ++ str r0, [r5, r2] ++ ldr r0, =0x00000460 ++ str r0, [r5, #MMDC0_MPMUR0] ++ ldr r2, =MMDC1_MPMUR0 ++ str r0, [r5, r2] ++ ldr r0, =0x00000c60 ++ str r0, [r5, #MMDC0_MPMUR0] ++ ldr r2, =MMDC1_MPMUR0 ++ str r0, [r5, r2] ++ ++continue_dll_off_3: ++ /* clear SBS - unblock accesses to DDR. */ ++ ldr r0, [r5, #MMDC0_MADPCR0] ++ bic r0, r0, #(0x1 << 8) ++ str r0, [r5, #MMDC0_MADPCR0] ++ ++ mov r0, #0x0 ++ str r0, [r5, #MMDC0_MDSCR] ++poll_conreq_clear_1: ++ ldr r0, [r5, #MMDC0_MDSCR] ++ and r0, r0, #(0x4 << 12) ++ cmp r0, #(0x4 << 12) ++ beq poll_conreq_clear_1 ++ ++ b done ++ ++dll_on_mode: ++ /* assert DVFS - enter self refresh mode. */ ++ ldr r0, [r5, #MMDC0_MAPSR] ++ orr r0, r0, #(1 << 21) ++ str r0, [r5, #MMDC0_MAPSR] ++ ++ /* de-assert CON_REQ. */ ++ mov r0, #0x0 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ /* poll DVFS ack. */ ++poll_dvfs_set_2: ++ ldr r0, [r5, #MMDC0_MAPSR] ++ and r0, r0, #(1 << 25) ++ cmp r0, #(1 << 25) ++ bne poll_dvfs_set_2 ++ ++ ldr r1, =528000000 ++ cmp r4, r1 ++ beq switch_freq_528 ++ ++ switch_to_400MHz ++ ++ b continue_dll_on ++ ++switch_freq_528: ++ switch_to_528MHz ++ ++continue_dll_on: ++ ++ /* set SBS step-by-step mode. */ ++ ldr r0, [r5, #MMDC0_MADPCR0] ++ orr r0, r0, #( 1 << 8) ++ str r0, [r5, #MMDC0_MADPCR0] ++ ++ /* clear DVFS - exit self refresh mode. */ ++ ldr r0, [r5, #MMDC0_MAPSR] ++ bic r0, r0, #(1 << 21) ++ str r0, [r5, #MMDC0_MAPSR] ++ ++poll_dvfs_clear_2: ++ ldr r0, [r5, #MMDC0_MAPSR] ++ and r0, r0, #(1 << 25) ++ cmp r0, #(1 << 25) ++ beq poll_dvfs_clear_2 ++ ++ /* if DLL is currently off, turn it back on. */ ++ cmp r9, #0 ++ beq update_calibration_only ++ ++ ldr r0, =0xa5390003 ++ str r0, [r5, #MMDC0_MPZQHWCTRL] ++ ldr r2, =MMDC1_MPZQHWCTRL ++ str r0, [r5, r2] ++ ++ /* enable DQS gating. */ ++ ldr r2, =MMDC0_MPDGCTRL0 ++ ldr r0, [r5, r2] ++ bic r0, r0, #(1 << 29) ++ str r0, [r5, r2] ++ ++ ldr r2, =MMDC1_MPDGCTRL0 ++ ldr r0, [r5, r2] ++ bic r0, r0, #(1 << 29) ++ str r0, [r5, r2] ++ ++ /* force measure. */ ++ ldr r0, =0x00000800 ++ str r0, [r5, #MMDC0_MPMUR0] ++ ldr r2, =MMDC1_MPMUR0 ++ str r0, [r5, r2] ++ ++ /* delay for while. */ ++ ldr r1, =4 ++delay5: ++ ldr r2, =0 ++cont5: ++ ldr r0, [r5, r2] ++ add r2, r2, #4 ++ cmp r2, #16 ++ bne cont5 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt delay5 ++ ++ /* disable dqs pull down in the IOMUX. */ ++ ldr r1, [r11] ++ add r11, r11, #8 ++update_iomux1: ++ ldr r0, [r11, #0x0] ++ ldr r3, [r11, #0x4] ++ str r3, [r7, r0] ++ add r11, r11, #8 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt update_iomux1 ++ ++ /* config MMDC timings to 528MHz. */ ++ ldr r9, [r8] ++ add r8, r8, #8 ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ ++ /* update MISC register: WALAT, RALAT */ ++ ldr r0, =0x00081740 ++ str r0, [r5, #MMDC0_MDMISC] ++ ++ /* configure ddr devices to dll on, odt. */ ++ ldr r0, =0x00028031 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x00028039 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ /* delay for while. */ ++ ldr r1, =4 ++delay7: ++ ldr r2, =0 ++cont7: ++ ldr r0, [r5, r2] ++ add r2, r2, #4 ++ cmp r2, #16 ++ bne cont7 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt delay7 ++ ++ /* reset dll. */ ++ ldr r0, =0x09208030 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x09208038 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ /* delay for while. */ ++ ldr r1, =100 ++delay8: ++ ldr r2, =0 ++cont8: ++ ldr r0, [r5, r2] ++ add r2, r2, #4 ++ cmp r2, #16 ++ bne cont8 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt delay8 ++ ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ ++ ldr r0, =0x00428031 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x00428039 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ ++ /* issue a zq command. */ ++ ldr r0, =0x04008040 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ ldr r0, =0x04008048 ++ str r0, [r5, #MMDC0_MDSCR] ++ ++ /* MMDC ODT enable. */ ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ ++ ldr r2, =0x4818 ++ str r0, [r5, r2] ++ ++ /* delay for while. */ ++ ldr r1, =40 ++delay15: ++ ldr r2, =0 ++cont15: ++ ldr r0, [r5, r2] ++ add r2, r2, #4 ++ cmp r2, #16 ++ bne cont15 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt delay15 ++ ++ /* MMDC0_MAPSR adopt power down enable. */ ++ ldr r0, [r5, #MMDC0_MAPSR] ++ bic r0, r0, #0x01 ++ str r0, [r5, #MMDC0_MAPSR] ++ ++ /* enable MMDC power down timer. */ ++ ldr r0, [r5, #MMDC0_MDPDC] ++ orr r0, r0, #(0x55 << 8) ++ str r0, [r5, #MMDC0_MDPDC] ++ ++ b update_calibration ++ ++update_calibration_only: ++ ldr r1, [r8] ++ sub r1, r1, #7 ++ add r8, r8, #64 ++ b update_calib ++ ++update_calibration: ++ /* write the new calibration values. */ ++ mov r1, r9 ++ sub r1, r1, #7 ++ ++update_calib: ++ ldr r0, [r8, #0x0] ++ ldr r3, [r8, #0x4] ++ str r3, [r5, r0] ++ add r8, r8, #8 ++ sub r1, r1, #1 ++ cmp r1, #0 ++ bgt update_calib ++ ++ /* perform a force measurement. */ ++ ldr r0, =0x800 ++ str r0, [r5, #MMDC0_MPMUR0] ++ ldr r2, =MMDC1_MPMUR0 ++ str r0, [r5, r2] ++ ++ /* clear SBS - unblock DDR accesses. */ ++ ldr r0, [r5, #MMDC0_MADPCR0] ++ bic r0, r0, #(1 << 8) ++ str r0, [r5, #MMDC0_MADPCR0] ++ ++ mov r0, #0x0 ++ str r0, [r5, #MMDC0_MDSCR] ++poll_conreq_clear_2: ++ ldr r0, [r5, #MMDC0_MDSCR] ++ and r0, r0, #(0x4 << 12) ++ cmp r0, #(0x4 << 12) ++ beq poll_conreq_clear_2 ++ ++done: ++ /* restore registers */ ++ ++ ldmfd sp!, {r4-r12} ++ mov pc, lr ++ ++ .type mx6_do_ddr3_freq_change, #object ++ENTRY(mx6_do_ddr_freq_change) ++ .word mx6_ddr3_freq_change ++ .size mx6_ddr3_freq_change, . - mx6_ddr3_freq_change +diff -Nur linux-3.14.36/arch/arm/mach-imx/gpc.c linux-openelec/arch/arm/mach-imx/gpc.c +--- linux-3.14.36/arch/arm/mach-imx/gpc.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/gpc.c 2015-05-06 12:05:43.000000000 -0500 +@@ -10,30 +10,69 @@ + * http://www.gnu.org/copyleft/gpl.html + */ + ++#include ++#include + #include + #include ++#include + #include + #include + #include ++#include + #include ++#include ++#include ++#include + #include "common.h" ++#include "hardware.h" + + #define GPC_IMR1 0x008 + #define GPC_PGC_CPU_PDN 0x2a0 ++#define GPC_PGC_GPU_PDN 0x260 ++#define GPC_PGC_GPU_PUPSCR 0x264 ++#define GPC_PGC_GPU_PDNSCR 0x268 ++#define GPC_PGC_GPU_SW_SHIFT 0 ++#define GPC_PGC_GPU_SW_MASK 0x3f ++#define GPC_PGC_GPU_SW2ISO_SHIFT 8 ++#define GPC_PGC_GPU_SW2ISO_MASK 0x3f ++#define GPC_PGC_CPU_PUPSCR 0x2a4 ++#define GPC_PGC_CPU_PDNSCR 0x2a8 ++#define GPC_PGC_CPU_SW_SHIFT 0 ++#define GPC_PGC_CPU_SW_MASK 0x3f ++#define GPC_PGC_CPU_SW2ISO_SHIFT 8 ++#define GPC_PGC_CPU_SW2ISO_MASK 0x3f ++#define GPC_CNTR 0x0 ++#define GPC_CNTR_PU_UP_REQ_SHIFT 0x1 ++#define GPC_CNTR_PU_DOWN_REQ_SHIFT 0x0 + + #define IMR_NUM 4 + + static void __iomem *gpc_base; + static u32 gpc_wake_irqs[IMR_NUM]; + static u32 gpc_saved_imrs[IMR_NUM]; ++static struct clk *gpu3d_clk, *gpu3d_shader_clk, *gpu2d_clk, *gpu2d_axi_clk; ++static struct clk *openvg_axi_clk, *vpu_clk, *ipg_clk; ++static struct device *gpc_dev; ++struct regulator *pu_reg; ++struct notifier_block nb; ++static struct regulator_dev *pu_dummy_regulator_rdev; ++static struct regulator_init_data pu_dummy_initdata = { ++ .constraints = { ++ .max_uV = 1450000, /* allign with real max of anatop */ ++ .valid_ops_mask = REGULATOR_CHANGE_STATUS | ++ REGULATOR_CHANGE_VOLTAGE, ++ }, ++}; ++static int pu_dummy_enable; + +-void imx_gpc_pre_suspend(void) ++void imx_gpc_pre_suspend(bool arm_power_off) + { + void __iomem *reg_imr1 = gpc_base + GPC_IMR1; + int i; + +- /* Tell GPC to power off ARM core when suspend */ +- writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_PDN); ++ if (arm_power_off) ++ /* Tell GPC to power off ARM core when suspend */ ++ writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_PDN); + + for (i = 0; i < IMR_NUM; i++) { + gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); +@@ -120,10 +159,119 @@ + writel_relaxed(val, reg); + } + ++static void imx_pu_clk(bool enable) ++{ ++ if (enable) { ++ if (cpu_is_imx6sl()) { ++ clk_prepare_enable(gpu2d_clk); ++ clk_prepare_enable(openvg_axi_clk); ++ } else { ++ clk_prepare_enable(vpu_clk); ++ clk_prepare_enable(gpu3d_clk); ++ clk_prepare_enable(gpu3d_shader_clk); ++ clk_prepare_enable(gpu2d_clk); ++ clk_prepare_enable(gpu2d_axi_clk); ++ clk_prepare_enable(openvg_axi_clk); ++ } ++ } else { ++ if (cpu_is_imx6sl()) { ++ clk_disable_unprepare(gpu2d_clk); ++ clk_disable_unprepare(openvg_axi_clk); ++ } else { ++ clk_disable_unprepare(openvg_axi_clk); ++ clk_disable_unprepare(gpu2d_axi_clk); ++ clk_disable_unprepare(gpu2d_clk); ++ clk_disable_unprepare(gpu3d_shader_clk); ++ clk_disable_unprepare(gpu3d_clk); ++ clk_disable_unprepare(vpu_clk); ++ } ++ } ++} ++ ++static void imx_gpc_pu_enable(bool enable) ++{ ++ u32 rate, delay_us; ++ u32 gpu_pupscr_sw2iso, gpu_pdnscr_iso2sw; ++ u32 gpu_pupscr_sw, gpu_pdnscr_iso; ++ ++ /* get ipg clk rate for PGC delay */ ++ rate = clk_get_rate(ipg_clk); ++ ++ if (enable) { ++ imx_anatop_pu_enable(true); ++ /* ++ * need to add necessary delay between powering up PU LDO and ++ * disabling PU isolation in PGC, the counter of PU isolation ++ * is based on ipg clk. ++ */ ++ gpu_pupscr_sw2iso = (readl_relaxed(gpc_base + ++ GPC_PGC_GPU_PUPSCR) >> GPC_PGC_GPU_SW2ISO_SHIFT) ++ & GPC_PGC_GPU_SW2ISO_MASK; ++ gpu_pupscr_sw = (readl_relaxed(gpc_base + ++ GPC_PGC_GPU_PUPSCR) >> GPC_PGC_GPU_SW_SHIFT) ++ & GPC_PGC_GPU_SW_MASK; ++ delay_us = (gpu_pupscr_sw2iso + gpu_pupscr_sw) * 1000000 ++ / rate + 1; ++ udelay(delay_us); ++ ++ imx_pu_clk(true); ++ writel_relaxed(1, gpc_base + GPC_PGC_GPU_PDN); ++ writel_relaxed(1 << GPC_CNTR_PU_UP_REQ_SHIFT, ++ gpc_base + GPC_CNTR); ++ while (readl_relaxed(gpc_base + GPC_CNTR) & ++ (1 << GPC_CNTR_PU_UP_REQ_SHIFT)) ++ ; ++ imx_pu_clk(false); ++ } else { ++ writel_relaxed(1, gpc_base + GPC_PGC_GPU_PDN); ++ writel_relaxed(1 << GPC_CNTR_PU_DOWN_REQ_SHIFT, ++ gpc_base + GPC_CNTR); ++ while (readl_relaxed(gpc_base + GPC_CNTR) & ++ (1 << GPC_CNTR_PU_DOWN_REQ_SHIFT)) ++ ; ++ /* ++ * need to add necessary delay between enabling PU isolation ++ * in PGC and powering down PU LDO , the counter of PU isolation ++ * is based on ipg clk. ++ */ ++ gpu_pdnscr_iso2sw = (readl_relaxed(gpc_base + ++ GPC_PGC_GPU_PDNSCR) >> GPC_PGC_GPU_SW2ISO_SHIFT) ++ & GPC_PGC_GPU_SW2ISO_MASK; ++ gpu_pdnscr_iso = (readl_relaxed(gpc_base + ++ GPC_PGC_GPU_PDNSCR) >> GPC_PGC_GPU_SW_SHIFT) ++ & GPC_PGC_GPU_SW_MASK; ++ delay_us = (gpu_pdnscr_iso2sw + gpu_pdnscr_iso) * 1000000 ++ / rate + 1; ++ udelay(delay_us); ++ imx_anatop_pu_enable(false); ++ } ++} ++ ++static int imx_gpc_regulator_notify(struct notifier_block *nb, ++ unsigned long event, ++ void *ignored) ++{ ++ switch (event) { ++ case REGULATOR_EVENT_PRE_DISABLE: ++ imx_gpc_pu_enable(false); ++ break; ++ case REGULATOR_EVENT_ENABLE: ++ imx_gpc_pu_enable(true); ++ break; ++ default: ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++ + void __init imx_gpc_init(void) + { + struct device_node *np; + int i; ++ u32 val; ++ u32 cpu_pupscr_sw2iso, cpu_pupscr_sw; ++ u32 cpu_pdnscr_iso2sw, cpu_pdnscr_iso; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); + gpc_base = of_iomap(np, 0); +@@ -137,4 +285,190 @@ + gic_arch_extn.irq_mask = imx_gpc_irq_mask; + gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; + gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; ++ ++ /* ++ * If there are CPU isolation timing settings in dts, ++ * update them according to dts, otherwise, keep them ++ * with default value in registers. ++ */ ++ cpu_pupscr_sw2iso = cpu_pupscr_sw = ++ cpu_pdnscr_iso2sw = cpu_pdnscr_iso = 0; ++ ++ /* Read CPU isolation setting for GPC */ ++ of_property_read_u32(np, "fsl,cpu_pupscr_sw2iso", &cpu_pupscr_sw2iso); ++ of_property_read_u32(np, "fsl,cpu_pupscr_sw", &cpu_pupscr_sw); ++ of_property_read_u32(np, "fsl,cpu_pdnscr_iso2sw", &cpu_pdnscr_iso2sw); ++ of_property_read_u32(np, "fsl,cpu_pdnscr_iso", &cpu_pdnscr_iso); ++ ++ /* Update CPU PUPSCR timing if it is defined in dts */ ++ val = readl_relaxed(gpc_base + GPC_PGC_CPU_PUPSCR); ++ if (cpu_pupscr_sw2iso) ++ val &= ~(GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT); ++ if (cpu_pupscr_sw) ++ val &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT); ++ val |= cpu_pupscr_sw2iso << GPC_PGC_CPU_SW2ISO_SHIFT; ++ val |= cpu_pupscr_sw << GPC_PGC_CPU_SW_SHIFT; ++ writel_relaxed(val, gpc_base + GPC_PGC_CPU_PUPSCR); ++ ++ /* Update CPU PDNSCR timing if it is defined in dts */ ++ val = readl_relaxed(gpc_base + GPC_PGC_CPU_PDNSCR); ++ if (cpu_pdnscr_iso2sw) ++ val &= ~(GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT); ++ if (cpu_pdnscr_iso) ++ val &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT); ++ val |= cpu_pdnscr_iso2sw << GPC_PGC_CPU_SW2ISO_SHIFT; ++ val |= cpu_pdnscr_iso << GPC_PGC_CPU_SW_SHIFT; ++ writel_relaxed(val, gpc_base + GPC_PGC_CPU_PDNSCR); ++} ++ ++static int imx_pureg_set_voltage(struct regulator_dev *reg, int min_uV, ++ int max_uV, unsigned *selector) ++{ ++ return 0; ++} ++ ++static int imx_pureg_enable(struct regulator_dev *rdev) ++{ ++ pu_dummy_enable = 1; ++ ++ return 0; ++} ++ ++static int imx_pureg_disable(struct regulator_dev *rdev) ++{ ++ pu_dummy_enable = 0; ++ ++ return 0; + } ++ ++static int imx_pureg_is_enable(struct regulator_dev *rdev) ++{ ++ return pu_dummy_enable; ++} ++ ++static int imx_pureg_list_voltage(struct regulator_dev *rdev, ++ unsigned int selector) ++{ ++ return 0; ++} ++ ++static struct regulator_ops pu_dummy_ops = { ++ .set_voltage = imx_pureg_set_voltage, ++ .enable = imx_pureg_enable, ++ .disable = imx_pureg_disable, ++ .is_enabled = imx_pureg_is_enable, ++ .list_voltage = imx_pureg_list_voltage, ++}; ++ ++static struct regulator_desc pu_dummy_desc = { ++ .name = "pureg-dummy", ++ .id = -1, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++ .ops = &pu_dummy_ops, ++}; ++ ++static int pu_dummy_probe(struct platform_device *pdev) ++{ ++ struct regulator_config config = { }; ++ int ret; ++ ++ config.dev = &pdev->dev; ++ config.init_data = &pu_dummy_initdata; ++ config.of_node = pdev->dev.of_node; ++ ++ pu_dummy_regulator_rdev = regulator_register(&pu_dummy_desc, &config); ++ if (IS_ERR(pu_dummy_regulator_rdev)) { ++ ret = PTR_ERR(pu_dummy_regulator_rdev); ++ dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id imx_pudummy_ids[] = { ++ { .compatible = "fsl,imx6-dummy-pureg" }, ++}; ++MODULE_DEVICE_TABLE(of, imx_pudummy_ids); ++ ++static struct platform_driver pu_dummy_driver = { ++ .probe = pu_dummy_probe, ++ .driver = { ++ .name = "pu-dummy", ++ .owner = THIS_MODULE, ++ .of_match_table = imx_pudummy_ids, ++ }, ++}; ++ ++static int imx_gpc_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ gpc_dev = &pdev->dev; ++ ++ pu_reg = devm_regulator_get(gpc_dev, "pu"); ++ if (IS_ERR(pu_reg)) { ++ ret = PTR_ERR(pu_reg); ++ dev_info(gpc_dev, "pu regulator not ready.\n"); ++ return ret; ++ } ++ nb.notifier_call = &imx_gpc_regulator_notify; ++ ++ /* Get gpu&vpu clk for power up PU by GPC */ ++ if (cpu_is_imx6sl()) { ++ gpu2d_clk = devm_clk_get(gpc_dev, "gpu2d_podf"); ++ openvg_axi_clk = devm_clk_get(gpc_dev, "gpu2d_ovg"); ++ ipg_clk = devm_clk_get(gpc_dev, "ipg"); ++ if (IS_ERR(gpu2d_clk) || IS_ERR(openvg_axi_clk) ++ || IS_ERR(ipg_clk)) { ++ dev_err(gpc_dev, "failed to get clk!\n"); ++ return -ENOENT; ++ } ++ } else { ++ gpu3d_clk = devm_clk_get(gpc_dev, "gpu3d_core"); ++ gpu3d_shader_clk = devm_clk_get(gpc_dev, "gpu3d_shader"); ++ gpu2d_clk = devm_clk_get(gpc_dev, "gpu2d_core"); ++ gpu2d_axi_clk = devm_clk_get(gpc_dev, "gpu2d_axi"); ++ openvg_axi_clk = devm_clk_get(gpc_dev, "openvg_axi"); ++ vpu_clk = devm_clk_get(gpc_dev, "vpu_axi"); ++ ipg_clk = devm_clk_get(gpc_dev, "ipg"); ++ if (IS_ERR(gpu3d_clk) || IS_ERR(gpu3d_shader_clk) ++ || IS_ERR(gpu2d_clk) || IS_ERR(gpu2d_axi_clk) ++ || IS_ERR(openvg_axi_clk) || IS_ERR(vpu_clk) ++ || IS_ERR(ipg_clk)) { ++ dev_err(gpc_dev, "failed to get clk!\n"); ++ return -ENOENT; ++ } ++ } ++ ++ ret = regulator_register_notifier(pu_reg, &nb); ++ if (ret) { ++ dev_err(gpc_dev, ++ "regulator notifier request failed\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id imx_gpc_ids[] = { ++ { .compatible = "fsl,imx6q-gpc" }, ++}; ++MODULE_DEVICE_TABLE(of, imx_gpc_ids); ++ ++static struct platform_driver imx_gpc_platdrv = { ++ .driver = { ++ .name = "imx-gpc", ++ .owner = THIS_MODULE, ++ .of_match_table = imx_gpc_ids, ++ }, ++ .probe = imx_gpc_probe, ++}; ++module_platform_driver(imx_gpc_platdrv); ++ ++module_platform_driver(pu_dummy_driver); ++ ++MODULE_AUTHOR("Anson Huang "); ++MODULE_DESCRIPTION("Freescale i.MX GPC driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/arch/arm/mach-imx/hardware.h linux-openelec/arch/arm/mach-imx/hardware.h +--- linux-3.14.36/arch/arm/mach-imx/hardware.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/hardware.h 2015-05-06 12:05:43.000000000 -0500 +@@ -1,5 +1,5 @@ + /* +- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Copyright 2004-2007, 2014 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or +@@ -20,7 +20,9 @@ + #ifndef __ASM_ARCH_MXC_HARDWARE_H__ + #define __ASM_ARCH_MXC_HARDWARE_H__ + ++#ifndef __ASSEMBLY__ + #include ++#endif + #include + + #define addr_in_module(addr, mod) \ +diff -Nur linux-3.14.36/arch/arm/mach-imx/headsmp.S linux-openelec/arch/arm/mach-imx/headsmp.S +--- linux-3.14.36/arch/arm/mach-imx/headsmp.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/headsmp.S 2015-05-06 12:05:43.000000000 -0500 +@@ -12,8 +12,6 @@ + + #include + #include +-#include +-#include + + .section ".text.head", "ax" + +@@ -35,37 +33,3 @@ + b secondary_startup + ENDPROC(v7_secondary_startup) + #endif +- +-#ifdef CONFIG_ARM_CPU_SUSPEND +-/* +- * The following code must assume it is running from physical address +- * where absolute virtual addresses to the data section have to be +- * turned into relative ones. +- */ +- +-#ifdef CONFIG_CACHE_L2X0 +- .macro pl310_resume +- adr r0, l2x0_saved_regs_offset +- ldr r2, [r0] +- add r2, r2, r0 +- ldr r0, [r2, #L2X0_R_PHY_BASE] @ get physical base of l2x0 +- ldr r1, [r2, #L2X0_R_AUX_CTRL] @ get aux_ctrl value +- str r1, [r0, #L2X0_AUX_CTRL] @ restore aux_ctrl +- mov r1, #0x1 +- str r1, [r0, #L2X0_CTRL] @ re-enable L2 +- .endm +- +-l2x0_saved_regs_offset: +- .word l2x0_saved_regs - . +- +-#else +- .macro pl310_resume +- .endm +-#endif +- +-ENTRY(v7_cpu_resume) +- bl v7_invalidate_l1 +- pl310_resume +- b cpu_resume +-ENDPROC(v7_cpu_resume) +-#endif +diff -Nur linux-3.14.36/arch/arm/mach-imx/imx6sl_wfi.S linux-openelec/arch/arm/mach-imx/imx6sl_wfi.S +--- linux-3.14.36/arch/arm/mach-imx/imx6sl_wfi.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/imx6sl_wfi.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,639 @@ ++/* ++ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#define IRAM_WAIT_SIZE (1 << 11) ++ ++ .macro sl_ddr_io_save ++ ++ ldr r4, [r1, #0x30c] /* DRAM_DQM0 */ ++ ldr r5, [r1, #0x310] /* DRAM_DQM1 */ ++ ldr r6, [r1, #0x314] /* DRAM_DQM2 */ ++ ldr r7, [r1, #0x318] /* DRAM_DQM3 */ ++ stmfd r9!, {r4-r7} ++ ++ ldr r4, [r1, #0x5c4] /* GPR_B0DS */ ++ ldr r5, [r1, #0x5cc] /* GPR_B1DS */ ++ ldr r6, [r1, #0x5d4] /* GPR_B2DS */ ++ ldr r7, [r1, #0x5d8] /* GPR_B3DS */ ++ stmfd r9!, {r4-r7} ++ ++ ldr r4, [r1, #0x300] /* DRAM_CAS */ ++ ldr r5, [r1, #0x31c] /* DRAM_RAS */ ++ ldr r6, [r1, #0x338] /* DRAM_SDCLK_0 */ ++ ldr r7, [r1, #0x5ac] /* GPR_ADDS*/ ++ stmfd r9!, {r4-r7} ++ ++ ldr r4, [r1, #0x5b0] /* DDRMODE_CTL */ ++ ldr r5, [r1, #0x5c0] /* DDRMODE */ ++ ldr r6, [r1, #0x33c] /* DRAM_SODT0*/ ++ ldr r7, [r1, #0x340] /* DRAM_SODT1*/ ++ stmfd r9!, {r4-r7} ++ ++ ldr r4, [r1, #0x330] /* DRAM_SDCKE0 */ ++ ldr r5, [r1, #0x334] /* DRAM_SDCKE1 */ ++ ldr r6, [r1, #0x320] /* DRAM_RESET */ ++ stmfd r9!, {r4-r6} ++ ++ .endm ++ ++ .macro sl_ddr_io_restore ++ ++ /* ++ * r9 points to IRAM stack. ++ * r1 points to IOMUX base address. ++ * r8 points to MMDC base address. ++ */ ++ ldmea r9!, {r4-r7} ++ str r4, [r1, #0x30c] /* DRAM_DQM0 */ ++ str r5, [r1, #0x310] /* DRAM_DQM1 */ ++ str r6, [r1, #0x314] /* DRAM_DQM2 */ ++ str r7, [r1, #0x318] /* DRAM_DQM3 */ ++ ++ ldmea r9!, {r4-r7} ++ str r4, [r1, #0x5c4] /* GPR_B0DS */ ++ str r5, [r1, #0x5cc] /* GPR_B1DS */ ++ str r6, [r1, #0x5d4] /* GPR_B2DS */ ++ str r7, [r1, #0x5d8] /* GPR_B3DS */ ++ ++ ldmea r9!, {r4-r7} ++ str r4, [r1, #0x300] /* DRAM_CAS */ ++ str r5, [r1, #0x31c] /* DRAM_RAS */ ++ str r6, [r1, #0x338] /* DRAM_SDCLK_0 */ ++ str r7, [r1, #0x5ac] /* GPR_ADDS*/ ++ ++ ldmea r9!, {r4-r7} ++ str r4, [r1, #0x5b0] /* DDRMODE_CTL */ ++ str r5, [r1, #0x5c0] /* DDRMODE */ ++ str r6, [r1, #0x33c] /* DRAM_SODT0*/ ++ str r7, [r1, #0x340] /* DRAM_SODT1*/ ++ ++ ldmea r9!, {r4-r6} ++ str r4, [r1, #0x330] /* DRAM_SDCKE0 */ ++ str r5, [r1, #0x334] /* DRAM_SDCKE1 */ ++ str r6, [r1, #0x320] /* DRAM_RESET */ ++ ++ /* ++ * Need to reset the FIFO to avoid MMDC lockup ++ * caused because of floating/changing the ++ * configuration of many DDR IO pads. ++ */ ++ ldr r7, =0x83c ++ ldr r6, [r8, r7] ++ orr r6, r6, #0x80000000 ++ str r6, [r8, r7] ++fifo_reset1_wait: ++ ldr r6, [r8, r7] ++ and r6, r6, #0x80000000 ++ cmp r6, #0 ++ bne fifo_reset1_wait ++ ++ /* reset FIFO a second time */ ++ ldr r6, [r8, r7] ++ orr r6, r6, #0x80000000 ++ str r6, [r8, r7] ++fifo_reset2_wait: ++ ldr r6, [r8, r7] ++ and r6, r6, #0x80000000 ++ cmp r6, #0 ++ bne fifo_reset2_wait ++ ++ .endm ++ ++ .macro sl_ddr_io_set_lpm ++ ++ mov r4, #0 ++ str r4, [r1, #0x30c] /* DRAM_DQM0 */ ++ str r4, [r1, #0x310] /* DRAM_DQM1 */ ++ str r4, [r1, #0x314] /* DRAM_DQM2 */ ++ str r4, [r1, #0x318] /* DRAM_DQM3 */ ++ ++ str r4, [r1, #0x5c4] /* GPR_B0DS */ ++ str r4, [r1, #0x5cc] /* GPR_B1DS */ ++ str r4, [r1, #0x5d4] /* GPR_B2DS */ ++ str r4, [r1, #0x5d8] /* GPR_B3DS */ ++ ++ str r4, [r1, #0x300] /* DRAM_CAS */ ++ str r4, [r1, #0x31c] /* DRAM_RAS */ ++ str r4, [r1, #0x338] /* DRAM_SDCLK_0 */ ++ str r4, [r1, #0x5ac] /* GPR_ADDS*/ ++ ++ str r4, [r1, #0x5b0] /* DDRMODE_CTL */ ++ str r4, [r1, #0x5c0] /* DDRMODE */ ++ str r4, [r1, #0x33c] /* DRAM_SODT0*/ ++ str r4, [r1, #0x340] /* DRAM_SODT1*/ ++ ++ mov r4, #0x80000 ++ str r4, [r1, #0x320] /* DRAM_RESET */ ++ mov r4, #0x1000 ++ str r4, [r1, #0x330] /* DRAM_SDCKE0 */ ++ str r4, [r1, #0x334] /* DRAM_SDCKE1 */ ++ ++ .endm ++ ++/* ++ * imx6sl_low_power_wfi ++ * ++ * Idle the processor (eg, wait for interrupt). ++ * Make sure DDR is in self-refresh. ++ * IRQs are already disabled. ++ * r0: WFI IRAMcode base address. ++ * r1: IOMUX base address ++ * r2: Base address of CCM, ANATOP and MMDC ++ * r3: 1 if in audio_bus_freq_mode ++ */ ++ .align 3 ++ENTRY(imx6sl_low_power_wfi) ++ ++ push {r4-r11} ++ ++mx6sl_lpm_wfi: ++ /* Store audio_bus_freq_mode */ ++ mov r11, r3 ++ ++ mov r4,r2 ++ /* Get the IRAM data storage address. */ ++ mov r10, r0 ++ mov r9, r0 /* get suspend_iram_base */ ++ add r9, r9, #IRAM_WAIT_SIZE ++ ++ /* Anatop Base address in r3. */ ++ ldr r3, [r4] ++ /* CCM Base Address in r2 */ ++ ldr r2, [r4, #0x4] ++ /* MMDC Base Address in r8 */ ++ ldr r8, [r4, #0x8] ++ /* L2 Base Address in r7 */ ++ ldr r7, [r4, #0xC] ++ ++ ldr r6, [r8] ++ ldr r6, [r3] ++ ldr r6, [r2] ++ ldr r6, [r1] ++ ++ /* Store the original ARM PODF. */ ++ ldr r0, [r2, #0x10] ++ ++ /* Drain all the L1 buffers. */ ++ dsb ++ ++#ifdef CONFIG_CACHE_L2X0 ++ /* ++ * Need to make sure the buffers in L2 are drained. ++ * Performing a sync operation does this. ++ */ ++ mov r6, #0x0 ++ str r6, [r7, #0x730] ++#endif ++ ++ /* ++ * The second dsb might be needed to keep cache sync (device write) ++ * ordering with the memory accesses before it. ++ */ ++ dsb ++ isb ++ ++ /* Save the DDR IO state. */ ++ sl_ddr_io_save ++ ++ /* Disable Automatic power savings. */ ++ ldr r6, [r8, #0x404] ++ orr r6, r6, #0x01 ++ str r6, [r8, #0x404] ++ ++ /* Make the DDR explicitly enter self-refresh. */ ++ ldr r6, [r8, #0x404] ++ orr r6, r6, #0x200000 ++ str r6, [r8, #0x404] ++ ++poll_dvfs_set_1: ++ ldr r6, [r8, #0x404] ++ and r6, r6, #0x2000000 ++ cmp r6, #0x2000000 ++ bne poll_dvfs_set_1 ++ ++ /* set SBS step-by-step mode */ ++ ldr r6, [r8, #0x410] ++ orr r6, r6, #0x100 ++ str r6, [r8, #0x410] ++ ++ cmp r11, #1 ++ beq audio_mode ++ /* ++ * Now set DDR rate to 1MHz. ++ * DDR is from bypassed PLL2 on periph2_clk2 path. ++ * Set the periph2_clk2_podf to divide by 8. ++ */ ++ ldr r6, [r2, #0x14] ++ orr r6, r6, #0x07 ++ str r6, [r2, #0x14] ++ ++ /* Now set MMDC PODF to divide by 3. */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x38 ++ orr r6, r6, #0x10 ++ str r6, [r2, #0x14] ++ b mmdc_podf ++ ++audio_mode: ++ /* MMDC is from PLL2_200M. ++ * Set the mmdc_podf to div by 8. ++ */ ++ ldr r6, [r2, #0x14] ++ orr r6, r6, #0x38 ++ str r6, [r2, #0x14] ++ ++ /* Loop till podf is accepted. */ ++mmdc_podf: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0x0 ++ bne mmdc_podf ++ ++ /* Set the DDR IO in LPM state. */ ++ sl_ddr_io_set_lpm ++ ++ cmp r11, #1 ++ beq do_audio_arm_clk ++ ++ /* ++ * Check if none of the PLLs are ++ * locked, except PLL1 which will get ++ * bypassed below. ++ * We should not be here if PLL2 is not ++ * bypassed. ++ */ ++ ldr r7, =1 ++ /* USB1 PLL3 */ ++ ldr r6, [r3, #0x10] ++ and r6, r6, #0x80000000 ++ cmp r6, #0x80000000 ++ beq no_analog_saving ++ ++ /* USB2 PLL7 */ ++ ldr r6, [r3, #0x20] ++ and r6, r6, #0x80000000 ++ cmp r6, #0x80000000 ++ beq no_analog_saving ++ ++ /* Audio PLL4 */ ++ ldr r6, [r3, #0x70] ++ and r6, r6, #0x80000000 ++ cmp r6, #0x80000000 ++ beq no_analog_saving ++ ++ /* Video PLL5 */ ++ ldr r6, [r3, #0xA0] ++ and r6, r6, #0x80000000 ++ cmp r6, #0x80000000 ++ beq no_analog_saving ++ ++ /* ENET PLL8 */ ++ ldr r6, [r3, #0xE0] ++ and r6, r6, #0x80000000 ++ cmp r6, #0x80000000 ++ beq no_analog_saving ++ ++ b cont ++ ++no_analog_saving: ++ ldr r7, =0 ++ ++cont: ++ /* Set the AHB to 3MHz. AXI to 3MHz. */ ++ ldr r9, [r2, #0x14] ++ mov r6, r9 ++ orr r6, r6, #0x1c00 ++ orr r6, r6, #0x70000 ++ str r6, [r2, #0x14] ++ ++ /* Loop till podf is accepted. */ ++ahb_podf: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0x0 ++ bne podf_loop ++ ++ /* ++ * Now set ARM to 24MHz. ++ * Move ARM to be sourced from STEP_CLK ++ * after setting STEP_CLK to 24MHz. ++ */ ++ ldr r6, [r2, #0xc] ++ bic r6, r6, #0x100 ++ str r6, [r2, #0x0c] ++ /* Now PLL1_SW_CLK to step_clk. */ ++ ldr r6, [r2, #0x0c] ++ orr r6, r6, #0x4 ++ str r6, [r2, #0x0c] ++ ++ /* Bypass PLL1 and power it down. */ ++ ldr r6, =(1 << 16) ++ orr r6, r6, #0x1000 ++ str r6, [r3, #0x04] ++ ++ /* ++ * Set the ARM PODF to divide by 8. ++ * IPG is at 1.5MHz here, we need ARM to ++ * run at the 12:5 ratio (WAIT mode issue). ++ */ ++ ldr r6, =0x7 ++ str r6, [r2, #0x10] ++ ++ /* Loop till podf is accepted. */ ++podf_loop: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0x0 ++ bne podf_loop ++ ++ /* ++ * Check if we can save some ++ * power in the Analog section. ++ */ ++ cmp r7, #0x1 ++ bne do_wfi ++ ++ /* Disable 1p1 brown out. */ ++ ldr r6, [r3, #0x110] ++ bic r6, r6, #0x2 ++ str r6, [r3, #0x110] ++ ++ /* Enable the weak 2P5 */ ++ ldr r6, [r3, #0x130] ++ orr r6, r6, #0x40000 ++ str r6, [r3, #0x130] ++ ++ /* Disable main 2p5. */ ++ ldr r6, [r3, #0x130] ++ bic r6, r6, #0x1 ++ str r6, [r3, #0x130] ++ ++ /* ++ * Set the OSC bias current to -37.5% ++ * to drop the power on VDDHIGH. ++ */ ++ ldr r6, [r3, #0x150] ++ orr r6, r6, #0xC000 ++ str r6, [r3, #0x150] ++ ++ /* Enable low power bandgap */ ++ ldr r6, [r3, #0x260] ++ orr r6, r6, #0x20 ++ str r6, [r3, #0x260] ++ ++ /* ++ * Turn off the bias current ++ * from the regular bandgap. ++ */ ++ ldr r6, [r3, #0x260] ++ orr r6, r6, #0x80 ++ str r6, [r3, #0x260] ++ ++ /* ++ * Clear the REFTOP_SELFBIASOFF, ++ * self-bias circuit of the band gap. ++ * Per RM, should be cleared when ++ * band gap is powered down. ++ */ ++ ldr r6, [r3, #0x150] ++ bic r6, r6, #0x8 ++ str r6, [r3, #0x150] ++ ++ /* Power down the regular bandgap. */ ++ ldr r6, [r3, #0x150] ++ orr r6, r6, #0x1 ++ str r6, [r3, #0x150] ++ ++ b do_wfi ++ ++do_audio_arm_clk: ++ /* ++ * ARM is from PLL2_PFD2_400M here. ++ * Switch ARM to bypassed PLL1. ++ */ ++ ldr r6, [r2, #0xC] ++ bic r6, r6, #0x4 ++ str r6, [r2, #0xC] ++ ++ /* ++ * Set the ARM_PODF to divide by 2 ++ * as IPG is at 4MHz, we cannot run ++ * ARM_CLK above 9.6MHz when ++ * system enters WAIT mode. ++ */ ++ ldr r6, =0x2 ++ str r6, [r2, #0x10] ++ ++ /* Loop till podf is accepted. */ ++podf_loop_audio: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0x0 ++ bne podf_loop_audio ++ ++do_wfi: ++ /* Now do WFI. */ ++ wfi ++ ++ /* Set original ARM PODF back. */ ++ str r0, [r2, #0x10] ++ ++ /* Loop till podf is accepted. */ ++podf_loop1: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0x0 ++ bne podf_loop1 ++ ++ cmp r11, #1 ++ beq audio_arm_clk_restore ++ ++ /* ++ * Check if powered down ++ * analog components. ++ */ ++ cmp r7, #0x1 ++ bne skip_analog_restore ++ ++ /* Power up the regular bandgap. */ ++ ldr r6, [r3, #0x150] ++ bic r6, r6, #0x1 ++ str r6, [r3, #0x150] ++ ++ /* ++ * Turn on the bias current ++ * from the regular bandgap. ++ */ ++ ldr r6, [r3, #0x260] ++ bic r6, r6, #0x80 ++ str r6, [r3, #0x260] ++ ++ /* Disable the low power bandgap */ ++ ldr r6, [r3, #0x260] ++ bic r6, r6, #0x20 ++ str r6, [r3, #0x260] ++ ++ /* ++ * Set the OSC bias current to max ++ * value for normal operation. ++ */ ++ ldr r6, [r3, #0x150] ++ bic r6, r6, #0xC000 ++ str r6, [r3, #0x150] ++ ++ /* Enable main 2p5. */ ++ ldr r6, [r3, #0x130] ++ orr r6, r6, #0x1 ++ str r6, [r3, #0x130] ++ ++ /* Ensure the 2P5 is up. */ ++loop_2p5: ++ ldr r6, [r3, #0x130] ++ and r6, r6, #0x20000 ++ cmp r6, #0x20000 ++ bne loop_2p5 ++ ++ /* Disable the weak 2P5 */ ++ ldr r6, [r3, #0x130] ++ bic r6, r6, #0x40000 ++ str r6, [r3, #0x130] ++ ++ /* Enable 1p1 brown out. */ ++ ldr r6, [r3, #0x110] ++ orr r6, r6, #0x2 ++ str r6, [r3, #0x110] ++ ++skip_analog_restore: ++ ++ /* Power up PLL1 and un-bypass it. */ ++ ldr r6, =(1 << 12) ++ str r6, [r3, #0x08] ++ ++ /* Wait for PLL1 to relock. */ ++wait_for_pll_lock: ++ ldr r6, [r3, #0x0] ++ and r6, r6, #0x80000000 ++ cmp r6, #0x80000000 ++ bne wait_for_pll_lock ++ ++ ldr r6, =(1 << 16) ++ str r6, [r3, #0x08] ++ ++ /* Set PLL1_sw_clk back to PLL1. */ ++ ldr r6, [r2, #0x0c] ++ bic r6, r6, #0x4 ++ str r6, [r2, #0xc] ++ ++ /* Restore AHB/AXI back. */ ++ str r9, [r2, #0x14] ++ ++ /* Loop till podf is accepted. */ ++ahb_podf1: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0x0 ++ bne podf_loop1 ++ ++ b wfi_restore ++ ++ audio_arm_clk_restore: ++ /* Move ARM back to PLL2_PFD2_400M */ ++ ldr r6, [r2, #0xC] ++ orr r6, r6, #0x4 ++ str r6, [r2, #0xC] ++ ++wfi_restore: ++ /* get suspend_iram_base */ ++ mov r9, r10 ++ add r9, r9, #IRAM_WAIT_SIZE ++ ++ /* Restore the DDR IO before exiting self-refresh. */ ++ sl_ddr_io_restore ++ ++ /* ++ * Set MMDC back to 24MHz. ++ * Set periph2_clk2_podf to divide by 1 ++ * Now set MMDC PODF to divide by 1. ++ */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x3f ++ str r6, [r2, #0x14] ++ ++mmdc_podf1: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0x0 ++ bne mmdc_podf1 ++ ++ /* clear DVFS - exit from self refresh mode */ ++ ldr r6, [r8, #0x404] ++ bic r6, r6, #0x200000 ++ str r6, [r8, #0x404] ++ ++poll_dvfs_clear_1: ++ ldr r6, [r8, #0x404] ++ and r6, r6, #0x2000000 ++ cmp r6, #0x2000000 ++ beq poll_dvfs_clear_1 ++ ++ /* ++ * Add these nops so that the ++ * prefetcher will not try to get ++ * any instructions from DDR. ++ * The prefetch depth is about 23 ++ * on A9, so adding 25 nops. ++ */ ++ nop ++ nop ++ nop ++ nop ++ nop ++ ++ nop ++ nop ++ nop ++ nop ++ nop ++ ++ nop ++ nop ++ nop ++ nop ++ nop ++ ++ nop ++ nop ++ nop ++ nop ++ nop ++ ++ nop ++ nop ++ nop ++ nop ++ nop ++ ++ /* Enable Automatic power savings. */ ++ ldr r6, [r8, #0x404] ++ bic r6, r6, #0x01 ++ str r6, [r8, #0x404] ++ ++ /* clear SBS - unblock DDR accesses */ ++ ldr r6, [r8, #0x410] ++ bic r6, r6, #0x100 ++ str r6, [r8, #0x410] ++ ++ ++ pop {r4-r11} ++ ++ /* Restore registers */ ++ mov pc, lr +diff -Nur linux-3.14.36/arch/arm/mach-imx/Kconfig linux-openelec/arch/arm/mach-imx/Kconfig +--- linux-3.14.36/arch/arm/mach-imx/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/Kconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -1,5 +1,6 @@ + config ARCH_MXC + bool "Freescale i.MX family" if ARCH_MULTI_V4_V5 || ARCH_MULTI_V6_V7 ++ select ARCH_HAS_RESET_CONTROLLER + select ARCH_REQUIRE_GPIOLIB + select ARM_CPU_SUSPEND if PM + select ARM_PATCH_PHYS_VIRT +@@ -13,6 +14,7 @@ + select PINCTRL + select SOC_BUS + select SPARSE_IRQ ++ select SRAM + select USE_OF + help + Support for Freescale MXC/iMX-based family of processors +@@ -63,7 +65,6 @@ + + config HAVE_IMX_SRC + def_bool y if SMP +- select ARCH_HAS_RESET_CONTROLLER + + config IMX_HAVE_IOMUX_V1 + bool +@@ -791,6 +792,8 @@ + select ARM_ERRATA_754322 + select ARM_ERRATA_764369 if SMP + select ARM_ERRATA_775420 ++ select ARM_ERRATA_794072 if SMP ++ select ARM_ERRATA_761320 if SMP + select ARM_GIC + select CPU_V7 + select HAVE_ARM_SCU if SMP +@@ -803,11 +806,13 @@ + select MFD_SYSCON + select MIGHT_HAVE_PCI + select PCI_DOMAINS if PCI ++ select ARCH_SUPPORTS_MSI + select PINCTRL_IMX6Q + select PL310_ERRATA_588369 if CACHE_PL310 + select PL310_ERRATA_727915 if CACHE_PL310 + select PL310_ERRATA_769419 if CACHE_PL310 + select PM_OPP if PM ++ select ZONE_DMA + + help + This enables support for Freescale i.MX6 Quad processor. +diff -Nur linux-3.14.36/arch/arm/mach-imx/lpddr2_freq_imx6.S linux-openelec/arch/arm/mach-imx/lpddr2_freq_imx6.S +--- linux-3.14.36/arch/arm/mach-imx/lpddr2_freq_imx6.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/lpddr2_freq_imx6.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,484 @@ ++/* ++ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++ ++ .macro mx6sl_switch_to_24MHz ++ ++ /* ++ * Set MMDC clock to be sourced from PLL3. ++ * Ensure first periph2_clk2 is sourced from PLL3. ++ * Set the PERIPH2_CLK2_PODF to divide by 2. ++ */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x7 ++ orr r6, r6, #0x1 ++ str r6, [r2, #0x14] ++ ++ /* Select PLL3 to source MMDC. */ ++ ldr r6, [r2, #0x18] ++ bic r6, r6, #0x100000 ++ str r6, [r2, #0x18] ++ ++ /* Swtich periph2_clk_sel to run from PLL3. */ ++ ldr r6, [r2, #0x14] ++ orr r6, r6, #0x4000000 ++ str r6, [r2, #0x14] ++ ++periph2_clk_switch1: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne periph2_clk_switch1 ++ ++ /* ++ * Need to clock gate the 528 PFDs before ++ * powering down PLL2. ++ * Only the PLL2_PFD2_400M should be ON ++ * at this time, so only clock gate that one. ++ */ ++ ldr r6, [r3, #0x100] ++ orr r6, r6, #0x800000 ++ str r6, [r3, #0x100] ++ ++ /* ++ * Set PLL2 to bypass state. We should be here ++ * only if MMDC is not sourced from PLL2. ++ */ ++ ldr r6, [r3, #0x30] ++ orr r6, r6, #0x10000 ++ str r6, [r3, #0x30] ++ ++ ldr r6, [r3, #0x30] ++ orr r6, r6, #0x1000 ++ str r6, [r3, #0x30] ++ ++ /* Ensure pre_periph2_clk_mux is set to pll2 */ ++ ldr r6, [r2, #0x18] ++ bic r6, r6, #0x600000 ++ str r6, [r2, #0x18] ++ ++ /* Set MMDC clock to be sourced from the bypassed PLL2. */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x4000000 ++ str r6, [r2, #0x14] ++ ++periph2_clk_switch2: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne periph2_clk_switch2 ++ ++ /* ++ * Now move MMDC back to periph2_clk2 source. ++ * after selecting PLL2 as the option. ++ * Select PLL2 as the source. ++ */ ++ ldr r6, [r2, #0x18] ++ orr r6, r6, #0x100000 ++ str r6, [r2, #0x18] ++ ++ /* set periph2_clk2_podf to divide by 1. */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x7 ++ str r6, [r2, #0x14] ++ ++ /* Now move periph2_clk to periph2_clk2 source */ ++ ldr r6, [r2, #0x14] ++ orr r6, r6, #0x4000000 ++ str r6, [r2, #0x14] ++ ++periph2_clk_switch3: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne periph2_clk_switch3 ++ ++ /* Now set the MMDC PODF back to 1.*/ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x38 ++ str r6, [r2, #0x14] ++ ++mmdc_podf0: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne mmdc_podf0 ++ ++ .endm ++ ++ .macro ddr_switch_400MHz ++ ++ /* Set MMDC divider first, in case PLL3 is at 480MHz. */ ++ ldr r6, [r3, #0x10] ++ and r6, r6, #0x10000 ++ cmp r6, #0x10000 ++ beq pll3_in_bypass ++ ++ /* Set MMDC divder to divide by 2. */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x38 ++ orr r6, r6, #0x8 ++ str r6, [r2, #0x14] ++ ++mmdc_podf: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne mmdc_podf ++ ++pll3_in_bypass: ++ /* ++ * Check if we are switching between ++ * 400Mhz <-> 100MHz.If so, we should ++ * try to source MMDC from PLL2_200M. ++ */ ++ cmp r1, #0 ++ beq not_low_bus_freq ++ ++ /* Ensure that MMDC is sourced from PLL2 mux first. */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x4000000 ++ str r6, [r2, #0x14] ++ ++periph2_clk_switch4: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne periph2_clk_switch4 ++ ++not_low_bus_freq: ++ /* Now ensure periph2_clk2_sel mux is set to PLL3 */ ++ ldr r6, [r2, #0x18] ++ bic r6, r6, #0x100000 ++ str r6, [r2, #0x18] ++ ++ /* Now switch MMDC to PLL3. */ ++ ldr r6, [r2, #0x14] ++ orr r6, r6, #0x4000000 ++ str r6, [r2, #0x14] ++ ++periph2_clk_switch5: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne periph2_clk_switch5 ++ ++ /* ++ * Check if PLL2 is already unlocked. ++ * If so do nothing with PLL2. ++ */ ++ cmp r1, #0 ++ beq pll2_already_on ++ ++ /* Now power up PLL2 and unbypass it. */ ++ ldr r6, [r3, #0x30] ++ bic r6, r6, #0x1000 ++ str r6, [r3, #0x30] ++ ++ /* Make sure PLL2 has locked.*/ ++wait_for_pll_lock: ++ ldr r6, [r3, #0x30] ++ and r6, r6, #0x80000000 ++ cmp r6, #0x80000000 ++ bne wait_for_pll_lock ++ ++ ldr r6, [r3, #0x30] ++ bic r6, r6, #0x10000 ++ str r6, [r3, #0x30] ++ ++ /* ++ * Need to enable the 528 PFDs after ++ * powering up PLL2. ++ * Only the PLL2_PFD2_400M should be ON ++ * as it feeds the MMDC. Rest should have ++ * been managed by clock code. ++ */ ++ ldr r6, [r3, #0x100] ++ bic r6, r6, #0x800000 ++ str r6, [r3, #0x100] ++ ++pll2_already_on: ++ /* ++ * Now switch MMDC clk back to pll2_mux option. ++ * Ensure pre_periph2_clk2 is set to pll2_pfd_400M. ++ * If switching to audio DDR freq, set the ++ * pre_periph2_clk2 to PLL2_PFD_200M ++ */ ++ ldr r6, =400000000 ++ cmp r6, r0 ++ bne use_pll2_pfd_200M ++ ++ ldr r6, [r2, #0x18] ++ bic r6, r6, #0x600000 ++ orr r6, r6, #0x200000 ++ str r6, [r2, #0x18] ++ ldr r6, =400000000 ++ b cont2 ++ ++use_pll2_pfd_200M: ++ ldr r6, [r2, #0x18] ++ orr r6, r6, #0x600000 ++ str r6, [r2, #0x18] ++ ldr r6, =200000000 ++ ++cont2: ++ ldr r4, [r2, #0x14] ++ bic r4, r4, #0x4000000 ++ str r4, [r2, #0x14] ++ ++periph2_clk_switch6: ++ ldr r4, [r2, #0x48] ++ cmp r4, #0 ++ bne periph2_clk_switch6 ++ ++change_divider_only: ++ /* ++ * Calculate the MMDC divider ++ * based on the requested freq. ++ */ ++ ldr r4, =0 ++Loop2: ++ sub r6, r6, r0 ++ cmp r6, r0 ++ blt Div_Found ++ add r4, r4, #1 ++ bgt Loop2 ++ ++ /* Shift divider into correct offset. */ ++ lsl r4, r4, #3 ++Div_Found: ++ /* Set the MMDC PODF. */ ++ ldr r6, [r2, #0x14] ++ bic r6, r6, #0x38 ++ orr r6, r6, r4 ++ str r6, [r2, #0x14] ++ ++mmdc_podf1: ++ ldr r6, [r2, #0x48] ++ cmp r6, #0 ++ bne mmdc_podf1 ++ ++ .endm ++ ++ .macro mmdc_clk_lower_100MHz ++ ++ /* ++ * Prior to reducing the DDR frequency (at 528/400 MHz), ++ * read the Measure unit count bits (MU_UNIT_DEL_NUM) ++ */ ++ ldr r5, =0x8B8 ++ ldr r6, [r8, r5] ++ /* Original MU unit count */ ++ mov r6, r6, LSR #16 ++ ldr r4, =0x3FF ++ and r6, r6, r4 ++ /* Original MU unit count * 2 */ ++ mov r7, r6, LSL #1 ++ /* ++ * Bypass the automatic measure unit when below 100 MHz ++ * by setting the Measure unit bypass enable bit (MU_BYP_EN) ++ */ ++ ldr r6, [r8, r5] ++ orr r6, r6, #0x400 ++ str r6, [r8, r5] ++ /* ++ * Double the measure count value read in step 1 and program it in the ++ * measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit ++ * Register for the reduced frequency operation below 100 MHz ++ */ ++ ldr r6, [r8, r5] ++ ldr r4, =0x3FF ++ bic r6, r6, r4 ++ orr r6, r6, r7 ++ str r6, [r8, r5] ++ /* Now perform a Force Measurement. */ ++ ldr r6, [r8, r5] ++ orr r6, r6, #0x800 ++ str r6, [r8, r5] ++ /* Wait for FRC_MSR to clear. */ ++force_measure: ++ ldr r6, [r8, r5] ++ and r6, r6, #0x800 ++ cmp r6, #0x0 ++ bne force_measure ++ ++ .endm ++ ++ .macro mmdc_clk_above_100MHz ++ ++ /* Make sure that the PHY measurement unit is NOT in bypass mode */ ++ ldr r5, =0x8B8 ++ ldr r6, [r8, r5] ++ bic r6, r6, #0x400 ++ str r6, [r8, r5] ++ /* Now perform a Force Measurement. */ ++ ldr r6, [r8, r5] ++ orr r6, r6, #0x800 ++ str r6, [r8, r5] ++ /* Wait for FRC_MSR to clear. */ ++force_measure1: ++ ldr r6, [r8, r5] ++ and r6, r6, #0x800 ++ cmp r6, #0x0 ++ bne force_measure1 ++ .endm ++ ++/* ++ * mx6_lpddr2_freq_change ++ * ++ * Make sure DDR is in self-refresh. ++ * IRQs are already disabled. ++ * r0 : DDR freq. ++ * r1: low_bus_freq_mode flag ++ * r2: Pointer to array containing addresses of registers. ++ */ ++ .align 3 ++ENTRY(mx6_lpddr2_freq_change) ++ ++ push {r4-r10} ++ ++ mov r4, r2 ++ ldr r3, [r4] @ANATOP_BASE_ADDR ++ ldr r2, [r4, #0x4] @CCM_BASE_ADDR ++ ldr r8, [r4, #0x8] @MMDC_P0_BASE_ADDR ++ ldr r7, [r4, #0xC] @L2_BASE_ADDR ++ ++lpddr2_freq_change: ++ adr r9, lpddr2_freq_change ++ ++ /* Prime all TLB entries. */ ++ ldr r6, [r9] ++ ldr r6, [r8] ++ ldr r6, [r3] ++ ldr r6, [r2] ++ ++ /* Drain all the L1 buffers. */ ++ dsb ++ ++#ifdef CONFIG_CACHE_L2X0 ++ /* ++ * Need to make sure the buffers in L2 are drained. ++ * Performing a sync operation does this. ++ */ ++ mov r6, #0x0 ++ str r6, [r7, #0x730] ++#endif ++ ++ /* ++ * The second dsb might be needed to keep cache sync (device write) ++ * ordering with the memory accesses before it. ++ */ ++ dsb ++ isb ++ ++ /* Disable Automatic power savings. */ ++ ldr r6, [r8, #0x404] ++ orr r6, r6, #0x01 ++ str r6, [r8, #0x404] ++ ++ /* MMDC0_MDPDC disable power down timer */ ++ ldr r6, [r8, #0x4] ++ bic r6, r6, #0xff00 ++ str r6, [r8, #0x4] ++ ++ /* Delay for a while */ ++ ldr r10, =10 ++delay1: ++ ldr r7, =0 ++cont1: ++ ldr r6, [r8, r7] ++ add r7, r7, #4 ++ cmp r7, #16 ++ bne cont1 ++ sub r10, r10, #1 ++ cmp r10, #0 ++ bgt delay1 ++ ++ /* Make the DDR explicitly enter self-refresh. */ ++ ldr r6, [r8, #0x404] ++ orr r6, r6, #0x200000 ++ str r6, [r8, #0x404] ++ ++poll_dvfs_set_1: ++ ldr r6, [r8, #0x404] ++ and r6, r6, #0x2000000 ++ cmp r6, #0x2000000 ++ bne poll_dvfs_set_1 ++ ++ /* set SBS step-by-step mode */ ++ ldr r6, [r8, #0x410] ++ orr r6, r6, #0x100 ++ str r6, [r8, #0x410] ++ ++ ldr r10, =100000000 ++ cmp r0, r10 ++ bgt set_ddr_mu_above_100 ++ mmdc_clk_lower_100MHz ++ ++set_ddr_mu_above_100: ++ ldr r10, =24000000 ++ cmp r0, r10 ++ beq set_to_24MHz ++ ++ ddr_switch_400MHz ++ ++ ldr r10,=100000000 ++ cmp r0, r10 ++ blt done ++ mmdc_clk_above_100MHz ++ ++ b done ++ ++set_to_24MHz: ++ mx6sl_switch_to_24MHz ++ ++done: ++ /* clear DVFS - exit from self refresh mode */ ++ ldr r6, [r8, #0x404] ++ bic r6, r6, #0x200000 ++ str r6, [r8, #0x404] ++ ++poll_dvfs_clear_1: ++ ldr r6, [r8, #0x404] ++ and r6, r6, #0x2000000 ++ cmp r6, #0x2000000 ++ beq poll_dvfs_clear_1 ++ ++ /* Enable Automatic power savings. */ ++ ldr r6, [r8, #0x404] ++ bic r6, r6, #0x01 ++ str r6, [r8, #0x404] ++ ++ ldr r10, =24000000 ++ cmp r0, r10 ++ beq skip_power_down ++ ++ /* Enable MMDC power down timer. */ ++ ldr r6, [r8, #0x4] ++ orr r6, r6, #0x5500 ++ str r6, [r8, #0x4] ++ ++skip_power_down: ++ /* clear SBS - unblock DDR accesses */ ++ ldr r6, [r8, #0x410] ++ bic r6, r6, #0x100 ++ str r6, [r8, #0x410] ++ ++ pop {r4-r10} ++ ++ /* Restore registers */ ++ mov pc, lr ++ ++ .type mx6_lpddr2_do_iram, #object ++ENTRY(mx6_lpddr2_do_iram) ++ .word mx6_lpddr2_freq_change ++ .size mx6_lpddr2_freq_change, . - mx6_lpddr2_freq_change +diff -Nur linux-3.14.36/arch/arm/mach-imx/mach-imx6q.c linux-openelec/arch/arm/mach-imx/mach-imx6q.c +--- linux-3.14.36/arch/arm/mach-imx/mach-imx6q.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/mach-imx6q.c 2015-07-24 18:03:30.408842002 -0500 +@@ -1,5 +1,5 @@ + /* +- * Copyright 2011-2013 Freescale Semiconductor, Inc. ++ * Copyright 2011-2014 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -22,15 +23,19 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -194,6 +199,87 @@ + + } + ++static void __init imx6q_csi_mux_init(void) ++{ ++ /* ++ * MX6Q SabreSD board: ++ * IPU1 CSI0 connects to parallel interface. ++ * Set GPR1 bit 19 to 0x1. ++ * ++ * MX6DL SabreSD board: ++ * IPU1 CSI0 connects to parallel interface. ++ * Set GPR13 bit 0-2 to 0x4. ++ * IPU1 CSI1 connects to MIPI CSI2 virtual channel 1. ++ * Set GPR13 bit 3-5 to 0x1. ++ */ ++ struct regmap *gpr; ++ ++ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); ++ if (!IS_ERR(gpr)) { ++ if (of_machine_is_compatible("fsl,imx6q-sabresd") || ++ of_machine_is_compatible("fsl,imx6q-sabreauto")) ++ regmap_update_bits(gpr, IOMUXC_GPR1, 1 << 19, 1 << 19); ++ else if (of_machine_is_compatible("fsl,imx6dl-sabresd") || ++ of_machine_is_compatible("fsl,imx6dl-sabreauto")) ++ regmap_update_bits(gpr, IOMUXC_GPR13, 0x3F, 0x0C); ++ } else { ++ pr_err("%s(): failed to find fsl,imx6q-iomux-gpr regmap\n", ++ __func__); ++ } ++} ++ ++#define OCOTP_MACn(n) (0x00000620 + (n) * 0x10) ++void __init imx6_enet_mac_init(const char *compatible) ++{ ++ struct device_node *enet_np; ++ struct property *newmac; ++ u32 macaddr_low, macaddr_high; ++ u8 *macaddr; ++ int ret; ++ ++ enet_np = of_find_compatible_node(NULL, NULL, compatible); ++ if (!enet_np) ++ return; ++ ++ if (of_get_mac_address(enet_np)) ++ goto put_enet_node; ++ ++ ret = fsl_otp_readl(OCOTP_MACn(0), &macaddr_high); ++ ret = fsl_otp_readl(OCOTP_MACn(1), &macaddr_low); ++ ++ newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL); ++ if (!newmac) ++ goto put_enet_node; ++ ++ newmac->value = newmac + 1; ++ newmac->length = 6; ++ newmac->name = kstrdup("local-mac-address", GFP_KERNEL); ++ if (!newmac->name) { ++ kfree(newmac); ++ goto put_enet_node; ++ } ++ ++ macaddr = newmac->value; ++ macaddr[5] = macaddr_high & 0xff; ++ macaddr[4] = (macaddr_high >> 8) & 0xff; ++ macaddr[3] = (macaddr_high >> 16) & 0xff; ++ macaddr[2] = (macaddr_high >> 24) & 0xff; ++ macaddr[1] = macaddr_low & 0xff; ++ macaddr[0] = (macaddr_low >> 8) & 0xff; ++ ++ of_update_property(enet_np, newmac); ++ ++put_enet_node: ++ of_node_put(enet_np); ++} ++ ++static inline void imx6q_enet_init(void) ++{ ++ imx6_enet_mac_init("fsl,imx6q-fec"); ++ imx6q_enet_phy_init(); ++ imx6q_1588_init(); ++} ++ + static void __init imx6q_init_machine(void) + { + struct device *parent; +@@ -207,45 +293,60 @@ + if (parent == NULL) + pr_warn("failed to initialize soc device\n"); + +- imx6q_enet_phy_init(); +- + of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); + ++ imx6q_enet_init(); + imx_anatop_init(); +- imx6q_pm_init(); +- imx6q_1588_init(); ++ cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init(); ++ imx6q_csi_mux_init(); + } + + #define OCOTP_CFG3 0x440 + #define OCOTP_CFG3_SPEED_SHIFT 16 + #define OCOTP_CFG3_SPEED_1P2GHZ 0x3 ++#define OCOTP_CFG3_SPEED_1GHZ 0x2 ++#define OCOTP_CFG3_SPEED_850MHZ 0x1 ++#define OCOTP_CFG3_SPEED_800MHZ 0x0 + +-static void __init imx6q_opp_check_1p2ghz(struct device *cpu_dev) ++static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev) + { +- struct device_node *np; +- void __iomem *base; + u32 val; ++ int ret; + +- np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); +- if (!np) { +- pr_warn("failed to find ocotp node\n"); +- return; ++ ret = fsl_otp_readl(OCOTP_CFG3, &val); ++ if (ret) { ++ pr_warn("failed to read ocotp\n"); ++ return; + } + +- base = of_iomap(np, 0); +- if (!base) { +- pr_warn("failed to map ocotp\n"); +- goto put_node; +- } ++ /* ++ * SPEED_GRADING[1:0] defines the max speed of ARM: ++ * 2b'11: 1200000000Hz; -- i.MX6Q only. ++ * 2b'10: 1000000000Hz; ++ * 2b'01: 850000000Hz; -- i.MX6Q Only, exclusive with 1GHz. ++ * 2b'00: 800000000Hz; ++ * We need to set the max speed of ARM according to fuse map. ++ */ + +- val = readl_relaxed(base + OCOTP_CFG3); + val >>= OCOTP_CFG3_SPEED_SHIFT; +- if ((val & 0x3) != OCOTP_CFG3_SPEED_1P2GHZ) +- if (dev_pm_opp_disable(cpu_dev, 1200000000)) +- pr_warn("failed to disable 1.2 GHz OPP\n"); +- +-put_node: +- of_node_put(np); ++ if (cpu_is_imx6q()) { ++ if ((val & 0x3) < OCOTP_CFG3_SPEED_1P2GHZ) ++ if (dev_pm_opp_disable(cpu_dev, 1200000000)) ++ pr_warn("failed to disable 1.2 GHz OPP\n"); ++ } ++ if ((val & 0x3) < OCOTP_CFG3_SPEED_1GHZ) ++ if (dev_pm_opp_disable(cpu_dev, 996000000)) ++ pr_warn("failed to disable 1 GHz OPP\n"); ++ if (cpu_is_imx6q()) { ++ if ((val & 0x3) < OCOTP_CFG3_SPEED_850MHZ || ++ (val & 0x3) == OCOTP_CFG3_SPEED_1GHZ) ++ if (dev_pm_opp_disable(cpu_dev, 852000000)) ++ pr_warn("failed to disable 850 MHz OPP\n"); ++ } ++ if (IS_ENABLED(CONFIG_MX6_VPU_352M)) { ++ if (dev_pm_opp_disable(cpu_dev, 396000000)) ++ pr_warn("failed to disable 396MHz OPP\n"); ++ } + } + + static void __init imx6q_opp_init(void) +@@ -268,29 +369,70 @@ + goto put_node; + } + +- imx6q_opp_check_1p2ghz(cpu_dev); ++ imx6q_opp_check_speed_grading(cpu_dev); + + put_node: + of_node_put(np); + } + ++#define ESAI_AUDIO_MCLK 24576000 ++ ++static void __init imx6q_audio_lvds2_init(void) ++{ ++ struct clk *pll4_sel, *lvds2_in, *pll4_audio_div, *esai; ++ ++ pll4_audio_div = clk_get_sys(NULL, "pll4_audio_div"); ++ pll4_sel = clk_get_sys(NULL, "pll4_sel"); ++ lvds2_in = clk_get_sys(NULL, "lvds2_in"); ++ esai = clk_get_sys(NULL, "esai"); ++ if (IS_ERR(pll4_audio_div) || IS_ERR(pll4_sel) || ++ IS_ERR(lvds2_in) || IS_ERR(esai)) ++ return; ++ ++ if (clk_get_rate(lvds2_in) != ESAI_AUDIO_MCLK) ++ return; ++ ++ clk_set_parent(pll4_sel, lvds2_in); ++ clk_set_rate(pll4_audio_div, 786432000); ++ clk_set_rate(esai, ESAI_AUDIO_MCLK); ++} ++ + static struct platform_device imx6q_cpufreq_pdev = { +- .name = "imx6q-cpufreq", ++ .name = "imx6-cpufreq", + }; + + static void __init imx6q_init_late(void) + { ++ struct regmap *gpr; ++ ++ /* ++ * Need to force IOMUXC irq pending to meet CCM low power mode ++ * restriction, this is recommended by hardware team. ++ */ ++ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); ++ if (!IS_ERR(gpr)) ++ regmap_update_bits(gpr, IOMUXC_GPR1, ++ IMX6Q_GPR1_GINT_MASK, ++ IMX6Q_GPR1_GINT_ASSERT); ++ + /* + * WAIT mode is broken on TO 1.0 and 1.1, so there is no point + * to run cpuidle on them. + */ +- if (imx_get_soc_revision() > IMX_CHIP_REVISION_1_1) ++ if ((cpu_is_imx6q() && imx_get_soc_revision() > IMX_CHIP_REVISION_1_1) ++ || (cpu_is_imx6dl() && imx_get_soc_revision() > ++ IMX_CHIP_REVISION_1_0)) + imx6q_cpuidle_init(); + +- if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) { ++ if (IS_ENABLED(CONFIG_ARM_IMX6_CPUFREQ)) { + imx6q_opp_init(); + platform_device_register(&imx6q_cpufreq_pdev); + } ++ ++ if (of_machine_is_compatible("fsl,imx6q-sabreauto") ++ || of_machine_is_compatible("fsl,imx6dl-sabreauto")) { ++ imx6q_audio_lvds2_init(); ++ } + } + + static void __init imx6q_map_io(void) +@@ -315,6 +457,12 @@ + }; + + DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)") ++ /* ++ * i.MX6Q/DL maps system memory at 0x10000000 (offset 256MiB), and ++ * GPU has a limit on physical address that it accesses, which must ++ * be below 2GiB. ++ */ ++ .dma_zone_size = (SZ_2G - SZ_256M), + .smp = smp_ops(imx_smp_ops), + .map_io = imx6q_map_io, + .init_irq = imx6q_init_irq, +diff -Nur linux-3.14.36/arch/arm/mach-imx/mach-imx6q.c.orig linux-openelec/arch/arm/mach-imx/mach-imx6q.c.orig +--- linux-3.14.36/arch/arm/mach-imx/mach-imx6q.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/mach-imx6q.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,469 @@ ++/* ++ * Copyright 2011-2014 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "common.h" ++#include "cpuidle.h" ++#include "hardware.h" ++ ++/* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */ ++static int ksz9021rn_phy_fixup(struct phy_device *phydev) ++{ ++ if (IS_BUILTIN(CONFIG_PHYLIB)) { ++ /* min rx data delay */ ++ phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, ++ 0x8000 | MICREL_KSZ9021_RGMII_RX_DATA_PAD_SCEW); ++ phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0x0000); ++ ++ /* max rx/tx clock delay, min rx/tx control delay */ ++ phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, ++ 0x8000 | MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW); ++ phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0xf0f0); ++ phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, ++ MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW); ++ } ++ ++ return 0; ++} ++ ++static void mmd_write_reg(struct phy_device *dev, int device, int reg, int val) ++{ ++ phy_write(dev, 0x0d, device); ++ phy_write(dev, 0x0e, reg); ++ phy_write(dev, 0x0d, (1 << 14) | device); ++ phy_write(dev, 0x0e, val); ++} ++ ++static int ksz9031rn_phy_fixup(struct phy_device *dev) ++{ ++ /* ++ * min rx data delay, max rx/tx clock delay, ++ * min rx/tx control delay ++ */ ++ mmd_write_reg(dev, 2, 4, 0); ++ mmd_write_reg(dev, 2, 5, 0); ++ mmd_write_reg(dev, 2, 8, 0x003ff); ++ ++ return 0; ++} ++ ++/* ++ * fixup for PLX PEX8909 bridge to configure GPIO1-7 as output High ++ * as they are used for slots1-7 PERST# ++ */ ++static void ventana_pciesw_early_fixup(struct pci_dev *dev) ++{ ++ u32 dw; ++ ++ if (!of_machine_is_compatible("gw,ventana")) ++ return; ++ ++ if (dev->devfn != 0) ++ return; ++ ++ pci_read_config_dword(dev, 0x62c, &dw); ++ dw |= 0xaaa8; // GPIO1-7 outputs ++ pci_write_config_dword(dev, 0x62c, dw); ++ ++ pci_read_config_dword(dev, 0x644, &dw); ++ dw |= 0xfe; // GPIO1-7 output high ++ pci_write_config_dword(dev, 0x644, dw); ++ ++ msleep(100); ++} ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8609, ventana_pciesw_early_fixup); ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8606, ventana_pciesw_early_fixup); ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8604, ventana_pciesw_early_fixup); ++ ++static int ar8031_phy_fixup(struct phy_device *dev) ++{ ++ u16 val; ++ ++ /* To enable AR8031 output a 125MHz clk from CLK_25M */ ++ phy_write(dev, 0xd, 0x7); ++ phy_write(dev, 0xe, 0x8016); ++ phy_write(dev, 0xd, 0x4007); ++ ++ val = phy_read(dev, 0xe); ++ val &= 0xffe3; ++ val |= 0x18; ++ phy_write(dev, 0xe, val); ++ ++ /* introduce tx clock delay */ ++ phy_write(dev, 0x1d, 0x5); ++ val = phy_read(dev, 0x1e); ++ val |= 0x0100; ++ phy_write(dev, 0x1e, val); ++ ++ return 0; ++} ++ ++#define PHY_ID_AR8031 0x004dd074 ++ ++static int ar8035_phy_fixup(struct phy_device *dev) ++{ ++ u16 val; ++ ++ /* Ar803x phy SmartEEE feature cause link status generates glitch, ++ * which cause ethernet link down/up issue, so disable SmartEEE ++ */ ++ phy_write(dev, 0xd, 0x3); ++ phy_write(dev, 0xe, 0x805d); ++ phy_write(dev, 0xd, 0x4003); ++ ++ val = phy_read(dev, 0xe); ++ phy_write(dev, 0xe, val & ~(1 << 8)); ++ ++ /* ++ * Enable 125MHz clock from CLK_25M on the AR8031. This ++ * is fed in to the IMX6 on the ENET_REF_CLK (V22) pad. ++ * Also, introduce a tx clock delay. ++ * ++ * This is the same as is the AR8031 fixup. ++ */ ++ ar8031_phy_fixup(dev); ++ ++ /*check phy power*/ ++ val = phy_read(dev, 0x0); ++ if (val & BMCR_PDOWN) ++ phy_write(dev, 0x0, val & ~BMCR_PDOWN); ++ ++ return 0; ++} ++ ++#define PHY_ID_AR8035 0x004dd072 ++ ++static void __init imx6q_enet_phy_init(void) ++{ ++ if (IS_BUILTIN(CONFIG_PHYLIB)) { ++ phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK, ++ ksz9021rn_phy_fixup); ++ phy_register_fixup_for_uid(PHY_ID_KSZ9031, MICREL_PHY_ID_MASK, ++ ksz9031rn_phy_fixup); ++ phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff, ++ ar8031_phy_fixup); ++ phy_register_fixup_for_uid(PHY_ID_AR8035, 0xffffffef, ++ ar8035_phy_fixup); ++ } ++} ++ ++static void __init imx6q_1588_init(void) ++{ ++ struct regmap *gpr; ++ ++ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); ++ if (!IS_ERR(gpr)) ++ regmap_update_bits(gpr, IOMUXC_GPR1, ++ IMX6Q_GPR1_ENET_CLK_SEL_MASK, ++ IMX6Q_GPR1_ENET_CLK_SEL_ANATOP); ++ else ++ pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n"); ++ ++} ++ ++static void __init imx6q_csi_mux_init(void) ++{ ++ /* ++ * MX6Q SabreSD board: ++ * IPU1 CSI0 connects to parallel interface. ++ * Set GPR1 bit 19 to 0x1. ++ * ++ * MX6DL SabreSD board: ++ * IPU1 CSI0 connects to parallel interface. ++ * Set GPR13 bit 0-2 to 0x4. ++ * IPU1 CSI1 connects to MIPI CSI2 virtual channel 1. ++ * Set GPR13 bit 3-5 to 0x1. ++ */ ++ struct regmap *gpr; ++ ++ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); ++ if (!IS_ERR(gpr)) { ++ if (of_machine_is_compatible("fsl,imx6q-sabresd") || ++ of_machine_is_compatible("fsl,imx6q-sabreauto")) ++ regmap_update_bits(gpr, IOMUXC_GPR1, 1 << 19, 1 << 19); ++ else if (of_machine_is_compatible("fsl,imx6dl-sabresd") || ++ of_machine_is_compatible("fsl,imx6dl-sabreauto")) ++ regmap_update_bits(gpr, IOMUXC_GPR13, 0x3F, 0x0C); ++ } else { ++ pr_err("%s(): failed to find fsl,imx6q-iomux-gpr regmap\n", ++ __func__); ++ } ++} ++ ++#define OCOTP_MACn(n) (0x00000620 + (n) * 0x10) ++void __init imx6_enet_mac_init(const char *compatible) ++{ ++ struct device_node *enet_np; ++ struct property *newmac; ++ u32 macaddr_low, macaddr_high; ++ u8 *macaddr; ++ int ret; ++ ++ enet_np = of_find_compatible_node(NULL, NULL, compatible); ++ if (!enet_np) ++ return; ++ ++ if (of_get_mac_address(enet_np)) ++ goto put_enet_node; ++ ++ ret = fsl_otp_readl(OCOTP_MACn(0), &macaddr_high); ++ ret = fsl_otp_readl(OCOTP_MACn(1), &macaddr_low); ++ ++ newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL); ++ if (!newmac) ++ goto put_enet_node; ++ ++ newmac->value = newmac + 1; ++ newmac->length = 6; ++ newmac->name = kstrdup("local-mac-address", GFP_KERNEL); ++ if (!newmac->name) { ++ kfree(newmac); ++ goto put_enet_node; ++ } ++ ++ macaddr = newmac->value; ++ macaddr[5] = macaddr_high & 0xff; ++ macaddr[4] = (macaddr_high >> 8) & 0xff; ++ macaddr[3] = (macaddr_high >> 16) & 0xff; ++ macaddr[2] = (macaddr_high >> 24) & 0xff; ++ macaddr[1] = macaddr_low & 0xff; ++ macaddr[0] = (macaddr_low >> 8) & 0xff; ++ ++ of_update_property(enet_np, newmac); ++ ++put_enet_node: ++ of_node_put(enet_np); ++} ++ ++static inline void imx6q_enet_init(void) ++{ ++ imx6_enet_mac_init("fsl,imx6q-fec"); ++ imx6q_enet_phy_init(); ++ imx6q_1588_init(); ++} ++ ++static void __init imx6q_init_machine(void) ++{ ++ struct device *parent; ++ ++ imx_print_silicon_rev(cpu_is_imx6dl() ? "i.MX6DL" : "i.MX6Q", ++ imx_get_soc_revision()); ++ ++ mxc_arch_reset_init_dt(); ++ ++ parent = imx_soc_device_init(); ++ if (parent == NULL) ++ pr_warn("failed to initialize soc device\n"); ++ ++ of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); ++ ++ imx6q_enet_init(); ++ imx_anatop_init(); ++ cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init(); ++ imx6q_csi_mux_init(); ++} ++ ++#define OCOTP_CFG3 0x440 ++#define OCOTP_CFG3_SPEED_SHIFT 16 ++#define OCOTP_CFG3_SPEED_1P2GHZ 0x3 ++#define OCOTP_CFG3_SPEED_1GHZ 0x2 ++#define OCOTP_CFG3_SPEED_850MHZ 0x1 ++#define OCOTP_CFG3_SPEED_800MHZ 0x0 ++ ++static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev) ++{ ++ u32 val; ++ int ret; ++ ++ ret = fsl_otp_readl(OCOTP_CFG3, &val); ++ if (ret) { ++ pr_warn("failed to read ocotp\n"); ++ return; ++ } ++ ++ /* ++ * SPEED_GRADING[1:0] defines the max speed of ARM: ++ * 2b'11: 1200000000Hz; -- i.MX6Q only. ++ * 2b'10: 1000000000Hz; ++ * 2b'01: 850000000Hz; -- i.MX6Q Only, exclusive with 1GHz. ++ * 2b'00: 800000000Hz; ++ * We need to set the max speed of ARM according to fuse map. ++ */ ++ ++ val >>= OCOTP_CFG3_SPEED_SHIFT; ++ if (cpu_is_imx6q()) { ++ if ((val & 0x3) < OCOTP_CFG3_SPEED_1P2GHZ) ++ if (dev_pm_opp_disable(cpu_dev, 1200000000)) ++ pr_warn("failed to disable 1.2 GHz OPP\n"); ++ } ++ if ((val & 0x3) < OCOTP_CFG3_SPEED_1GHZ) ++ if (dev_pm_opp_disable(cpu_dev, 996000000)) ++ pr_warn("failed to disable 1 GHz OPP\n"); ++ if (cpu_is_imx6q()) { ++ if ((val & 0x3) < OCOTP_CFG3_SPEED_850MHZ || ++ (val & 0x3) == OCOTP_CFG3_SPEED_1GHZ) ++ if (dev_pm_opp_disable(cpu_dev, 852000000)) ++ pr_warn("failed to disable 850 MHz OPP\n"); ++ } ++} ++ ++static void __init imx6q_opp_init(void) ++{ ++ struct device_node *np; ++ struct device *cpu_dev = get_cpu_device(0); ++ ++ if (!cpu_dev) { ++ pr_warn("failed to get cpu0 device\n"); ++ return; ++ } ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) { ++ pr_warn("failed to find cpu0 node\n"); ++ return; ++ } ++ ++ if (of_init_opp_table(cpu_dev)) { ++ pr_warn("failed to init OPP table\n"); ++ goto put_node; ++ } ++ ++ imx6q_opp_check_speed_grading(cpu_dev); ++ ++put_node: ++ of_node_put(np); ++} ++ ++#define ESAI_AUDIO_MCLK 24576000 ++ ++static void __init imx6q_audio_lvds2_init(void) ++{ ++ struct clk *pll4_sel, *lvds2_in, *pll4_audio_div, *esai; ++ ++ pll4_audio_div = clk_get_sys(NULL, "pll4_audio_div"); ++ pll4_sel = clk_get_sys(NULL, "pll4_sel"); ++ lvds2_in = clk_get_sys(NULL, "lvds2_in"); ++ esai = clk_get_sys(NULL, "esai"); ++ if (IS_ERR(pll4_audio_div) || IS_ERR(pll4_sel) || ++ IS_ERR(lvds2_in) || IS_ERR(esai)) ++ return; ++ ++ if (clk_get_rate(lvds2_in) != ESAI_AUDIO_MCLK) ++ return; ++ ++ clk_set_parent(pll4_sel, lvds2_in); ++ clk_set_rate(pll4_audio_div, 786432000); ++ clk_set_rate(esai, ESAI_AUDIO_MCLK); ++} ++ ++static struct platform_device imx6q_cpufreq_pdev = { ++ .name = "imx6-cpufreq", ++}; ++ ++static void __init imx6q_init_late(void) ++{ ++ struct regmap *gpr; ++ ++ /* ++ * Need to force IOMUXC irq pending to meet CCM low power mode ++ * restriction, this is recommended by hardware team. ++ */ ++ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); ++ if (!IS_ERR(gpr)) ++ regmap_update_bits(gpr, IOMUXC_GPR1, ++ IMX6Q_GPR1_GINT_MASK, ++ IMX6Q_GPR1_GINT_ASSERT); ++ ++ /* ++ * WAIT mode is broken on TO 1.0 and 1.1, so there is no point ++ * to run cpuidle on them. ++ */ ++ if ((cpu_is_imx6q() && imx_get_soc_revision() > IMX_CHIP_REVISION_1_1) ++ || (cpu_is_imx6dl() && imx_get_soc_revision() > ++ IMX_CHIP_REVISION_1_0)) ++ imx6q_cpuidle_init(); ++ ++ if (IS_ENABLED(CONFIG_ARM_IMX6_CPUFREQ)) { ++ imx6q_opp_init(); ++ platform_device_register(&imx6q_cpufreq_pdev); ++ } ++ ++ if (of_machine_is_compatible("fsl,imx6q-sabreauto") ++ || of_machine_is_compatible("fsl,imx6dl-sabreauto")) { ++ imx6q_audio_lvds2_init(); ++ } ++} ++ ++static void __init imx6q_map_io(void) ++{ ++ debug_ll_io_init(); ++ imx_scu_map_io(); ++} ++ ++static void __init imx6q_init_irq(void) ++{ ++ imx_init_revision_from_anatop(); ++ imx_init_l2cache(); ++ imx_src_init(); ++ imx_gpc_init(); ++ irqchip_init(); ++} ++ ++static const char *imx6q_dt_compat[] __initconst = { ++ "fsl,imx6dl", ++ "fsl,imx6q", ++ NULL, ++}; ++ ++DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)") ++ /* ++ * i.MX6Q/DL maps system memory at 0x10000000 (offset 256MiB), and ++ * GPU has a limit on physical address that it accesses, which must ++ * be below 2GiB. ++ */ ++ .dma_zone_size = (SZ_2G - SZ_256M), ++ .smp = smp_ops(imx_smp_ops), ++ .map_io = imx6q_map_io, ++ .init_irq = imx6q_init_irq, ++ .init_machine = imx6q_init_machine, ++ .init_late = imx6q_init_late, ++ .dt_compat = imx6q_dt_compat, ++ .restart = mxc_restart, ++MACHINE_END +diff -Nur linux-3.14.36/arch/arm/mach-imx/mach-imx6sl.c linux-openelec/arch/arm/mach-imx/mach-imx6sl.c +--- linux-3.14.36/arch/arm/mach-imx/mach-imx6sl.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/mach-imx6sl.c 2015-05-06 12:05:43.000000000 -0500 +@@ -17,8 +17,9 @@ + #include + + #include "common.h" ++#include "cpuidle.h" + +-static void __init imx6sl_fec_init(void) ++static void __init imx6sl_fec_clk_init(void) + { + struct regmap *gpr; + +@@ -34,8 +35,17 @@ + } + } + ++static inline void imx6sl_fec_init(void) ++{ ++ imx6sl_fec_clk_init(); ++ imx6_enet_mac_init("fsl,imx6sl-fec"); ++} ++ + static void __init imx6sl_init_late(void) + { ++ /* Init CPUIDLE */ ++ imx6sl_cpuidle_init(); ++ + /* imx6sl reuses imx6q cpufreq driver */ + if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) + platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); +@@ -55,8 +65,7 @@ + + imx6sl_fec_init(); + imx_anatop_init(); +- /* Reuse imx6q pm code */ +- imx6q_pm_init(); ++ imx6sl_pm_init(); + } + + static void __init imx6sl_init_irq(void) +diff -Nur linux-3.14.36/arch/arm/mach-imx/mach-vf610.c linux-openelec/arch/arm/mach-imx/mach-vf610.c +--- linux-3.14.36/arch/arm/mach-imx/mach-vf610.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/mach-vf610.c 2015-05-06 12:05:43.000000000 -0500 +@@ -22,7 +22,7 @@ + + static void __init vf610_init_irq(void) + { +- l2x0_of_init(0, ~0UL); ++ l2x0_of_init(0, ~0); + irqchip_init(); + } + +diff -Nur linux-3.14.36/arch/arm/mach-imx/Makefile linux-openelec/arch/arm/mach-imx/Makefile +--- linux-3.14.36/arch/arm/mach-imx/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -30,6 +30,7 @@ + ifeq ($(CONFIG_CPU_IDLE),y) + obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o + obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o ++obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o + endif + + ifdef CONFIG_SND_IMX_SOC +@@ -101,9 +102,18 @@ + obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o + obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o + +-obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o +-# i.MX6SL reuses i.MX6Q code +-obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o ++AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a ++obj-$(CONFIG_PM) += suspend-imx6.o pm-imx6.o headsmp.o ++ ++obj-y += busfreq-imx6.o ++ifeq ($(CONFIG_ARM_IMX6_CPUFREQ),y) ++obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o ++obj-$(CONFIG_SOC_IMX6SL) += lpddr2_freq_imx6.o busfreq_lpddr2.o ++endif ++ifeq ($(CONFIG_CPU_IDLE), y) ++obj-$(CONFIG_SOC_IMX6SL) += imx6sl_wfi.o ++endif ++ + + # i.MX5 based machines + obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o +diff -Nur linux-3.14.36/arch/arm/mach-imx/mx6.h linux-openelec/arch/arm/mach-imx/mx6.h +--- linux-3.14.36/arch/arm/mach-imx/mx6.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/mx6.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,35 @@ ++/* ++ * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __ASM_ARCH_MXC_IOMAP_H__ ++#define __ASM_ARCH_MXC_IOMAP_H__ ++ ++#define MX6Q_IO_P2V(x) IMX_IO_P2V(x) ++#define MX6Q_IO_ADDRESS(x) IOMEM(MX6Q_IO_P2V(x)) ++ ++#define MX6Q_L2_BASE_ADDR 0x00a02000 ++#define MX6Q_L2_SIZE 0x1000 ++#define MX6Q_IOMUXC_BASE_ADDR 0x020e0000 ++#define MX6Q_IOMUXC_SIZE 0x4000 ++#define MX6Q_SRC_BASE_ADDR 0x020d8000 ++#define MX6Q_SRC_SIZE 0x4000 ++#define MX6Q_CCM_BASE_ADDR 0x020c4000 ++#define MX6Q_CCM_SIZE 0x4000 ++#define MX6Q_ANATOP_BASE_ADDR 0x020c8000 ++#define MX6Q_ANATOP_SIZE 0x1000 ++#define MX6Q_GPC_BASE_ADDR 0x020dc000 ++#define MX6Q_GPC_SIZE 0x4000 ++#define MX6Q_MMDC_P0_BASE_ADDR 0x021b0000 ++#define MX6Q_MMDC_P0_SIZE 0x4000 ++#define MX6Q_MMDC_P1_BASE_ADDR 0x021b4000 ++#define MX6Q_MMDC_P1_SIZE 0x4000 ++ ++#define MX6_SUSPEND_IRAM_SIZE 0x1000 ++#endif +diff -Nur linux-3.14.36/arch/arm/mach-imx/mxc.h linux-openelec/arch/arm/mach-imx/mxc.h +--- linux-3.14.36/arch/arm/mach-imx/mxc.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/mxc.h 2015-05-06 12:05:43.000000000 -0500 +@@ -42,6 +42,8 @@ + #define IMX_CHIP_REVISION_1_1 0x11 + #define IMX_CHIP_REVISION_1_2 0x12 + #define IMX_CHIP_REVISION_1_3 0x13 ++#define IMX_CHIP_REVISION_1_4 0x14 ++#define IMX_CHIP_REVISION_1_5 0x15 + #define IMX_CHIP_REVISION_2_0 0x20 + #define IMX_CHIP_REVISION_2_1 0x21 + #define IMX_CHIP_REVISION_2_2 0x22 +@@ -177,6 +179,7 @@ + extern struct cpu_op *(*get_cpu_op)(int *op); + #endif + ++#define cpu_is_imx6() (cpu_is_imx6q() || cpu_is_imx6dl() || cpu_is_imx6sl()) + #define cpu_is_mx3() (cpu_is_mx31() || cpu_is_mx35()) + #define cpu_is_mx2() (cpu_is_mx21() || cpu_is_mx27()) + +diff -Nur linux-3.14.36/arch/arm/mach-imx/pm-imx6.c linux-openelec/arch/arm/mach-imx/pm-imx6.c +--- linux-3.14.36/arch/arm/mach-imx/pm-imx6.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/pm-imx6.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,580 @@ ++/* ++ * Copyright 2011-2014 Freescale Semiconductor, Inc. ++ * Copyright 2011 Linaro Ltd. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "common.h" ++#include "hardware.h" ++ ++#define CCR 0x0 ++#define BM_CCR_WB_COUNT (0x7 << 16) ++#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21) ++#define BM_CCR_RBC_EN (0x1 << 27) ++ ++#define CLPCR 0x54 ++#define BP_CLPCR_LPM 0 ++#define BM_CLPCR_LPM (0x3 << 0) ++#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2) ++#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) ++#define BM_CLPCR_SBYOS (0x1 << 6) ++#define BM_CLPCR_DIS_REF_OSC (0x1 << 7) ++#define BM_CLPCR_VSTBY (0x1 << 8) ++#define BP_CLPCR_STBY_COUNT 9 ++#define BM_CLPCR_STBY_COUNT (0x3 << 9) ++#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11) ++#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16) ++#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17) ++#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19) ++#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21) ++#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22) ++#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23) ++#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24) ++#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25) ++#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26) ++#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27) ++ ++#define CGPR 0x64 ++#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17) ++ ++#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000 ++#define MX6_MAX_MMDC_IO_NUM 33 ++ ++static void __iomem *ccm_base; ++static void __iomem *suspend_ocram_base; ++static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase); ++ ++/* ++ * suspend ocram space layout: ++ * ======================== high address ====================== ++ * . ++ * . ++ * . ++ * ^ ++ * ^ ++ * ^ ++ * imx6_suspend code ++ * PM_INFO structure(imx6_cpu_pm_info) ++ * ======================== low address ======================= ++ */ ++ ++struct imx6_pm_base { ++ phys_addr_t pbase; ++ void __iomem *vbase; ++}; ++ ++struct imx6_pm_socdata { ++ u32 cpu_type; ++ const char *mmdc_compat; ++ const char *src_compat; ++ const char *iomuxc_compat; ++ const char *gpc_compat; ++ const u32 mmdc_io_num; ++ const u32 *mmdc_io_offset; ++}; ++ ++static const u32 imx6q_mmdc_io_offset[] __initconst = { ++ 0x5ac, 0x5b4, 0x528, 0x520, /* DQM0 ~ DQM3 */ ++ 0x514, 0x510, 0x5bc, 0x5c4, /* DQM4 ~ DQM7 */ ++ 0x56c, 0x578, 0x588, 0x594, /* CAS, RAS, SDCLK_0, SDCLK_1 */ ++ 0x5a8, 0x5b0, 0x524, 0x51c, /* SDQS0 ~ SDQS3 */ ++ 0x518, 0x50c, 0x5b8, 0x5c0, /* SDQS4 ~ SDQS7 */ ++ 0x784, 0x788, 0x794, 0x79c, /* GPR_B0DS ~ GPR_B3DS */ ++ 0x7a0, 0x7a4, 0x7a8, 0x748, /* GPR_B4DS ~ GPR_B7DS */ ++ 0x59c, 0x5a0, 0x750, 0x774, /* SODT0, SODT1, MODE_CTL, MODE */ ++ 0x74c, /* GPR_ADDS */ ++}; ++ ++static const struct imx6_pm_socdata imx6q_pm_data __initconst = { ++ .cpu_type = MXC_CPU_IMX6Q, ++ .mmdc_compat = "fsl,imx6q-mmdc", ++ .src_compat = "fsl,imx6q-src", ++ .iomuxc_compat = "fsl,imx6q-iomuxc", ++ .gpc_compat = "fsl,imx6q-gpc", ++ .mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset), ++ .mmdc_io_offset = imx6q_mmdc_io_offset, ++}; ++ ++/* ++ * This structure is for passing necessary data for low level ocram ++ * suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct ++ * definition is changed, the offset definition in ++ * arch/arm/mach-imx/suspend-imx6.S must be also changed accordingly, ++ * otherwise, the suspend to ocram function will be broken! ++ */ ++struct imx6_cpu_pm_info { ++ phys_addr_t pbase; /* The physical address of pm_info. */ ++ phys_addr_t resume_addr; /* The physical resume address for asm code */ ++ u32 cpu_type; ++ u32 pm_info_size; /* Size of pm_info. */ ++ struct imx6_pm_base mmdc_base; ++ struct imx6_pm_base src_base; ++ struct imx6_pm_base iomuxc_base; ++ struct imx6_pm_base ccm_base; ++ struct imx6_pm_base gpc_base; ++ struct imx6_pm_base l2_base; ++ u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */ ++ u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2]; /* To save offset and value */ ++} __aligned(8); ++ ++void imx6q_set_cache_lpm_in_wait(bool enable) ++{ ++ if ((cpu_is_imx6q() && imx_get_soc_revision() > ++ IMX_CHIP_REVISION_1_1) || ++ (cpu_is_imx6dl() && imx_get_soc_revision() > ++ IMX_CHIP_REVISION_1_0)) { ++ u32 val; ++ ++ val = readl_relaxed(ccm_base + CGPR); ++ if (enable) ++ val |= BM_CGPR_INT_MEM_CLK_LPM; ++ else ++ val &= ~BM_CGPR_INT_MEM_CLK_LPM; ++ writel_relaxed(val, ccm_base + CGPR); ++ } ++} ++ ++static void imx6q_enable_rbc(bool enable) ++{ ++ u32 val; ++ ++ /* ++ * need to mask all interrupts in GPC before ++ * operating RBC configurations ++ */ ++ imx_gpc_mask_all(); ++ ++ /* configure RBC enable bit */ ++ val = readl_relaxed(ccm_base + CCR); ++ val &= ~BM_CCR_RBC_EN; ++ val |= enable ? BM_CCR_RBC_EN : 0; ++ writel_relaxed(val, ccm_base + CCR); ++ ++ /* configure RBC count */ ++ val = readl_relaxed(ccm_base + CCR); ++ val &= ~BM_CCR_RBC_BYPASS_COUNT; ++ val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0; ++ writel(val, ccm_base + CCR); ++ ++ /* ++ * need to delay at least 2 cycles of CKIL(32K) ++ * due to hardware design requirement, which is ++ * ~61us, here we use 65us for safe ++ */ ++ udelay(65); ++ ++ /* restore GPC interrupt mask settings */ ++ imx_gpc_restore_all(); ++} ++ ++static void imx6q_enable_wb(bool enable) ++{ ++ u32 val; ++ ++ /* configure well bias enable bit */ ++ val = readl_relaxed(ccm_base + CLPCR); ++ val &= ~BM_CLPCR_WB_PER_AT_LPM; ++ val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0; ++ writel_relaxed(val, ccm_base + CLPCR); ++ ++ /* configure well bias count */ ++ val = readl_relaxed(ccm_base + CCR); ++ val &= ~BM_CCR_WB_COUNT; ++ val |= enable ? BM_CCR_WB_COUNT : 0; ++ writel_relaxed(val, ccm_base + CCR); ++} ++ ++int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) ++{ ++ struct irq_desc *iomuxc_irq_desc; ++ u32 val = readl_relaxed(ccm_base + CLPCR); ++ ++ val &= ~BM_CLPCR_LPM; ++ switch (mode) { ++ case WAIT_CLOCKED: ++ break; ++ case WAIT_UNCLOCKED: ++ val |= 0x1 << BP_CLPCR_LPM; ++ val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; ++ val &= ~BM_CLPCR_VSTBY; ++ val &= ~BM_CLPCR_SBYOS; ++ if (cpu_is_imx6sl()) ++ val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; ++ else ++ val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; ++ break; ++ case STOP_POWER_ON: ++ val |= 0x2 << BP_CLPCR_LPM; ++ val &= ~BM_CLPCR_VSTBY; ++ val &= ~BM_CLPCR_SBYOS; ++ val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; ++ break; ++ case WAIT_UNCLOCKED_POWER_OFF: ++ val |= 0x1 << BP_CLPCR_LPM; ++ val &= ~BM_CLPCR_VSTBY; ++ val &= ~BM_CLPCR_SBYOS; ++ break; ++ case STOP_POWER_OFF: ++ val |= 0x2 << BP_CLPCR_LPM; ++ val |= 0x3 << BP_CLPCR_STBY_COUNT; ++ val |= BM_CLPCR_VSTBY; ++ val |= BM_CLPCR_SBYOS; ++ if (cpu_is_imx6sl()) { ++ val |= BM_CLPCR_BYPASS_PMIC_READY; ++ val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; ++ } else { ++ val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * ERR007265: CCM: When improper low-power sequence is used, ++ * the SoC enters low power mode before the ARM core executes WFI. ++ * ++ * Software workaround: ++ * 1) Software should trigger IRQ #32 (IOMUX) to be always pending ++ * by setting IOMUX_GPR1_GINT. ++ * 2) Software should then unmask IRQ #32 in GPC before setting CCM ++ * Low-Power mode. ++ * 3) Software should mask IRQ #32 right after CCM Low-Power mode ++ * is set (set bits 0-1 of CCM_CLPCR). ++ */ ++ iomuxc_irq_desc = irq_to_desc(32); ++ imx_gpc_irq_unmask(&iomuxc_irq_desc->irq_data); ++ writel_relaxed(val, ccm_base + CLPCR); ++ imx_gpc_irq_mask(&iomuxc_irq_desc->irq_data); ++ ++ return 0; ++} ++ ++static int imx6q_suspend_finish(unsigned long val) ++{ ++ if (!imx6_suspend_in_ocram_fn) { ++ cpu_do_idle(); ++ } else { ++ /* ++ * call low level suspend function in ocram, ++ * as we need to float DDR IO. ++ */ ++ local_flush_tlb_all(); ++ imx6_suspend_in_ocram_fn(suspend_ocram_base); ++ } ++ ++ return 0; ++} ++ ++static int imx6q_pm_enter(suspend_state_t state) ++{ ++ struct regmap *g; ++ ++ /* ++ * L2 can exit by 'reset' or Inband beacon (from remote EP) ++ * toggling phy_powerdown has same effect as 'inband beacon' ++ * So, toggle bit18 of GPR1, used as a workaround of errata ++ * "PCIe PCIe does not support L2 Power Down" ++ */ ++ if (IS_ENABLED(CONFIG_PCI_IMX6)) { ++ g = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); ++ if (IS_ERR(g)) { ++ pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n"); ++ return PTR_ERR(g); ++ } ++ regmap_update_bits(g, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD, ++ IMX6Q_GPR1_PCIE_TEST_PD); ++ } ++ ++ switch (state) { ++ case PM_SUSPEND_STANDBY: ++ imx6q_set_lpm(STOP_POWER_ON); ++ imx6q_set_cache_lpm_in_wait(true); ++ imx_gpc_pre_suspend(false); ++ if (cpu_is_imx6sl()) ++ imx6sl_set_wait_clk(true); ++ /* Zzz ... */ ++ cpu_do_idle(); ++ if (cpu_is_imx6sl()) ++ imx6sl_set_wait_clk(false); ++ imx_gpc_post_resume(); ++ imx6q_set_lpm(WAIT_CLOCKED); ++ break; ++ case PM_SUSPEND_MEM: ++ imx6q_set_cache_lpm_in_wait(false); ++ imx6q_set_lpm(STOP_POWER_OFF); ++ imx6q_enable_wb(true); ++ /* ++ * For suspend into ocram, asm code already take care of ++ * RBC setting, so we do NOT need to do that here. ++ */ ++ if (!imx6_suspend_in_ocram_fn) ++ imx6q_enable_rbc(true); ++ imx_gpc_pre_suspend(true); ++ imx_anatop_pre_suspend(); ++ imx_set_cpu_jump(0, v7_cpu_resume); ++ /* Zzz ... */ ++ cpu_suspend(0, imx6q_suspend_finish); ++ if (cpu_is_imx6q() || cpu_is_imx6dl()) ++ imx_smp_prepare(); ++ imx_anatop_post_resume(); ++ imx_gpc_post_resume(); ++ imx6q_enable_wb(false); ++ imx6q_set_lpm(WAIT_CLOCKED); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * L2 can exit by 'reset' or Inband beacon (from remote EP) ++ * toggling phy_powerdown has same effect as 'inband beacon' ++ * So, toggle bit18 of GPR1, used as a workaround of errata ++ * "PCIe PCIe does not support L2 Power Down" ++ */ ++ if (IS_ENABLED(CONFIG_PCI_IMX6)) { ++ regmap_update_bits(g, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD, ++ !IMX6Q_GPR1_PCIE_TEST_PD); ++ } ++ ++ return 0; ++} ++ ++static int imx6q_pm_valid(suspend_state_t state) ++{ ++ return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM); ++} ++ ++static const struct platform_suspend_ops imx6q_pm_ops = { ++ .enter = imx6q_pm_enter, ++ .valid = imx6q_pm_valid, ++}; ++ ++void __init imx6q_pm_set_ccm_base(void __iomem *base) ++{ ++ ccm_base = base; ++} ++ ++static int __init imx6_pm_get_base(struct imx6_pm_base *base, ++ const char *compat) ++{ ++ struct device_node *node; ++ struct resource res; ++ int ret = 0; ++ ++ node = of_find_compatible_node(NULL, NULL, compat); ++ if (!node) { ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ ret = of_address_to_resource(node, 0, &res); ++ if (ret) ++ goto put_node; ++ ++ base->pbase = res.start; ++ base->vbase = ioremap(res.start, resource_size(&res)); ++ if (!base->vbase) ++ ret = -ENOMEM; ++ ++put_node: ++ of_node_put(node); ++out: ++ return ret; ++} ++ ++static int __init imx6q_ocram_suspend_init(const struct imx6_pm_socdata ++ *socdata) ++{ ++ phys_addr_t ocram_pbase; ++ struct device_node *node; ++ struct platform_device *pdev; ++ struct imx6_cpu_pm_info *pm_info; ++ struct gen_pool *ocram_pool; ++ unsigned long ocram_base; ++ int i, ret = 0; ++ const u32 *mmdc_offset_array; ++ ++ if (!socdata) { ++ pr_warn("%s: invalid argument!\n", __func__); ++ return -EINVAL; ++ } ++ ++ node = of_find_compatible_node(NULL, NULL, "mmio-sram"); ++ if (!node) { ++ pr_warn("%s: failed to find ocram node!\n", __func__); ++ return -ENODEV; ++ } ++ ++ pdev = of_find_device_by_node(node); ++ if (!pdev) { ++ pr_warn("%s: failed to find ocram device!\n", __func__); ++ ret = -ENODEV; ++ goto put_node; ++ } ++ ++ ocram_pool = dev_get_gen_pool(&pdev->dev); ++ if (!ocram_pool) { ++ pr_warn("%s: ocram pool unavailable!\n", __func__); ++ ret = -ENODEV; ++ goto put_node; ++ } ++ ++ ocram_base = gen_pool_alloc(ocram_pool, MX6Q_SUSPEND_OCRAM_SIZE); ++ if (!ocram_base) { ++ pr_warn("%s: unable to alloc ocram!\n", __func__); ++ ret = -ENOMEM; ++ goto put_node; ++ } ++ ++ ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base); ++ ++ suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, ++ MX6Q_SUSPEND_OCRAM_SIZE, false); ++ ++ pm_info = suspend_ocram_base; ++ pm_info->pbase = ocram_pbase; ++ pm_info->resume_addr = virt_to_phys(v7_cpu_resume); ++ pm_info->pm_info_size = sizeof(*pm_info); ++ ++ /* ++ * ccm physical address is not used by asm code currently, ++ * so get ccm virtual address directly, as we already have ++ * it from ccm driver. ++ */ ++ pm_info->ccm_base.vbase = ccm_base; ++ ++ ret = imx6_pm_get_base(&pm_info->mmdc_base, socdata->mmdc_compat); ++ if (ret) { ++ pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret); ++ goto put_node; ++ } ++ ++ ret = imx6_pm_get_base(&pm_info->src_base, socdata->src_compat); ++ if (ret) { ++ pr_warn("%s: failed to get src base %d!\n", __func__, ret); ++ goto src_map_failed; ++ } ++ ++ ret = imx6_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat); ++ if (ret) { ++ pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret); ++ goto iomuxc_map_failed; ++ } ++ ++ ret = imx6_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat); ++ if (ret) { ++ pr_warn("%s: failed to get gpc base %d!\n", __func__, ret); ++ goto gpc_map_failed; ++ } ++ ++ ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache"); ++ if (ret) { ++ pr_warn("%s: failed to get pl310-cache base %d!\n", ++ __func__, ret); ++ goto pl310_cache_map_failed; ++ } ++ ++ pm_info->cpu_type = socdata->cpu_type; ++ pm_info->mmdc_io_num = socdata->mmdc_io_num; ++ mmdc_offset_array = socdata->mmdc_io_offset; ++ ++ for (i = 0; i < pm_info->mmdc_io_num; i++) { ++ pm_info->mmdc_io_val[i][0] = ++ mmdc_offset_array[i]; ++ pm_info->mmdc_io_val[i][1] = ++ readl_relaxed(pm_info->iomuxc_base.vbase + ++ mmdc_offset_array[i]); ++ } ++ ++ imx6_suspend_in_ocram_fn = fncpy( ++ suspend_ocram_base + sizeof(*pm_info), ++ &imx6_suspend, ++ MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info)); ++ ++ goto put_node; ++ ++pl310_cache_map_failed: ++ iounmap(&pm_info->gpc_base.vbase); ++gpc_map_failed: ++ iounmap(&pm_info->iomuxc_base.vbase); ++iomuxc_map_failed: ++ iounmap(&pm_info->src_base.vbase); ++src_map_failed: ++ iounmap(&pm_info->mmdc_base.vbase); ++put_node: ++ of_node_put(node); ++ ++ return ret; ++} ++ ++static void __init imx6_pm_common_init(const struct imx6_pm_socdata ++ *socdata) ++{ ++ struct regmap *gpr; ++ int ret; ++ ++ WARN_ON(!ccm_base); ++ ++ ret = imx6q_ocram_suspend_init(socdata); ++ if (ret) ++ pr_warn("%s: failed to initialize ocram suspend %d!\n", ++ __func__, ret); ++ ++ /* ++ * This is for SW workaround step #1 of ERR007265, see comments ++ * in imx6q_set_lpm for details of this errata. ++ * Force IOMUXC irq pending, so that the interrupt to GPC can be ++ * used to deassert dsm_request signal when the signal gets ++ * asserted unexpectedly. ++ */ ++ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); ++ if (!IS_ERR(gpr)) ++ regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT_MASK, ++ IMX6Q_GPR1_GINT_MASK); ++ ++ ++ suspend_set_ops(&imx6q_pm_ops); ++} ++ ++void __init imx6q_pm_init(void) ++{ ++ imx6_pm_common_init(&imx6q_pm_data); ++} ++ ++void __init imx6dl_pm_init(void) ++{ ++ imx6_pm_common_init(NULL); ++} ++ ++void __init imx6sl_pm_init(void) ++{ ++ imx6_pm_common_init(NULL); ++} +diff -Nur linux-3.14.36/arch/arm/mach-imx/pm-imx6q.c linux-openelec/arch/arm/mach-imx/pm-imx6q.c +--- linux-3.14.36/arch/arm/mach-imx/pm-imx6q.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/pm-imx6q.c 1969-12-31 18:00:00.000000000 -0600 +@@ -1,241 +0,0 @@ +-/* +- * Copyright 2011-2013 Freescale Semiconductor, Inc. +- * Copyright 2011 Linaro Ltd. +- * +- * The code contained herein is licensed under the GNU General Public +- * License. You may obtain a copy of the GNU General Public License +- * Version 2 or later at the following locations: +- * +- * http://www.opensource.org/licenses/gpl-license.html +- * http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "common.h" +-#include "hardware.h" +- +-#define CCR 0x0 +-#define BM_CCR_WB_COUNT (0x7 << 16) +-#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21) +-#define BM_CCR_RBC_EN (0x1 << 27) +- +-#define CLPCR 0x54 +-#define BP_CLPCR_LPM 0 +-#define BM_CLPCR_LPM (0x3 << 0) +-#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2) +-#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) +-#define BM_CLPCR_SBYOS (0x1 << 6) +-#define BM_CLPCR_DIS_REF_OSC (0x1 << 7) +-#define BM_CLPCR_VSTBY (0x1 << 8) +-#define BP_CLPCR_STBY_COUNT 9 +-#define BM_CLPCR_STBY_COUNT (0x3 << 9) +-#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11) +-#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16) +-#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17) +-#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19) +-#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21) +-#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22) +-#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23) +-#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24) +-#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25) +-#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26) +-#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27) +- +-#define CGPR 0x64 +-#define BM_CGPR_CHICKEN_BIT (0x1 << 17) +- +-static void __iomem *ccm_base; +- +-void imx6q_set_chicken_bit(void) +-{ +- u32 val = readl_relaxed(ccm_base + CGPR); +- +- val |= BM_CGPR_CHICKEN_BIT; +- writel_relaxed(val, ccm_base + CGPR); +-} +- +-static void imx6q_enable_rbc(bool enable) +-{ +- u32 val; +- +- /* +- * need to mask all interrupts in GPC before +- * operating RBC configurations +- */ +- imx_gpc_mask_all(); +- +- /* configure RBC enable bit */ +- val = readl_relaxed(ccm_base + CCR); +- val &= ~BM_CCR_RBC_EN; +- val |= enable ? BM_CCR_RBC_EN : 0; +- writel_relaxed(val, ccm_base + CCR); +- +- /* configure RBC count */ +- val = readl_relaxed(ccm_base + CCR); +- val &= ~BM_CCR_RBC_BYPASS_COUNT; +- val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0; +- writel(val, ccm_base + CCR); +- +- /* +- * need to delay at least 2 cycles of CKIL(32K) +- * due to hardware design requirement, which is +- * ~61us, here we use 65us for safe +- */ +- udelay(65); +- +- /* restore GPC interrupt mask settings */ +- imx_gpc_restore_all(); +-} +- +-static void imx6q_enable_wb(bool enable) +-{ +- u32 val; +- +- /* configure well bias enable bit */ +- val = readl_relaxed(ccm_base + CLPCR); +- val &= ~BM_CLPCR_WB_PER_AT_LPM; +- val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0; +- writel_relaxed(val, ccm_base + CLPCR); +- +- /* configure well bias count */ +- val = readl_relaxed(ccm_base + CCR); +- val &= ~BM_CCR_WB_COUNT; +- val |= enable ? BM_CCR_WB_COUNT : 0; +- writel_relaxed(val, ccm_base + CCR); +-} +- +-int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) +-{ +- struct irq_desc *iomuxc_irq_desc; +- u32 val = readl_relaxed(ccm_base + CLPCR); +- +- val &= ~BM_CLPCR_LPM; +- switch (mode) { +- case WAIT_CLOCKED: +- break; +- case WAIT_UNCLOCKED: +- val |= 0x1 << BP_CLPCR_LPM; +- val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; +- break; +- case STOP_POWER_ON: +- val |= 0x2 << BP_CLPCR_LPM; +- break; +- case WAIT_UNCLOCKED_POWER_OFF: +- val |= 0x1 << BP_CLPCR_LPM; +- val &= ~BM_CLPCR_VSTBY; +- val &= ~BM_CLPCR_SBYOS; +- break; +- case STOP_POWER_OFF: +- val |= 0x2 << BP_CLPCR_LPM; +- val |= 0x3 << BP_CLPCR_STBY_COUNT; +- val |= BM_CLPCR_VSTBY; +- val |= BM_CLPCR_SBYOS; +- if (cpu_is_imx6sl()) { +- val |= BM_CLPCR_BYPASS_PMIC_READY; +- val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; +- } else { +- val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; +- } +- break; +- default: +- return -EINVAL; +- } +- +- /* +- * ERR007265: CCM: When improper low-power sequence is used, +- * the SoC enters low power mode before the ARM core executes WFI. +- * +- * Software workaround: +- * 1) Software should trigger IRQ #32 (IOMUX) to be always pending +- * by setting IOMUX_GPR1_GINT. +- * 2) Software should then unmask IRQ #32 in GPC before setting CCM +- * Low-Power mode. +- * 3) Software should mask IRQ #32 right after CCM Low-Power mode +- * is set (set bits 0-1 of CCM_CLPCR). +- */ +- iomuxc_irq_desc = irq_to_desc(32); +- imx_gpc_irq_unmask(&iomuxc_irq_desc->irq_data); +- writel_relaxed(val, ccm_base + CLPCR); +- imx_gpc_irq_mask(&iomuxc_irq_desc->irq_data); +- +- return 0; +-} +- +-static int imx6q_suspend_finish(unsigned long val) +-{ +- cpu_do_idle(); +- return 0; +-} +- +-static int imx6q_pm_enter(suspend_state_t state) +-{ +- switch (state) { +- case PM_SUSPEND_MEM: +- imx6q_set_lpm(STOP_POWER_OFF); +- imx6q_enable_wb(true); +- imx6q_enable_rbc(true); +- imx_gpc_pre_suspend(); +- imx_anatop_pre_suspend(); +- imx_set_cpu_jump(0, v7_cpu_resume); +- /* Zzz ... */ +- cpu_suspend(0, imx6q_suspend_finish); +- if (cpu_is_imx6q() || cpu_is_imx6dl()) +- imx_smp_prepare(); +- imx_anatop_post_resume(); +- imx_gpc_post_resume(); +- imx6q_enable_rbc(false); +- imx6q_enable_wb(false); +- imx6q_set_lpm(WAIT_CLOCKED); +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static const struct platform_suspend_ops imx6q_pm_ops = { +- .enter = imx6q_pm_enter, +- .valid = suspend_valid_only_mem, +-}; +- +-void __init imx6q_pm_set_ccm_base(void __iomem *base) +-{ +- ccm_base = base; +-} +- +-void __init imx6q_pm_init(void) +-{ +- struct regmap *gpr; +- +- WARN_ON(!ccm_base); +- +- /* +- * This is for SW workaround step #1 of ERR007265, see comments +- * in imx6q_set_lpm for details of this errata. +- * Force IOMUXC irq pending, so that the interrupt to GPC can be +- * used to deassert dsm_request signal when the signal gets +- * asserted unexpectedly. +- */ +- gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); +- if (!IS_ERR(gpr)) +- regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT, +- IMX6Q_GPR1_GINT); +- +- +- suspend_set_ops(&imx6q_pm_ops); +-} +diff -Nur linux-3.14.36/arch/arm/mach-imx/suspend-imx6.S linux-openelec/arch/arm/mach-imx/suspend-imx6.S +--- linux-3.14.36/arch/arm/mach-imx/suspend-imx6.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-imx/suspend-imx6.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,306 @@ ++/* ++ * Copyright 2014 Freescale Semiconductor, Inc. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include "hardware.h" ++ ++/* ++ * ==================== low level suspend ==================== ++ * ++ * Better to follow below rules to use ARM registers: ++ * r0: pm_info structure address; ++ * r1 ~ r4: for saving pm_info members; ++ * r5 ~ r10: free registers; ++ * r11: io base address. ++ * ++ * suspend ocram space layout: ++ * ======================== high address ====================== ++ * . ++ * . ++ * . ++ * ^ ++ * ^ ++ * ^ ++ * imx6_suspend code ++ * PM_INFO structure(imx6_cpu_pm_info) ++ * ======================== low address ======================= ++ */ ++ ++/* ++ * Below offsets are based on struct imx6_cpu_pm_info ++ * which defined in arch/arm/mach-imx/pm-imx6q.c, this ++ * structure contains necessary pm info for low level ++ * suspend related code. ++ */ ++#define PM_INFO_PBASE_OFFSET 0x0 ++#define PM_INFO_RESUME_ADDR_OFFSET 0x4 ++#define PM_INFO_CPU_TYPE_OFFSET 0x8 ++#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC ++#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10 ++#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14 ++#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18 ++#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C ++#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20 ++#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24 ++#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28 ++#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C ++#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30 ++#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34 ++#define PM_INFO_MX6Q_L2_P_OFFSET 0x38 ++#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C ++#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40 ++#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44 ++ ++#define MX6Q_SRC_GPR1 0x20 ++#define MX6Q_SRC_GPR2 0x24 ++#define MX6Q_MMDC_MAPSR 0x404 ++#define MX6Q_GPC_IMR1 0x08 ++#define MX6Q_GPC_IMR2 0x0c ++#define MX6Q_GPC_IMR3 0x10 ++#define MX6Q_GPC_IMR4 0x14 ++#define MX6Q_CCM_CCR 0x0 ++ ++ .align 3 ++ ++ .macro sync_l2_cache ++ ++ /* sync L2 cache to drain L2's buffers to DRAM. */ ++#ifdef CONFIG_CACHE_L2X0 ++ ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] ++ mov r6, #0x0 ++ str r6, [r11, #L2X0_CACHE_SYNC] ++1: ++ ldr r6, [r11, #L2X0_CACHE_SYNC] ++ ands r6, r6, #0x1 ++ bne 1b ++#endif ++ ++ .endm ++ ++ .macro resume_mmdc ++ ++ /* restore MMDC IO */ ++ cmp r5, #0x0 ++ ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] ++ ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET] ++ ++ ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] ++ ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET ++ add r7, r7, r0 ++1: ++ ldr r8, [r7], #0x4 ++ ldr r9, [r7], #0x4 ++ str r9, [r11, r8] ++ subs r6, r6, #0x1 ++ bne 1b ++ ++ cmp r5, #0x0 ++ ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] ++ ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] ++ ++ /* let DDR out of self-refresh */ ++ ldr r7, [r11, #MX6Q_MMDC_MAPSR] ++ bic r7, r7, #(1 << 21) ++ str r7, [r11, #MX6Q_MMDC_MAPSR] ++2: ++ ldr r7, [r11, #MX6Q_MMDC_MAPSR] ++ ands r7, r7, #(1 << 25) ++ bne 2b ++ ++ /* enable DDR auto power saving */ ++ ldr r7, [r11, #MX6Q_MMDC_MAPSR] ++ bic r7, r7, #0x1 ++ str r7, [r11, #MX6Q_MMDC_MAPSR] ++ ++ .endm ++ ++ENTRY(imx6_suspend) ++ ldr r1, [r0, #PM_INFO_PBASE_OFFSET] ++ ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET] ++ ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET] ++ ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET] ++ ++ /* ++ * counting the resume address in iram ++ * to set it in SRC register. ++ */ ++ ldr r6, =imx6_suspend ++ ldr r7, =resume ++ sub r7, r7, r6 ++ add r8, r1, r4 ++ add r9, r8, r7 ++ ++ /* ++ * make sure TLB contain the addr we want, ++ * as we will access them after MMDC IO floated. ++ */ ++ ++ ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] ++ ldr r6, [r11, #0x0] ++ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] ++ ldr r6, [r11, #0x0] ++ ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] ++ ldr r6, [r11, #0x0] ++ ++ /* use r11 to store the IO address */ ++ ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET] ++ /* store physical resume addr and pm_info address. */ ++ str r9, [r11, #MX6Q_SRC_GPR1] ++ str r1, [r11, #MX6Q_SRC_GPR2] ++ ++ /* need to sync L2 cache before DSM. */ ++ sync_l2_cache ++ ++ ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] ++ /* ++ * put DDR explicitly into self-refresh and ++ * disable automatic power savings. ++ */ ++ ldr r7, [r11, #MX6Q_MMDC_MAPSR] ++ orr r7, r7, #0x1 ++ str r7, [r11, #MX6Q_MMDC_MAPSR] ++ ++ /* make the DDR explicitly enter self-refresh. */ ++ ldr r7, [r11, #MX6Q_MMDC_MAPSR] ++ orr r7, r7, #(1 << 21) ++ str r7, [r11, #MX6Q_MMDC_MAPSR] ++ ++poll_dvfs_set: ++ ldr r7, [r11, #MX6Q_MMDC_MAPSR] ++ ands r7, r7, #(1 << 25) ++ beq poll_dvfs_set ++ ++ ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] ++ ldr r6, =0x0 ++ ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] ++ ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET ++ add r8, r8, r0 ++set_mmdc_io_lpm: ++ ldr r9, [r8], #0x8 ++ str r6, [r11, r9] ++ subs r7, r7, #0x1 ++ bne set_mmdc_io_lpm ++ ++ /* ++ * mask all GPC interrupts before ++ * enabling the RBC counters to ++ * avoid the counter starting too ++ * early if an interupt is already ++ * pending. ++ */ ++ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] ++ ldr r6, [r11, #MX6Q_GPC_IMR1] ++ ldr r7, [r11, #MX6Q_GPC_IMR2] ++ ldr r8, [r11, #MX6Q_GPC_IMR3] ++ ldr r9, [r11, #MX6Q_GPC_IMR4] ++ ++ ldr r10, =0xffffffff ++ str r10, [r11, #MX6Q_GPC_IMR1] ++ str r10, [r11, #MX6Q_GPC_IMR2] ++ str r10, [r11, #MX6Q_GPC_IMR3] ++ str r10, [r11, #MX6Q_GPC_IMR4] ++ ++ /* ++ * enable the RBC bypass counter here ++ * to hold off the interrupts. RBC counter ++ * = 32 (1ms), Minimum RBC delay should be ++ * 400us for the analog LDOs to power down. ++ */ ++ ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] ++ ldr r10, [r11, #MX6Q_CCM_CCR] ++ bic r10, r10, #(0x3f << 21) ++ orr r10, r10, #(0x20 << 21) ++ str r10, [r11, #MX6Q_CCM_CCR] ++ ++ /* enable the counter. */ ++ ldr r10, [r11, #MX6Q_CCM_CCR] ++ orr r10, r10, #(0x1 << 27) ++ str r10, [r11, #MX6Q_CCM_CCR] ++ ++ /* unmask all the GPC interrupts. */ ++ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] ++ str r6, [r11, #MX6Q_GPC_IMR1] ++ str r7, [r11, #MX6Q_GPC_IMR2] ++ str r8, [r11, #MX6Q_GPC_IMR3] ++ str r9, [r11, #MX6Q_GPC_IMR4] ++ ++ /* ++ * now delay for a short while (3usec) ++ * ARM is at 1GHz at this point ++ * so a short loop should be enough. ++ * this delay is required to ensure that ++ * the RBC counter can start counting in ++ * case an interrupt is already pending ++ * or in case an interrupt arrives just ++ * as ARM is about to assert DSM_request. ++ */ ++ ldr r6, =2000 ++rbc_loop: ++ subs r6, r6, #0x1 ++ bne rbc_loop ++ ++ /* Zzz, enter stop mode */ ++ wfi ++ nop ++ nop ++ nop ++ nop ++ ++ /* ++ * run to here means there is pending ++ * wakeup source, system should auto ++ * resume, we need to restore MMDC IO first ++ */ ++ mov r5, #0x0 ++ resume_mmdc ++ ++ /* return to suspend finish */ ++ mov pc, lr ++ ++resume: ++ /* invalidate L1 I-cache first */ ++ mov r6, #0x0 ++ mcr p15, 0, r6, c7, c5, 0 ++ mcr p15, 0, r6, c7, c5, 6 ++ /* enable the Icache and branch prediction */ ++ mov r6, #0x1800 ++ mcr p15, 0, r6, c1, c0, 0 ++ isb ++ ++ /* get physical resume address from pm_info. */ ++ ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET] ++ /* clear core0's entry and parameter */ ++ ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET] ++ mov r7, #0x0 ++ str r7, [r11, #MX6Q_SRC_GPR1] ++ str r7, [r11, #MX6Q_SRC_GPR2] ++ ++ mov r5, #0x1 ++ resume_mmdc ++ ++ mov pc, lr ++ENDPROC(imx6_suspend) ++ ++/* ++ * The following code must assume it is running from physical address ++ * where absolute virtual addresses to the data section have to be ++ * turned into relative ones. ++ */ ++ ++ENTRY(v7_cpu_resume) ++ bl v7_invalidate_l1 ++#ifdef CONFIG_CACHE_L2X0 ++ bl l2c310_early_resume ++#endif ++ b cpu_resume ++ENDPROC(v7_cpu_resume) +diff -Nur linux-3.14.36/arch/arm/mach-imx/system.c linux-openelec/arch/arm/mach-imx/system.c +--- linux-3.14.36/arch/arm/mach-imx/system.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/system.c 2015-05-06 12:05:43.000000000 -0500 +@@ -34,6 +34,7 @@ + + static void __iomem *wdog_base; + static struct clk *wdog_clk; ++static u32 wdog_source = 1; /* use WDOG1 default */ + + /* + * Reset the system. It is called by machine_restart(). +@@ -47,6 +48,15 @@ + + if (cpu_is_mx1()) + wcr_enable = (1 << 0); ++ /* ++ * Some i.MX6 boards use WDOG2 to reset external pmic in bypass mode, ++ * so do WDOG2 reset here. Do not set SRS, since we will ++ * trigger external POR later. Use WDOG1 to reset in ldo-enable ++ * mode. You can set it by "fsl,wdog-reset" in dts. ++ */ ++ else if (wdog_source == 2 && (cpu_is_imx6q() || cpu_is_imx6dl() || ++ cpu_is_imx6sl())) ++ wcr_enable = 0x14; + else + wcr_enable = (1 << 2); + +@@ -90,12 +100,29 @@ + + void __init mxc_arch_reset_init_dt(void) + { +- struct device_node *np; ++ struct device_node *np = NULL; ++ ++ if (cpu_is_imx6q() || cpu_is_imx6dl()) ++ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); ++ else if (cpu_is_imx6sl()) ++ np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-gpc"); ++ ++ if (np) ++ of_property_read_u32(np, "fsl,wdog-reset", &wdog_source); ++ pr_info("Use WDOG%d as reset source\n", wdog_source); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx21-wdt"); + wdog_base = of_iomap(np, 0); + WARN_ON(!wdog_base); + ++ /* Some i.MX6 boards use WDOG2 to reset board in ldo-bypass mode */ ++ if (wdog_source == 2 && (cpu_is_imx6q() || cpu_is_imx6dl() || ++ cpu_is_imx6sl())) { ++ np = of_find_compatible_node(np, NULL, "fsl,imx21-wdt"); ++ wdog_base = of_iomap(np, 0); ++ WARN_ON(!wdog_base); ++ } ++ + wdog_clk = of_clk_get(np, 0); + if (IS_ERR(wdog_clk)) { + pr_warn("%s: failed to get wdog clock\n", __func__); +@@ -124,7 +151,7 @@ + } + + /* Configure the L2 PREFETCH and POWER registers */ +- val = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL); ++ val = readl_relaxed(l2x0_base + L310_PREFETCH_CTRL); + val |= 0x70800000; + /* + * The L2 cache controller(PL310) version on the i.MX6D/Q is r3p1-50rel0 +@@ -137,14 +164,12 @@ + */ + if (cpu_is_imx6q()) + val &= ~(1 << 30 | 1 << 23); +- writel_relaxed(val, l2x0_base + L2X0_PREFETCH_CTRL); +- val = L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN; +- writel_relaxed(val, l2x0_base + L2X0_POWER_CTRL); ++ writel_relaxed(val, l2x0_base + L310_PREFETCH_CTRL); + + iounmap(l2x0_base); + of_node_put(np); + + out: +- l2x0_of_init(0, ~0UL); ++ l2x0_of_init(0, ~0); + } + #endif +diff -Nur linux-3.14.36/arch/arm/mach-imx/time.c linux-openelec/arch/arm/mach-imx/time.c +--- linux-3.14.36/arch/arm/mach-imx/time.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-imx/time.c 2015-05-06 12:05:43.000000000 -0500 +@@ -60,7 +60,11 @@ + #define V2_TCTL_WAITEN (1 << 3) /* Wait enable mode */ + #define V2_TCTL_CLK_IPG (1 << 6) + #define V2_TCTL_CLK_PER (2 << 6) ++#define V2_TCTL_CLK_OSC_DIV8 (5 << 6) ++#define V2_TCTL_CLK_OSC (7 << 6) ++#define V2_TCTL_24MEN (1 << 10) + #define V2_TCTL_FRR (1 << 9) ++#define V2_TPRER_PRE24M 12 + #define V2_IR 0x0c + #define V2_TSTAT 0x08 + #define V2_TSTAT_OF1 (1 << 0) +@@ -277,11 +281,20 @@ + + void __init mxc_timer_init(void __iomem *base, int irq) + { +- uint32_t tctl_val; ++ uint32_t tctl_val, tprer_val; + struct clk *timer_clk; + struct clk *timer_ipg_clk; + +- timer_clk = clk_get_sys("imx-gpt.0", "per"); ++ /* ++ * gpt clk source from 24M OSC on imx6q > TO1.0 and ++ * imx6dl, others from per clk. ++ */ ++ if ((cpu_is_imx6q() && imx_get_soc_revision() > IMX_CHIP_REVISION_1_0) ++ || cpu_is_imx6dl()) ++ timer_clk = clk_get_sys("imx-gpt.0", "gpt_3m"); ++ else ++ timer_clk = clk_get_sys("imx-gpt.0", "per"); ++ + if (IS_ERR(timer_clk)) { + pr_err("i.MX timer: unable to get clk\n"); + return; +@@ -302,10 +315,24 @@ + __raw_writel(0, timer_base + MXC_TCTL); + __raw_writel(0, timer_base + MXC_TPRER); /* see datasheet note */ + +- if (timer_is_v2()) +- tctl_val = V2_TCTL_CLK_PER | V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN; +- else ++ if (timer_is_v2()) { ++ if ((cpu_is_imx6q() && imx_get_soc_revision() > ++ IMX_CHIP_REVISION_1_0) || cpu_is_imx6dl()) { ++ tctl_val = V2_TCTL_CLK_OSC_DIV8 | V2_TCTL_FRR | ++ V2_TCTL_WAITEN | MXC_TCTL_TEN; ++ if (cpu_is_imx6dl()) { ++ /* 24 / 8 = 3 MHz */ ++ tprer_val = 7 << V2_TPRER_PRE24M; ++ __raw_writel(tprer_val, timer_base + MXC_TPRER); ++ tctl_val |= V2_TCTL_24MEN; ++ } ++ } else { ++ tctl_val = V2_TCTL_CLK_PER | V2_TCTL_FRR | ++ V2_TCTL_WAITEN | MXC_TCTL_TEN; ++ } ++ } else { + tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN; ++ } + + __raw_writel(tctl_val, timer_base + MXC_TCTL); + +diff -Nur linux-3.14.36/arch/arm/mach-nomadik/cpu-8815.c linux-openelec/arch/arm/mach-nomadik/cpu-8815.c +--- linux-3.14.36/arch/arm/mach-nomadik/cpu-8815.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-nomadik/cpu-8815.c 2015-05-06 12:05:43.000000000 -0500 +@@ -147,7 +147,7 @@ + { + #ifdef CONFIG_CACHE_L2X0 + /* At full speed latency must be >=2, so 0x249 in low bits */ +- l2x0_of_init(0x00730249, 0xfe000fff); ++ l2x0_of_init(0x00700249, 0xfe0fefff); + #endif + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + } +diff -Nur linux-3.14.36/arch/arm/mach-omap2/common.h linux-openelec/arch/arm/mach-omap2/common.h +--- linux-3.14.36/arch/arm/mach-omap2/common.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-omap2/common.h 2015-05-06 12:05:43.000000000 -0500 +@@ -91,6 +91,7 @@ + extern void omap3_secure_sync32k_timer_init(void); + extern void omap3_gptimer_timer_init(void); + extern void omap4_local_timer_init(void); ++int omap_l2_cache_init(void); + extern void omap5_realtime_timer_init(void); + + void omap2420_init_early(void); +diff -Nur linux-3.14.36/arch/arm/mach-omap2/io.c linux-openelec/arch/arm/mach-omap2/io.c +--- linux-3.14.36/arch/arm/mach-omap2/io.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-omap2/io.c 2015-05-06 12:05:43.000000000 -0500 +@@ -608,6 +608,7 @@ + am43xx_clockdomains_init(); + am43xx_hwmod_init(); + omap_hwmod_init_postsetup(); ++ omap_l2_cache_init(); + omap_clk_soc_init = am43xx_dt_clk_init; + } + +@@ -639,6 +640,7 @@ + omap44xx_clockdomains_init(); + omap44xx_hwmod_init(); + omap_hwmod_init_postsetup(); ++ omap_l2_cache_init(); + omap_clk_soc_init = omap4xxx_dt_clk_init; + } + +diff -Nur linux-3.14.36/arch/arm/mach-omap2/Kconfig linux-openelec/arch/arm/mach-omap2/Kconfig +--- linux-3.14.36/arch/arm/mach-omap2/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-omap2/Kconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -78,6 +78,7 @@ + select MULTI_IRQ_HANDLER + select ARM_GIC + select MACH_OMAP_GENERIC ++ select MIGHT_HAVE_CACHE_L2X0 + + config SOC_DRA7XX + bool "TI DRA7XX" +diff -Nur linux-3.14.36/arch/arm/mach-omap2/omap4-common.c linux-openelec/arch/arm/mach-omap2/omap4-common.c +--- linux-3.14.36/arch/arm/mach-omap2/omap4-common.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-omap2/omap4-common.c 2015-05-06 12:05:43.000000000 -0500 +@@ -166,75 +166,57 @@ + return l2cache_base; + } + +-static void omap4_l2x0_disable(void) ++static void omap4_l2c310_write_sec(unsigned long val, unsigned reg) + { +- outer_flush_all(); +- /* Disable PL310 L2 Cache controller */ +- omap_smc1(0x102, 0x0); +-} ++ unsigned smc_op; + +-static void omap4_l2x0_set_debug(unsigned long val) +-{ +- /* Program PL310 L2 Cache controller debug register */ +- omap_smc1(0x100, val); ++ switch (reg) { ++ case L2X0_CTRL: ++ smc_op = OMAP4_MON_L2X0_CTRL_INDEX; ++ break; ++ ++ case L2X0_AUX_CTRL: ++ smc_op = OMAP4_MON_L2X0_AUXCTRL_INDEX; ++ break; ++ ++ case L2X0_DEBUG_CTRL: ++ smc_op = OMAP4_MON_L2X0_DBG_CTRL_INDEX; ++ break; ++ ++ case L310_PREFETCH_CTRL: ++ smc_op = OMAP4_MON_L2X0_PREFETCH_INDEX; ++ break; ++ ++ default: ++ WARN_ONCE(1, "OMAP L2C310: ignoring write to reg 0x%x\n", reg); ++ return; ++ } ++ ++ omap_smc1(smc_op, val); + } + +-static int __init omap_l2_cache_init(void) ++int __init omap_l2_cache_init(void) + { +- u32 aux_ctrl = 0; +- +- /* +- * To avoid code running on other OMAPs in +- * multi-omap builds +- */ +- if (!cpu_is_omap44xx()) +- return -ENODEV; ++ u32 aux_ctrl; + + /* Static mapping, never released */ + l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K); + if (WARN_ON(!l2cache_base)) + return -ENOMEM; + +- /* +- * 16-way associativity, parity disabled +- * Way size - 32KB (es1.0) +- * Way size - 64KB (es2.0 +) +- */ +- aux_ctrl = ((1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) | +- (0x1 << 25) | +- (0x1 << L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT) | +- (0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT)); +- +- if (omap_rev() == OMAP4430_REV_ES1_0) { +- aux_ctrl |= 0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT; +- } else { +- aux_ctrl |= ((0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | +- (1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | +- (1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) | +- (1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) | +- (1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT)); +- } +- if (omap_rev() != OMAP4430_REV_ES1_0) +- omap_smc1(0x109, aux_ctrl); +- +- /* Enable PL310 L2 Cache controller */ +- omap_smc1(0x102, 0x1); ++ /* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */ ++ aux_ctrl = L2C_AUX_CTRL_SHARED_OVERRIDE | ++ L310_AUX_CTRL_DATA_PREFETCH | ++ L310_AUX_CTRL_INSTR_PREFETCH; + ++ outer_cache.write_sec = omap4_l2c310_write_sec; + if (of_have_populated_dt()) +- l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK); ++ l2x0_of_init(aux_ctrl, 0xcf9fffff); + else +- l2x0_init(l2cache_base, aux_ctrl, L2X0_AUX_CTRL_MASK); +- +- /* +- * Override default outer_cache.disable with a OMAP4 +- * specific one +- */ +- outer_cache.disable = omap4_l2x0_disable; +- outer_cache.set_debug = omap4_l2x0_set_debug; ++ l2x0_init(l2cache_base, aux_ctrl, 0xcf9fffff); + + return 0; + } +-omap_early_initcall(omap_l2_cache_init); + #endif + + void __iomem *omap4_get_sar_ram_base(void) +diff -Nur linux-3.14.36/arch/arm/mach-omap2/omap-mpuss-lowpower.c linux-openelec/arch/arm/mach-omap2/omap-mpuss-lowpower.c +--- linux-3.14.36/arch/arm/mach-omap2/omap-mpuss-lowpower.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-omap2/omap-mpuss-lowpower.c 2015-05-06 12:05:43.000000000 -0500 +@@ -187,19 +187,15 @@ + * in every restore MPUSS OFF path. + */ + #ifdef CONFIG_CACHE_L2X0 +-static void save_l2x0_context(void) ++static void __init save_l2x0_context(void) + { +- u32 val; +- void __iomem *l2x0_base = omap4_get_l2cache_base(); +- if (l2x0_base) { +- val = __raw_readl(l2x0_base + L2X0_AUX_CTRL); +- __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET); +- val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL); +- __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET); +- } ++ __raw_writel(l2x0_saved_regs.aux_ctrl, ++ sar_base + L2X0_AUXCTRL_OFFSET); ++ __raw_writel(l2x0_saved_regs.prefetch_ctrl, ++ sar_base + L2X0_PREFETCH_CTRL_OFFSET); + } + #else +-static void save_l2x0_context(void) ++static void __init save_l2x0_context(void) + {} + #endif + +diff -Nur linux-3.14.36/arch/arm/mach-prima2/l2x0.c linux-openelec/arch/arm/mach-prima2/l2x0.c +--- linux-3.14.36/arch/arm/mach-prima2/l2x0.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-prima2/l2x0.c 2015-05-06 12:05:43.000000000 -0500 +@@ -8,43 +8,10 @@ + + #include + #include +-#include + #include + +-struct l2x0_aux +-{ +- u32 val; +- u32 mask; +-}; +- +-static struct l2x0_aux prima2_l2x0_aux __initconst = { +- .val = 2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT, +- .mask = 0, +-}; +- +-static struct l2x0_aux marco_l2x0_aux __initconst = { +- .val = (2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | +- (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT), +- .mask = L2X0_AUX_CTRL_MASK, +-}; +- +-static struct of_device_id sirf_l2x0_ids[] __initconst = { +- { .compatible = "sirf,prima2-pl310-cache", .data = &prima2_l2x0_aux, }, +- { .compatible = "sirf,marco-pl310-cache", .data = &marco_l2x0_aux, }, +- {}, +-}; +- + static int __init sirfsoc_l2x0_init(void) + { +- struct device_node *np; +- const struct l2x0_aux *aux; +- +- np = of_find_matching_node(NULL, sirf_l2x0_ids); +- if (np) { +- aux = of_match_node(sirf_l2x0_ids, np)->data; +- return l2x0_of_init(aux->val, aux->mask); +- } +- +- return 0; ++ return l2x0_of_init(0, ~0); + } + early_initcall(sirfsoc_l2x0_init); +diff -Nur linux-3.14.36/arch/arm/mach-prima2/pm.c linux-openelec/arch/arm/mach-prima2/pm.c +--- linux-3.14.36/arch/arm/mach-prima2/pm.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-prima2/pm.c 2015-05-06 12:05:43.000000000 -0500 +@@ -71,7 +71,6 @@ + case PM_SUSPEND_MEM: + sirfsoc_pre_suspend_power_off(); + +- outer_flush_all(); + outer_disable(); + /* go zzz */ + cpu_suspend(0, sirfsoc_finish_suspend); +diff -Nur linux-3.14.36/arch/arm/mach-realview/realview_eb.c linux-openelec/arch/arm/mach-realview/realview_eb.c +--- linux-3.14.36/arch/arm/mach-realview/realview_eb.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-realview/realview_eb.c 2015-05-06 12:05:43.000000000 -0500 +@@ -442,8 +442,13 @@ + realview_eb11mp_fixup(); + + #ifdef CONFIG_CACHE_L2X0 +- /* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled +- * Bits: .... ...0 0111 1001 0000 .... .... .... */ ++ /* ++ * The PL220 needs to be manually configured as the hardware ++ * doesn't report the correct sizes. ++ * 1MB (128KB/way), 8-way associativity, event monitor and ++ * parity enabled, ignore share bit, no force write allocate ++ * Bits: .... ...0 0111 1001 0000 .... .... .... ++ */ + l2x0_init(__io_address(REALVIEW_EB11MP_L220_BASE), 0x00790000, 0xfe000fff); + #endif + platform_device_register(&pmu_device); +diff -Nur linux-3.14.36/arch/arm/mach-realview/realview_pb1176.c linux-openelec/arch/arm/mach-realview/realview_pb1176.c +--- linux-3.14.36/arch/arm/mach-realview/realview_pb1176.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-realview/realview_pb1176.c 2015-05-06 12:05:43.000000000 -0500 +@@ -355,7 +355,13 @@ + int i; + + #ifdef CONFIG_CACHE_L2X0 +- /* 128Kb (16Kb/way) 8-way associativity. evmon/parity/share enabled. */ ++ /* ++ * The PL220 needs to be manually configured as the hardware ++ * doesn't report the correct sizes. ++ * 128kB (16kB/way), 8-way associativity, event monitor and ++ * parity enabled, ignore share bit, no force write allocate ++ * Bits: .... ...0 0111 0011 0000 .... .... .... ++ */ + l2x0_init(__io_address(REALVIEW_PB1176_L220_BASE), 0x00730000, 0xfe000fff); + #endif + +diff -Nur linux-3.14.36/arch/arm/mach-realview/realview_pb11mp.c linux-openelec/arch/arm/mach-realview/realview_pb11mp.c +--- linux-3.14.36/arch/arm/mach-realview/realview_pb11mp.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-realview/realview_pb11mp.c 2015-05-06 12:05:43.000000000 -0500 +@@ -337,8 +337,13 @@ + int i; + + #ifdef CONFIG_CACHE_L2X0 +- /* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled +- * Bits: .... ...0 0111 1001 0000 .... .... .... */ ++ /* ++ * The PL220 needs to be manually configured as the hardware ++ * doesn't report the correct sizes. ++ * 1MB (128KB/way), 8-way associativity, event monitor and ++ * parity enabled, ignore share bit, no force write allocate ++ * Bits: .... ...0 0111 1001 0000 .... .... .... ++ */ + l2x0_init(__io_address(REALVIEW_TC11MP_L220_BASE), 0x00790000, 0xfe000fff); + #endif + +diff -Nur linux-3.14.36/arch/arm/mach-realview/realview_pbx.c linux-openelec/arch/arm/mach-realview/realview_pbx.c +--- linux-3.14.36/arch/arm/mach-realview/realview_pbx.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-realview/realview_pbx.c 2015-05-06 12:05:43.000000000 -0500 +@@ -370,8 +370,8 @@ + __io_address(REALVIEW_PBX_TILE_L220_BASE); + + /* set RAM latencies to 1 cycle for eASIC */ +- writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL); +- writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL); ++ writel(0, l2x0_base + L310_TAG_LATENCY_CTRL); ++ writel(0, l2x0_base + L310_DATA_LATENCY_CTRL); + + /* 16KB way size, 8-way associativity, parity disabled + * Bits: .. 0 0 0 0 1 00 1 0 1 001 0 000 0 .... .... .... */ +diff -Nur linux-3.14.36/arch/arm/mach-rockchip/rockchip.c linux-openelec/arch/arm/mach-rockchip/rockchip.c +--- linux-3.14.36/arch/arm/mach-rockchip/rockchip.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-rockchip/rockchip.c 2015-05-06 12:05:43.000000000 -0500 +@@ -25,7 +25,7 @@ + + static void __init rockchip_dt_init(void) + { +- l2x0_of_init(0, ~0UL); ++ l2x0_of_init(0, ~0); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + } + +diff -Nur linux-3.14.36/arch/arm/mach-shmobile/board-armadillo800eva.c linux-openelec/arch/arm/mach-shmobile/board-armadillo800eva.c +--- linux-3.14.36/arch/arm/mach-shmobile/board-armadillo800eva.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-shmobile/board-armadillo800eva.c 2015-05-06 12:05:43.000000000 -0500 +@@ -1270,8 +1270,8 @@ + + + #ifdef CONFIG_CACHE_L2X0 +- /* Early BRESP enable, Shared attribute override enable, 32K*8way */ +- l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff); ++ /* Shared attribute override enable, 32K*8way */ ++ l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff); + #endif + + i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); +diff -Nur linux-3.14.36/arch/arm/mach-shmobile/board-armadillo800eva-reference.c linux-openelec/arch/arm/mach-shmobile/board-armadillo800eva-reference.c +--- linux-3.14.36/arch/arm/mach-shmobile/board-armadillo800eva-reference.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-shmobile/board-armadillo800eva-reference.c 2015-05-06 12:05:43.000000000 -0500 +@@ -164,8 +164,8 @@ + r8a7740_meram_workaround(); + + #ifdef CONFIG_CACHE_L2X0 +- /* Early BRESP enable, Shared attribute override enable, 32K*8way */ +- l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff); ++ /* Shared attribute override enable, 32K*8way */ ++ l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff); + #endif + + r8a7740_add_standard_devices_dt(); +diff -Nur linux-3.14.36/arch/arm/mach-shmobile/board-kzm9g.c linux-openelec/arch/arm/mach-shmobile/board-kzm9g.c +--- linux-3.14.36/arch/arm/mach-shmobile/board-kzm9g.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-shmobile/board-kzm9g.c 2015-05-06 12:05:43.000000000 -0500 +@@ -878,8 +878,8 @@ + gpio_request_one(223, GPIOF_IN, NULL); /* IRQ8 */ + + #ifdef CONFIG_CACHE_L2X0 +- /* Early BRESP enable, Shared attribute override enable, 64K*8way */ +- l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff); ++ /* Shared attribute override enable, 64K*8way */ ++ l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); + #endif + + i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); +diff -Nur linux-3.14.36/arch/arm/mach-shmobile/board-kzm9g-reference.c linux-openelec/arch/arm/mach-shmobile/board-kzm9g-reference.c +--- linux-3.14.36/arch/arm/mach-shmobile/board-kzm9g-reference.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-shmobile/board-kzm9g-reference.c 2015-05-06 12:05:43.000000000 -0500 +@@ -36,8 +36,8 @@ + sh73a0_add_standard_devices_dt(); + + #ifdef CONFIG_CACHE_L2X0 +- /* Early BRESP enable, Shared attribute override enable, 64K*8way */ +- l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff); ++ /* Shared attribute override enable, 64K*8way */ ++ l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); + #endif + } + +diff -Nur linux-3.14.36/arch/arm/mach-shmobile/setup-r8a7778.c linux-openelec/arch/arm/mach-shmobile/setup-r8a7778.c +--- linux-3.14.36/arch/arm/mach-shmobile/setup-r8a7778.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-shmobile/setup-r8a7778.c 2015-05-06 12:05:43.000000000 -0500 +@@ -298,10 +298,10 @@ + void __iomem *base = ioremap_nocache(0xf0100000, 0x1000); + if (base) { + /* +- * Early BRESP enable, Shared attribute override enable, 64K*16way ++ * Shared attribute override enable, 64K*16way + * don't call iounmap(base) + */ +- l2x0_init(base, 0x40470000, 0x82000fff); ++ l2x0_init(base, 0x00400000, 0xc20f0fff); + } + #endif + +diff -Nur linux-3.14.36/arch/arm/mach-shmobile/setup-r8a7779.c linux-openelec/arch/arm/mach-shmobile/setup-r8a7779.c +--- linux-3.14.36/arch/arm/mach-shmobile/setup-r8a7779.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-shmobile/setup-r8a7779.c 2015-05-06 12:05:43.000000000 -0500 +@@ -700,8 +700,8 @@ + void __init r8a7779_add_standard_devices(void) + { + #ifdef CONFIG_CACHE_L2X0 +- /* Early BRESP enable, Shared attribute override enable, 64K*16way */ +- l2x0_init(IOMEM(0xf0100000), 0x40470000, 0x82000fff); ++ /* Shared attribute override enable, 64K*16way */ ++ l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); + #endif + r8a7779_pm_init(); + +diff -Nur linux-3.14.36/arch/arm/mach-socfpga/socfpga.c linux-openelec/arch/arm/mach-socfpga/socfpga.c +--- linux-3.14.36/arch/arm/mach-socfpga/socfpga.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-socfpga/socfpga.c 2015-05-06 12:05:43.000000000 -0500 +@@ -104,7 +104,7 @@ + + static void __init socfpga_cyclone5_init(void) + { +- l2x0_of_init(0, ~0UL); ++ l2x0_of_init(0, ~0); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + socfpga_init_clocks(); + } +diff -Nur linux-3.14.36/arch/arm/mach-spear/platsmp.c linux-openelec/arch/arm/mach-spear/platsmp.c +--- linux-3.14.36/arch/arm/mach-spear/platsmp.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-spear/platsmp.c 2015-05-06 12:05:43.000000000 -0500 +@@ -20,6 +20,18 @@ + #include + #include "generic.h" + ++/* ++ * Write pen_release in a way that is guaranteed to be visible to all ++ * observers, irrespective of whether they're taking part in coherency ++ * or not. This is necessary for the hotplug code to work reliably. ++ */ ++static void write_pen_release(int val) ++{ ++ pen_release = val; ++ smp_wmb(); ++ sync_cache_w(&pen_release); ++} ++ + static DEFINE_SPINLOCK(boot_lock); + + static void __iomem *scu_base = IOMEM(VA_SCU_BASE); +@@ -30,8 +42,7 @@ + * let the primary processor know we're out of the + * pen, then head off into the C entry point + */ +- pen_release = -1; +- smp_wmb(); ++ write_pen_release(-1); + + /* + * Synchronise with the boot thread. +@@ -58,9 +69,7 @@ + * Note that "pen_release" is the hardware CPU ID, whereas + * "cpu" is Linux's internal ID. + */ +- pen_release = cpu; +- flush_cache_all(); +- outer_flush_all(); ++ write_pen_release(cpu); + + timeout = jiffies + (1 * HZ); + while (time_before(jiffies, timeout)) { +diff -Nur linux-3.14.36/arch/arm/mach-spear/spear13xx.c linux-openelec/arch/arm/mach-spear/spear13xx.c +--- linux-3.14.36/arch/arm/mach-spear/spear13xx.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-spear/spear13xx.c 2015-05-06 12:05:43.000000000 -0500 +@@ -38,15 +38,15 @@ + if (!IS_ENABLED(CONFIG_CACHE_L2X0)) + return; + +- writel_relaxed(0x06, VA_L2CC_BASE + L2X0_PREFETCH_CTRL); ++ writel_relaxed(0x06, VA_L2CC_BASE + L310_PREFETCH_CTRL); + + /* + * Program following latencies in order to make + * SPEAr1340 work at 600 MHz + */ +- writel_relaxed(0x221, VA_L2CC_BASE + L2X0_TAG_LATENCY_CTRL); +- writel_relaxed(0x441, VA_L2CC_BASE + L2X0_DATA_LATENCY_CTRL); +- l2x0_init(VA_L2CC_BASE, 0x70A60001, 0xfe00ffff); ++ writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL); ++ writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL); ++ l2x0_init(VA_L2CC_BASE, 0x30a00001, 0xfe0fffff); + } + + /* +diff -Nur linux-3.14.36/arch/arm/mach-sti/board-dt.c linux-openelec/arch/arm/mach-sti/board-dt.c +--- linux-3.14.36/arch/arm/mach-sti/board-dt.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-sti/board-dt.c 2015-05-06 12:05:43.000000000 -0500 +@@ -16,15 +16,9 @@ + + void __init stih41x_l2x0_init(void) + { +- u32 way_size = 0x4; +- u32 aux_ctrl; +- /* may be this can be encoded in macros like BIT*() */ +- aux_ctrl = (0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | +- (0x1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) | +- (0x1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) | +- (way_size << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); +- +- l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK); ++ l2x0_of_init(L2C_AUX_CTRL_SHARED_OVERRIDE | ++ L310_AUX_CTRL_DATA_PREFETCH | ++ L310_AUX_CTRL_INSTR_PREFETCH, 0xc00f0fff); + } + + static void __init stih41x_machine_init(void) +diff -Nur linux-3.14.36/arch/arm/mach-tegra/pm.h linux-openelec/arch/arm/mach-tegra/pm.h +--- linux-3.14.36/arch/arm/mach-tegra/pm.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-tegra/pm.h 2015-05-06 12:05:43.000000000 -0500 +@@ -35,8 +35,6 @@ + void tegra30_lp1_iram_hook(void); + void tegra30_sleep_core_init(void); + +-extern unsigned long l2x0_saved_regs_addr; +- + void tegra_clear_cpu_in_lp2(void); + bool tegra_set_cpu_in_lp2(void); + +diff -Nur linux-3.14.36/arch/arm/mach-tegra/reset-handler.S linux-openelec/arch/arm/mach-tegra/reset-handler.S +--- linux-3.14.36/arch/arm/mach-tegra/reset-handler.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-tegra/reset-handler.S 2015-07-24 18:03:29.256842002 -0500 +@@ -19,7 +19,6 @@ + + #include + #include +-#include + + #include "flowctrl.h" + #include "fuse.h" +@@ -79,8 +78,10 @@ + str r1, [r0] + #endif + ++#ifdef CONFIG_CACHE_L2X0 + /* L2 cache resume & re-enable */ +- l2_cache_resume r0, r1, r2, l2x0_saved_regs_addr ++ bl l2c310_early_resume ++#endif + end_ca9_scu_l2_resume: + mov32 r9, 0xc0f + cmp r8, r9 +@@ -90,12 +91,6 @@ + ENDPROC(tegra_resume) + #endif + +-#ifdef CONFIG_CACHE_L2X0 +- .globl l2x0_saved_regs_addr +-l2x0_saved_regs_addr: +- .long 0 +-#endif +- + .align L1_CACHE_SHIFT + ENTRY(__tegra_cpu_reset_handler_start) + +diff -Nur linux-3.14.36/arch/arm/mach-tegra/reset-handler.S.orig linux-openelec/arch/arm/mach-tegra/reset-handler.S.orig +--- linux-3.14.36/arch/arm/mach-tegra/reset-handler.S.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-tegra/reset-handler.S.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,284 @@ ++/* ++ * Copyright (c) 2012, 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 . ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "flowctrl.h" ++#include "fuse.h" ++#include "iomap.h" ++#include "reset.h" ++#include "sleep.h" ++ ++#define PMC_SCRATCH41 0x140 ++ ++#define RESET_DATA(x) ((TEGRA_RESET_##x)*4) ++ ++#ifdef CONFIG_PM_SLEEP ++/* ++ * tegra_resume ++ * ++ * CPU boot vector when restarting the a CPU following ++ * an LP2 transition. Also branched to by LP0 and LP1 resume after ++ * re-enabling sdram. ++ * ++ * r6: SoC ID ++ * r8: CPU part number ++ */ ++ENTRY(tegra_resume) ++ check_cpu_part_num 0xc09, r8, r9 ++ bleq v7_invalidate_l1 ++ ++ cpu_id r0 ++ cmp r0, #0 @ CPU0? ++ THUMB( it ne ) ++ bne cpu_resume @ no ++ ++ /* Are we on Tegra20? */ ++ cmp r6, #TEGRA20 ++ beq 1f @ Yes ++ /* Clear the flow controller flags for this CPU. */ ++ cpu_to_csr_reg r1, r0 ++ mov32 r2, TEGRA_FLOW_CTRL_BASE ++ ldr r1, [r2, r1] ++ /* Clear event & intr flag */ ++ orr r1, r1, \ ++ #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG ++ movw r0, #0x3FFD @ enable, cluster_switch, immed, bitmaps ++ @ & ext flags for CPU power mgnt ++ bic r1, r1, r0 ++ str r1, [r2] ++1: ++ ++ mov32 r9, 0xc09 ++ cmp r8, r9 ++ bne end_ca9_scu_l2_resume ++#ifdef CONFIG_HAVE_ARM_SCU ++ /* enable SCU */ ++ mov32 r0, TEGRA_ARM_PERIF_BASE ++ ldr r1, [r0] ++ orr r1, r1, #1 ++ str r1, [r0] ++#endif ++ ++#ifdef CONFIG_CACHE_L2X0 ++ /* L2 cache resume & re-enable */ ++ bl l2c310_early_resume ++#endif ++end_ca9_scu_l2_resume: ++ mov32 r9, 0xc0f ++ cmp r8, r9 ++ bleq tegra_init_l2_for_a15 ++ ++ b cpu_resume ++ENDPROC(tegra_resume) ++#endif ++ ++ .align L1_CACHE_SHIFT ++ENTRY(__tegra_cpu_reset_handler_start) ++ ++/* ++ * __tegra_cpu_reset_handler: ++ * ++ * Common handler for all CPU reset events. ++ * ++ * Register usage within the reset handler: ++ * ++ * Others: scratch ++ * R6 = SoC ID ++ * R7 = CPU present (to the OS) mask ++ * R8 = CPU in LP1 state mask ++ * R9 = CPU in LP2 state mask ++ * R10 = CPU number ++ * R11 = CPU mask ++ * R12 = pointer to reset handler data ++ * ++ * NOTE: This code is copied to IRAM. All code and data accesses ++ * must be position-independent. ++ */ ++ ++ .align L1_CACHE_SHIFT ++ENTRY(__tegra_cpu_reset_handler) ++ ++ cpsid aif, 0x13 @ SVC mode, interrupts disabled ++ ++ tegra_get_soc_id TEGRA_APB_MISC_BASE, r6 ++#ifdef CONFIG_ARCH_TEGRA_2x_SOC ++t20_check: ++ cmp r6, #TEGRA20 ++ bne after_t20_check ++t20_errata: ++ # Tegra20 is a Cortex-A9 r1p1 ++ mrc p15, 0, r0, c1, c0, 0 @ read system control register ++ orr r0, r0, #1 << 14 @ erratum 716044 ++ mcr p15, 0, r0, c1, c0, 0 @ write system control register ++ mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register ++ orr r0, r0, #1 << 4 @ erratum 742230 ++ orr r0, r0, #1 << 11 @ erratum 751472 ++ mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register ++ b after_errata ++after_t20_check: ++#endif ++#ifdef CONFIG_ARCH_TEGRA_3x_SOC ++t30_check: ++ cmp r6, #TEGRA30 ++ bne after_t30_check ++t30_errata: ++ # Tegra30 is a Cortex-A9 r2p9 ++ mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register ++ orr r0, r0, #1 << 6 @ erratum 743622 ++ orr r0, r0, #1 << 11 @ erratum 751472 ++ mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register ++ b after_errata ++after_t30_check: ++#endif ++after_errata: ++ mrc p15, 0, r10, c0, c0, 5 @ MPIDR ++ and r10, r10, #0x3 @ R10 = CPU number ++ mov r11, #1 ++ mov r11, r11, lsl r10 @ R11 = CPU mask ++ adr r12, __tegra_cpu_reset_handler_data ++ ++#ifdef CONFIG_SMP ++ /* Does the OS know about this CPU? */ ++ ldr r7, [r12, #RESET_DATA(MASK_PRESENT)] ++ tst r7, r11 @ if !present ++ bleq __die @ CPU not present (to OS) ++#endif ++ ++#ifdef CONFIG_ARCH_TEGRA_2x_SOC ++ /* Are we on Tegra20? */ ++ 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 ++ mov r0, #0 ++ cmp r10, #0 ++ strne r0, [r5, #PMC_SCRATCH41] ++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)] ++ cmp lr, #0 ++ bleq __die @ no LP1 startup handler ++ THUMB( add lr, lr, #1 ) @ switch to Thumb mode ++ bx lr ++__is_not_lp1: ++ ++ /* Waking up from LP2? */ ++ ldr r9, [r12, #RESET_DATA(MASK_LP2)] ++ tst r9, r11 @ if in_lp2 ++ beq __is_not_lp2 ++ ldr lr, [r12, #RESET_DATA(STARTUP_LP2)] ++ cmp lr, #0 ++ bleq __die @ no LP2 startup handler ++ bx lr ++ ++__is_not_lp2: ++ ++#ifdef CONFIG_SMP ++ /* ++ * Can only be secondary boot (initial or hotplug) ++ * CPU0 can't be here for Tegra20/30 ++ */ ++ cmp r6, #TEGRA114 ++ beq __no_cpu0_chk ++ cmp r10, #0 ++ bleq __die @ CPU0 cannot be here ++__no_cpu0_chk: ++ ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)] ++ cmp lr, #0 ++ bleq __die @ no secondary startup handler ++ bx lr ++#endif ++ ++/* ++ * We don't know why the CPU reset. Just kill it. ++ * The LR register will contain the address we died at + 4. ++ */ ++ ++__die: ++ sub lr, lr, #4 ++ mov32 r7, TEGRA_PMC_BASE ++ str lr, [r7, #PMC_SCRATCH41] ++ ++ mov32 r7, TEGRA_CLK_RESET_BASE ++ ++ /* Are we on Tegra20? */ ++ cmp r6, #TEGRA20 ++ bne 1f ++ ++#ifdef CONFIG_ARCH_TEGRA_2x_SOC ++ mov32 r0, 0x1111 ++ mov r1, r0, lsl r10 ++ str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET ++#endif ++1: ++#ifdef CONFIG_ARCH_TEGRA_3x_SOC ++ mov32 r6, TEGRA_FLOW_CTRL_BASE ++ ++ cmp r10, #0 ++ moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS ++ moveq r2, #FLOW_CTRL_CPU0_CSR ++ movne r1, r10, lsl #3 ++ addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8) ++ addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8) ++ ++ /* Clear CPU "event" and "interrupt" flags and power gate ++ it when halting but not before it is in the "WFI" state. */ ++ ldr r0, [r6, +r2] ++ orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG ++ orr r0, r0, #FLOW_CTRL_CSR_ENABLE ++ str r0, [r6, +r2] ++ ++ /* Unconditionally halt this CPU */ ++ mov r0, #FLOW_CTRL_WAITEVENT ++ str r0, [r6, +r1] ++ ldr r0, [r6, +r1] @ memory barrier ++ ++ dsb ++ isb ++ wfi @ CPU should be power gated here ++ ++ /* If the CPU didn't power gate above just kill it's clock. */ ++ ++ mov r0, r11, lsl #8 ++ str r0, [r7, #348] @ CLK_CPU_CMPLX_SET ++#endif ++ ++ /* If the CPU still isn't dead, just spin here. */ ++ b . ++ENDPROC(__tegra_cpu_reset_handler) ++ ++ .align L1_CACHE_SHIFT ++ .type __tegra_cpu_reset_handler_data, %object ++ .globl __tegra_cpu_reset_handler_data ++__tegra_cpu_reset_handler_data: ++ .rept TEGRA_RESET_DATA_SIZE ++ .long 0 ++ .endr ++ .align L1_CACHE_SHIFT ++ ++ENTRY(__tegra_cpu_reset_handler_end) +diff -Nur linux-3.14.36/arch/arm/mach-tegra/sleep.h linux-openelec/arch/arm/mach-tegra/sleep.h +--- linux-3.14.36/arch/arm/mach-tegra/sleep.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-tegra/sleep.h 2015-05-06 12:05:43.000000000 -0500 +@@ -120,37 +120,6 @@ + mov \tmp1, \tmp1, lsr #8 + .endm + +-/* Macro to resume & re-enable L2 cache */ +-#ifndef L2X0_CTRL_EN +-#define L2X0_CTRL_EN 1 +-#endif +- +-#ifdef CONFIG_CACHE_L2X0 +-.macro l2_cache_resume, tmp1, tmp2, tmp3, phys_l2x0_saved_regs +- W(adr) \tmp1, \phys_l2x0_saved_regs +- ldr \tmp1, [\tmp1] +- ldr \tmp2, [\tmp1, #L2X0_R_PHY_BASE] +- ldr \tmp3, [\tmp2, #L2X0_CTRL] +- tst \tmp3, #L2X0_CTRL_EN +- bne exit_l2_resume +- ldr \tmp3, [\tmp1, #L2X0_R_TAG_LATENCY] +- str \tmp3, [\tmp2, #L2X0_TAG_LATENCY_CTRL] +- ldr \tmp3, [\tmp1, #L2X0_R_DATA_LATENCY] +- str \tmp3, [\tmp2, #L2X0_DATA_LATENCY_CTRL] +- ldr \tmp3, [\tmp1, #L2X0_R_PREFETCH_CTRL] +- str \tmp3, [\tmp2, #L2X0_PREFETCH_CTRL] +- ldr \tmp3, [\tmp1, #L2X0_R_PWR_CTRL] +- str \tmp3, [\tmp2, #L2X0_POWER_CTRL] +- ldr \tmp3, [\tmp1, #L2X0_R_AUX_CTRL] +- str \tmp3, [\tmp2, #L2X0_AUX_CTRL] +- mov \tmp3, #L2X0_CTRL_EN +- str \tmp3, [\tmp2, #L2X0_CTRL] +-exit_l2_resume: +-.endm +-#else /* CONFIG_CACHE_L2X0 */ +-.macro l2_cache_resume, tmp1, tmp2, tmp3, phys_l2x0_saved_regs +-.endm +-#endif /* CONFIG_CACHE_L2X0 */ + #else + void tegra_pen_lock(void); + void tegra_pen_unlock(void); +diff -Nur linux-3.14.36/arch/arm/mach-tegra/tegra.c linux-openelec/arch/arm/mach-tegra/tegra.c +--- linux-3.14.36/arch/arm/mach-tegra/tegra.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-tegra/tegra.c 2015-05-06 12:05:43.000000000 -0500 +@@ -73,27 +73,7 @@ + static void __init tegra_init_cache(void) + { + #ifdef CONFIG_CACHE_L2X0 +- static const struct of_device_id pl310_ids[] __initconst = { +- { .compatible = "arm,pl310-cache", }, +- {} +- }; +- +- struct device_node *np; +- int ret; +- void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; +- u32 aux_ctrl, cache_type; +- +- np = of_find_matching_node(NULL, pl310_ids); +- if (!np) +- return; +- +- cache_type = readl(p + L2X0_CACHE_TYPE); +- aux_ctrl = (cache_type & 0x700) << (17-8); +- aux_ctrl |= 0x7C400001; +- +- ret = l2x0_of_init(aux_ctrl, 0x8200c3fe); +- if (!ret) +- l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs); ++ l2x0_of_init(0x3c400001, 0xc20fc3fe); + #endif + } + +diff -Nur linux-3.14.36/arch/arm/mach-ux500/board-mop500-audio.c linux-openelec/arch/arm/mach-ux500/board-mop500-audio.c +--- linux-3.14.36/arch/arm/mach-ux500/board-mop500-audio.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-ux500/board-mop500-audio.c 2015-05-06 12:05:43.000000000 -0500 +@@ -9,7 +9,6 @@ + #include + #include + +-#include "irqs.h" + #include + + #include "ste-dma40-db8500.h" +diff -Nur linux-3.14.36/arch/arm/mach-ux500/cache-l2x0.c linux-openelec/arch/arm/mach-ux500/cache-l2x0.c +--- linux-3.14.36/arch/arm/mach-ux500/cache-l2x0.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-ux500/cache-l2x0.c 2015-05-06 12:05:43.000000000 -0500 +@@ -35,10 +35,16 @@ + return 0; + } + +-static int __init ux500_l2x0_init(void) ++static void ux500_l2c310_write_sec(unsigned long val, unsigned reg) + { +- u32 aux_val = 0x3e000000; ++ /* ++ * We can't write to secure registers as we are in non-secure ++ * mode, until we have some SMI service available. ++ */ ++} + ++static int __init ux500_l2x0_init(void) ++{ + if (cpu_is_u8500_family() || cpu_is_ux540_family()) + l2x0_base = __io_address(U8500_L2CC_BASE); + else +@@ -48,28 +54,12 @@ + /* Unlock before init */ + ux500_l2x0_unlock(); + +- /* DBx540's L2 has 128KB way size */ +- if (cpu_is_ux540_family()) +- /* 128KB way size */ +- aux_val |= (0x4 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); +- else +- /* 64KB way size */ +- aux_val |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); ++ outer_cache.write_sec = ux500_l2c310_write_sec; + +- /* 64KB way size, 8 way associativity, force WA */ + if (of_have_populated_dt()) +- l2x0_of_init(aux_val, 0xc0000fff); ++ l2x0_of_init(0, ~0); + else +- l2x0_init(l2x0_base, aux_val, 0xc0000fff); +- +- /* +- * We can't disable l2 as we are in non secure mode, currently +- * this seems be called only during kexec path. So let's +- * override outer.disable with nasty assignment until we have +- * some SMI service available. +- */ +- outer_cache.disable = NULL; +- outer_cache.set_debug = NULL; ++ l2x0_init(l2x0_base, 0, ~0); + + return 0; + } +diff -Nur linux-3.14.36/arch/arm/mach-ux500/cpu-db8500.c linux-openelec/arch/arm/mach-ux500/cpu-db8500.c +--- linux-3.14.36/arch/arm/mach-ux500/cpu-db8500.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-ux500/cpu-db8500.c 2015-05-06 12:05:43.000000000 -0500 +@@ -27,7 +27,6 @@ + #include + + #include "setup.h" +-#include "irqs.h" + + #include "board-mop500-regulators.h" + #include "board-mop500.h" +@@ -35,14 +34,11 @@ + #include "id.h" + + struct ab8500_platform_data ab8500_platdata = { +- .irq_base = MOP500_AB8500_IRQ_BASE, + .regulator = &ab8500_regulator_plat_data, + }; + + struct prcmu_pdata db8500_prcmu_pdata = { + .ab_platdata = &ab8500_platdata, +- .ab_irq = IRQ_DB8500_AB8500, +- .irq_base = IRQ_PRCMU_BASE, + .version_offset = DB8500_PRCMU_FW_VERSION_OFFSET, + .legacy_offset = DB8500_PRCMU_LEGACY_OFFSET, + }; +diff -Nur linux-3.14.36/arch/arm/mach-ux500/irqs-board-mop500.h linux-openelec/arch/arm/mach-ux500/irqs-board-mop500.h +--- linux-3.14.36/arch/arm/mach-ux500/irqs-board-mop500.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-ux500/irqs-board-mop500.h 1969-12-31 18:00:00.000000000 -0600 +@@ -1,55 +0,0 @@ +-/* +- * Copyright (C) ST-Ericsson SA 2010 +- * +- * Author: Rabin Vincent +- * License terms: GNU General Public License (GPL) version 2 +- */ +- +-#ifndef __MACH_IRQS_BOARD_MOP500_H +-#define __MACH_IRQS_BOARD_MOP500_H +- +-/* Number of AB8500 irqs is taken from header file */ +-#include +- +-#define MOP500_AB8500_IRQ_BASE IRQ_BOARD_START +-#define MOP500_AB8500_IRQ_END (MOP500_AB8500_IRQ_BASE \ +- + AB8500_MAX_NR_IRQS) +- +-/* TC35892 */ +-#define TC35892_NR_INTERNAL_IRQS 8 +-#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x)) +-#define TC35892_NR_GPIOS 24 +-#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS) +- +-#define MOP500_EGPIO_NR_IRQS TC35892_NR_IRQS +- +-#define MOP500_EGPIO_IRQ_BASE MOP500_AB8500_IRQ_END +-#define MOP500_EGPIO_IRQ_END (MOP500_EGPIO_IRQ_BASE \ +- + MOP500_EGPIO_NR_IRQS) +-/* STMPE1601 irqs */ +-#define STMPE_NR_INTERNAL_IRQS 9 +-#define STMPE_INT_GPIO(x) (STMPE_NR_INTERNAL_IRQS + (x)) +-#define STMPE_NR_GPIOS 24 +-#define STMPE_NR_IRQS STMPE_INT_GPIO(STMPE_NR_GPIOS) +- +-#define MOP500_STMPE1601_IRQBASE MOP500_EGPIO_IRQ_END +-#define MOP500_STMPE1601_IRQ(x) (MOP500_STMPE1601_IRQBASE + (x)) +- +-#define MOP500_STMPE1601_IRQ_END \ +- MOP500_STMPE1601_IRQ(STMPE_NR_INTERNAL_IRQS) +- +-#define MOP500_NR_IRQS MOP500_STMPE1601_IRQ_END +- +-#define MOP500_IRQ_END MOP500_NR_IRQS +- +-/* +- * We may have several boards, but only one will run at a +- * time, so the one with most IRQs will bump this ahead, +- * but the IRQ_BOARD_START remains the same for either board. +- */ +-#if MOP500_IRQ_END > IRQ_BOARD_END +-#undef IRQ_BOARD_END +-#define IRQ_BOARD_END MOP500_IRQ_END +-#endif +- +-#endif +diff -Nur linux-3.14.36/arch/arm/mach-ux500/irqs-db8500.h linux-openelec/arch/arm/mach-ux500/irqs-db8500.h +--- linux-3.14.36/arch/arm/mach-ux500/irqs-db8500.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-ux500/irqs-db8500.h 1969-12-31 18:00:00.000000000 -0600 +@@ -1,125 +0,0 @@ +-/* +- * Copyright (C) ST-Ericsson SA 2010 +- * +- * Author: Rabin Vincent +- * License terms: GNU General Public License (GPL) version 2 +- */ +- +-#ifndef __MACH_IRQS_DB8500_H +-#define __MACH_IRQS_DB8500_H +- +-#define IRQ_DB8500_MTU0 (IRQ_SHPI_START + 4) +-#define IRQ_DB8500_SPI2 (IRQ_SHPI_START + 6) +-#define IRQ_DB8500_PMU (IRQ_SHPI_START + 7) +-#define IRQ_DB8500_SPI0 (IRQ_SHPI_START + 8) +-#define IRQ_DB8500_RTT (IRQ_SHPI_START + 9) +-#define IRQ_DB8500_PKA (IRQ_SHPI_START + 10) +-#define IRQ_DB8500_UART0 (IRQ_SHPI_START + 11) +-#define IRQ_DB8500_I2C3 (IRQ_SHPI_START + 12) +-#define IRQ_DB8500_L2CC (IRQ_SHPI_START + 13) +-#define IRQ_DB8500_SSP0 (IRQ_SHPI_START + 14) +-#define IRQ_DB8500_CRYP1 (IRQ_SHPI_START + 15) +-#define IRQ_DB8500_MSP1_RX (IRQ_SHPI_START + 16) +-#define IRQ_DB8500_MTU1 (IRQ_SHPI_START + 17) +-#define IRQ_DB8500_RTC (IRQ_SHPI_START + 18) +-#define IRQ_DB8500_UART1 (IRQ_SHPI_START + 19) +-#define IRQ_DB8500_USB_WAKEUP (IRQ_SHPI_START + 20) +-#define IRQ_DB8500_I2C0 (IRQ_SHPI_START + 21) +-#define IRQ_DB8500_I2C1 (IRQ_SHPI_START + 22) +-#define IRQ_DB8500_USBOTG (IRQ_SHPI_START + 23) +-#define IRQ_DB8500_DMA_SECURE (IRQ_SHPI_START + 24) +-#define IRQ_DB8500_DMA (IRQ_SHPI_START + 25) +-#define IRQ_DB8500_UART2 (IRQ_SHPI_START + 26) +-#define IRQ_DB8500_ICN_PMU1 (IRQ_SHPI_START + 27) +-#define IRQ_DB8500_ICN_PMU2 (IRQ_SHPI_START + 28) +-#define IRQ_DB8500_HSIR_EXCEP (IRQ_SHPI_START + 29) +-#define IRQ_DB8500_MSP0 (IRQ_SHPI_START + 31) +-#define IRQ_DB8500_HSIR_CH0_OVRRUN (IRQ_SHPI_START + 32) +-#define IRQ_DB8500_HSIR_CH1_OVRRUN (IRQ_SHPI_START + 33) +-#define IRQ_DB8500_HSIR_CH2_OVRRUN (IRQ_SHPI_START + 34) +-#define IRQ_DB8500_HSIR_CH3_OVRRUN (IRQ_SHPI_START + 35) +-#define IRQ_DB8500_HSIR_CH4_OVRRUN (IRQ_SHPI_START + 36) +-#define IRQ_DB8500_HSIR_CH5_OVRRUN (IRQ_SHPI_START + 37) +-#define IRQ_DB8500_HSIR_CH6_OVRRUN (IRQ_SHPI_START + 38) +-#define IRQ_DB8500_HSIR_CH7_OVRRUN (IRQ_SHPI_START + 39) +-#define IRQ_DB8500_AB8500 (IRQ_SHPI_START + 40) +-#define IRQ_DB8500_SDMMC2 (IRQ_SHPI_START + 41) +-#define IRQ_DB8500_SIA (IRQ_SHPI_START + 42) +-#define IRQ_DB8500_SIA2 (IRQ_SHPI_START + 43) +-#define IRQ_DB8500_SVA (IRQ_SHPI_START + 44) +-#define IRQ_DB8500_SVA2 (IRQ_SHPI_START + 45) +-#define IRQ_DB8500_PRCMU0 (IRQ_SHPI_START + 46) +-#define IRQ_DB8500_PRCMU1 (IRQ_SHPI_START + 47) +-#define IRQ_DB8500_DISP (IRQ_SHPI_START + 48) +-#define IRQ_DB8500_SPI3 (IRQ_SHPI_START + 49) +-#define IRQ_DB8500_SDMMC1 (IRQ_SHPI_START + 50) +-#define IRQ_DB8500_I2C4 (IRQ_SHPI_START + 51) +-#define IRQ_DB8500_SSP1 (IRQ_SHPI_START + 52) +-#define IRQ_DB8500_SKE (IRQ_SHPI_START + 53) +-#define IRQ_DB8500_KB (IRQ_SHPI_START + 54) +-#define IRQ_DB8500_I2C2 (IRQ_SHPI_START + 55) +-#define IRQ_DB8500_B2R2 (IRQ_SHPI_START + 56) +-#define IRQ_DB8500_CRYP0 (IRQ_SHPI_START + 57) +-#define IRQ_DB8500_SDMMC3 (IRQ_SHPI_START + 59) +-#define IRQ_DB8500_SDMMC0 (IRQ_SHPI_START + 60) +-#define IRQ_DB8500_HSEM (IRQ_SHPI_START + 61) +-#define IRQ_DB8500_MSP1 (IRQ_SHPI_START + 62) +-#define IRQ_DB8500_SBAG (IRQ_SHPI_START + 63) +-#define IRQ_DB8500_SPI1 (IRQ_SHPI_START + 96) +-#define IRQ_DB8500_SRPTIMER (IRQ_SHPI_START + 97) +-#define IRQ_DB8500_MSP2 (IRQ_SHPI_START + 98) +-#define IRQ_DB8500_SDMMC4 (IRQ_SHPI_START + 99) +-#define IRQ_DB8500_SDMMC5 (IRQ_SHPI_START + 100) +-#define IRQ_DB8500_HSIRD0 (IRQ_SHPI_START + 104) +-#define IRQ_DB8500_HSIRD1 (IRQ_SHPI_START + 105) +-#define IRQ_DB8500_HSITD0 (IRQ_SHPI_START + 106) +-#define IRQ_DB8500_HSITD1 (IRQ_SHPI_START + 107) +-#define IRQ_DB8500_CTI0 (IRQ_SHPI_START + 108) +-#define IRQ_DB8500_CTI1 (IRQ_SHPI_START + 109) +-#define IRQ_DB8500_ICN_ERR (IRQ_SHPI_START + 110) +-#define IRQ_DB8500_MALI_PPMMU (IRQ_SHPI_START + 112) +-#define IRQ_DB8500_MALI_PP (IRQ_SHPI_START + 113) +-#define IRQ_DB8500_MALI_GPMMU (IRQ_SHPI_START + 114) +-#define IRQ_DB8500_MALI_GP (IRQ_SHPI_START + 115) +-#define IRQ_DB8500_MALI (IRQ_SHPI_START + 116) +-#define IRQ_DB8500_PRCMU_SEM (IRQ_SHPI_START + 118) +-#define IRQ_DB8500_GPIO0 (IRQ_SHPI_START + 119) +-#define IRQ_DB8500_GPIO1 (IRQ_SHPI_START + 120) +-#define IRQ_DB8500_GPIO2 (IRQ_SHPI_START + 121) +-#define IRQ_DB8500_GPIO3 (IRQ_SHPI_START + 122) +-#define IRQ_DB8500_GPIO4 (IRQ_SHPI_START + 123) +-#define IRQ_DB8500_GPIO5 (IRQ_SHPI_START + 124) +-#define IRQ_DB8500_GPIO6 (IRQ_SHPI_START + 125) +-#define IRQ_DB8500_GPIO7 (IRQ_SHPI_START + 126) +-#define IRQ_DB8500_GPIO8 (IRQ_SHPI_START + 127) +- +-#define IRQ_CA_WAKE_REQ_ED (IRQ_SHPI_START + 71) +-#define IRQ_AC_READ_NOTIFICATION_0_ED (IRQ_SHPI_START + 66) +-#define IRQ_AC_READ_NOTIFICATION_1_ED (IRQ_SHPI_START + 64) +-#define IRQ_CA_MSG_PEND_NOTIFICATION_0_ED (IRQ_SHPI_START + 67) +-#define IRQ_CA_MSG_PEND_NOTIFICATION_1_ED (IRQ_SHPI_START + 65) +- +-#define IRQ_CA_WAKE_REQ_V1 (IRQ_SHPI_START + 83) +-#define IRQ_AC_READ_NOTIFICATION_0_V1 (IRQ_SHPI_START + 78) +-#define IRQ_AC_READ_NOTIFICATION_1_V1 (IRQ_SHPI_START + 76) +-#define IRQ_CA_MSG_PEND_NOTIFICATION_0_V1 (IRQ_SHPI_START + 79) +-#define IRQ_CA_MSG_PEND_NOTIFICATION_1_V1 (IRQ_SHPI_START + 77) +- +-#ifdef CONFIG_UX500_SOC_DB8500 +- +-/* Virtual interrupts corresponding to the PRCMU wakeups. */ +-#define IRQ_PRCMU_BASE IRQ_SOC_START +-#define IRQ_PRCMU_END (IRQ_PRCMU_BASE + 23) +- +-/* +- * We may have several SoCs, but only one will run at a +- * time, so the one with most IRQs will bump this ahead, +- * but the IRQ_SOC_START remains the same for either SoC. +- */ +-#if IRQ_SOC_END < IRQ_PRCMU_END +-#undef IRQ_SOC_END +-#define IRQ_SOC_END IRQ_PRCMU_END +-#endif +- +-#endif /* CONFIG_UX500_SOC_DB8500 */ +-#endif +diff -Nur linux-3.14.36/arch/arm/mach-ux500/irqs.h linux-openelec/arch/arm/mach-ux500/irqs.h +--- linux-3.14.36/arch/arm/mach-ux500/irqs.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-ux500/irqs.h 1969-12-31 18:00:00.000000000 -0600 +@@ -1,49 +0,0 @@ +-/* +- * Copyright (C) 2008 STMicroelectronics +- * Copyright (C) 2009 ST-Ericsson. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- */ +-#ifndef ASM_ARCH_IRQS_H +-#define ASM_ARCH_IRQS_H +- +-#define IRQ_LOCALTIMER 29 +-#define IRQ_LOCALWDOG 30 +- +-/* Shared Peripheral Interrupt (SHPI) */ +-#define IRQ_SHPI_START 32 +- +-/* +- * MTU0 preserved for now until plat-nomadik is taught not to use it. Don't +- * add any other IRQs here, use the irqs-dbx500.h files. +- */ +-#define IRQ_MTU0 (IRQ_SHPI_START + 4) +- +-#define DBX500_NR_INTERNAL_IRQS 166 +- +-/* After chip-specific IRQ numbers we have the GPIO ones */ +-#define NOMADIK_NR_GPIO 288 +-#define NOMADIK_GPIO_TO_IRQ(gpio) ((gpio) + DBX500_NR_INTERNAL_IRQS) +-#define NOMADIK_IRQ_TO_GPIO(irq) ((irq) - DBX500_NR_INTERNAL_IRQS) +-#define IRQ_GPIO_END NOMADIK_GPIO_TO_IRQ(NOMADIK_NR_GPIO) +- +-#define IRQ_SOC_START IRQ_GPIO_END +-/* This will be overridden by SoC-specific irq headers */ +-#define IRQ_SOC_END IRQ_SOC_START +- +-#include "irqs-db8500.h" +- +-#define IRQ_BOARD_START IRQ_SOC_END +-/* This will be overridden by board-specific irq headers */ +-#define IRQ_BOARD_END IRQ_BOARD_START +- +-#ifdef CONFIG_MACH_MOP500 +-#include "irqs-board-mop500.h" +-#endif +- +-#define UX500_NR_IRQS IRQ_BOARD_END +- +-#endif /* ASM_ARCH_IRQS_H */ +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/ct-ca9x4.c linux-openelec/arch/arm/mach-vexpress/ct-ca9x4.c +--- linux-3.14.36/arch/arm/mach-vexpress/ct-ca9x4.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-vexpress/ct-ca9x4.c 2015-05-06 12:05:43.000000000 -0500 +@@ -45,6 +45,23 @@ + iotable_init(ct_ca9x4_io_desc, ARRAY_SIZE(ct_ca9x4_io_desc)); + } + ++static void __init ca9x4_l2_init(void) ++{ ++#ifdef CONFIG_CACHE_L2X0 ++ void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K); ++ ++ if (l2x0_base) { ++ /* set RAM latencies to 1 cycle for this core tile. */ ++ writel(0, l2x0_base + L310_TAG_LATENCY_CTRL); ++ writel(0, l2x0_base + L310_DATA_LATENCY_CTRL); ++ ++ l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff); ++ } else { ++ pr_err("L2C: unable to map L2 cache controller\n"); ++ } ++#endif ++} ++ + #ifdef CONFIG_HAVE_ARM_TWD + static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, A9_MPCORE_TWD, IRQ_LOCALTIMER); + +@@ -63,6 +80,7 @@ + gic_init(0, 29, ioremap(A9_MPCORE_GIC_DIST, SZ_4K), + ioremap(A9_MPCORE_GIC_CPU, SZ_256)); + ca9x4_twd_init(); ++ ca9x4_l2_init(); + } + + static int ct_ca9x4_clcd_setup(struct clcd_fb *fb) +@@ -141,16 +159,6 @@ + { + int i; + +-#ifdef CONFIG_CACHE_L2X0 +- void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K); +- +- /* set RAM latencies to 1 cycle for this core tile. */ +- writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL); +- writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL); +- +- l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff); +-#endif +- + for (i = 0; i < ARRAY_SIZE(ct_ca9x4_amba_devs); i++) + amba_device_register(ct_ca9x4_amba_devs[i], &iomem_resource); + +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/dcscb.c linux-openelec/arch/arm/mach-vexpress/dcscb.c +--- linux-3.14.36/arch/arm/mach-vexpress/dcscb.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-vexpress/dcscb.c 2015-05-06 12:05:43.000000000 -0500 +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + + #define RST_HOLD0 0x0 +@@ -193,6 +194,12 @@ + unsigned int cfg; + int ret; + ++ ret = psci_probe(); ++ if (!ret) { ++ pr_debug("psci found. Aborting native init\n"); ++ return -ENODEV; ++ } ++ + if (!cci_probed()) + return -ENODEV; + +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/Kconfig linux-openelec/arch/arm/mach-vexpress/Kconfig +--- linux-3.14.36/arch/arm/mach-vexpress/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-vexpress/Kconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -55,6 +55,7 @@ + + config ARCH_VEXPRESS_CA9X4 + bool "Versatile Express Cortex-A9x4 tile" ++ select ARM_ERRATA_643719 + + config ARCH_VEXPRESS_DCSCB + bool "Dual Cluster System Control Block (DCSCB) support" +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/Makefile linux-openelec/arch/arm/mach-vexpress/Makefile +--- linux-3.14.36/arch/arm/mach-vexpress/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-vexpress/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -8,8 +8,15 @@ + obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o + obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o + CFLAGS_dcscb.o += -march=armv7-a ++CFLAGS_REMOVE_dcscb.o = -pg + obj-$(CONFIG_ARCH_VEXPRESS_SPC) += spc.o ++CFLAGS_REMOVE_spc.o = -pg + obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o + CFLAGS_tc2_pm.o += -march=armv7-a ++CFLAGS_REMOVE_tc2_pm.o = -pg ++ifeq ($(CONFIG_ARCH_VEXPRESS_TC2_PM),y) ++obj-$(CONFIG_ARM_PSCI) += tc2_pm_psci.o ++CFLAGS_REMOVE_tc2_pm_psci.o = -pg ++endif + obj-$(CONFIG_SMP) += platsmp.o + obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/spc.c linux-openelec/arch/arm/mach-vexpress/spc.c +--- linux-3.14.36/arch/arm/mach-vexpress/spc.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-vexpress/spc.c 2015-05-06 12:05:43.000000000 -0500 +@@ -392,7 +392,7 @@ + * +--------------------------+ + * | 31 20 | 19 0 | + * +--------------------------+ +- * | u_volt | freq(kHz) | ++ * | m_volt | freq(kHz) | + * +--------------------------+ + */ + #define MULT_FACTOR 20 +@@ -414,7 +414,7 @@ + ret = ve_spc_read_sys_cfg(SYSCFG_SCC, off, &data); + if (!ret) { + opps->freq = (data & FREQ_MASK) * MULT_FACTOR; +- opps->u_volt = data >> VOLT_SHIFT; ++ opps->u_volt = (data >> VOLT_SHIFT) * 1000; + } else { + break; + } +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/tc2_pm.c linux-openelec/arch/arm/mach-vexpress/tc2_pm.c +--- linux-3.14.36/arch/arm/mach-vexpress/tc2_pm.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-vexpress/tc2_pm.c 2015-05-06 12:05:43.000000000 -0500 +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + #include + +@@ -329,6 +330,12 @@ + u32 a15_cluster_id, a7_cluster_id, sys_info; + struct device_node *np; + ++ ret = psci_probe(); ++ if (!ret) { ++ pr_debug("psci found. Aborting native init\n"); ++ return -ENODEV; ++ } ++ + /* + * The power management-related features are hidden behind + * SCC registers. We need to extract runtime information like +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/tc2_pm_psci.c linux-openelec/arch/arm/mach-vexpress/tc2_pm_psci.c +--- linux-3.14.36/arch/arm/mach-vexpress/tc2_pm_psci.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mach-vexpress/tc2_pm_psci.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,173 @@ ++/* ++ * arch/arm/mach-vexpress/tc2_pm_psci.c - TC2 PSCI support ++ * ++ * Created by: Achin Gupta, December 2012 ++ * Copyright: (C) 2012 ARM Limited ++ * ++ * Some portions of this file were originally written by Nicolas Pitre ++ * Copyright: (C) 2012 Linaro Limited ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++/* ++ * Platform specific state id understood by the firmware and used to ++ * program the power controller ++ */ ++#define PSCI_POWER_STATE_ID 0 ++ ++#define TC2_CLUSTERS 2 ++#define TC2_MAX_CPUS_PER_CLUSTER 3 ++ ++static atomic_t tc2_pm_use_count[TC2_MAX_CPUS_PER_CLUSTER][TC2_CLUSTERS]; ++ ++static int tc2_pm_psci_power_up(unsigned int cpu, unsigned int cluster) ++{ ++ unsigned int mpidr = (cluster << 8) | cpu; ++ int ret = 0; ++ ++ BUG_ON(!psci_ops.cpu_on); ++ ++ switch (atomic_inc_return(&tc2_pm_use_count[cpu][cluster])) { ++ case 1: ++ /* ++ * This is a request to power up a cpu that linux thinks has ++ * been powered down. Retries are needed if the firmware has ++ * seen the power down request as yet. ++ */ ++ do ++ ret = psci_ops.cpu_on(mpidr, ++ virt_to_phys(mcpm_entry_point)); ++ while (ret == -EAGAIN); ++ ++ return ret; ++ case 2: ++ /* This power up request has overtaken a power down request */ ++ return ret; ++ default: ++ /* Any other value is a bug */ ++ BUG(); ++ } ++} ++ ++static void tc2_pm_psci_power_down(void) ++{ ++ struct psci_power_state power_state; ++ unsigned int mpidr, cpu, cluster; ++ ++ mpidr = read_cpuid_mpidr(); ++ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); ++ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); ++ ++ BUG_ON(!psci_ops.cpu_off); ++ ++ switch (atomic_dec_return(&tc2_pm_use_count[cpu][cluster])) { ++ case 1: ++ /* ++ * Overtaken by a power up. Flush caches, exit coherency, ++ * return & fake a reset ++ */ ++ set_cr(get_cr() & ~CR_C); ++ ++ flush_cache_louis(); ++ ++ asm volatile ("clrex"); ++ set_auxcr(get_auxcr() & ~(1 << 6)); ++ ++ return; ++ case 0: ++ /* A normal request to possibly power down the cluster */ ++ power_state.id = PSCI_POWER_STATE_ID; ++ power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN; ++ power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1; ++ ++ psci_ops.cpu_off(power_state); ++ ++ /* On success this function never returns */ ++ default: ++ /* Any other value is a bug */ ++ BUG(); ++ } ++} ++ ++static void tc2_pm_psci_suspend(u64 unused) ++{ ++ struct psci_power_state power_state; ++ ++ BUG_ON(!psci_ops.cpu_suspend); ++ ++ /* On TC2 always attempt to power down the cluster */ ++ power_state.id = PSCI_POWER_STATE_ID; ++ power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN; ++ power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1; ++ ++ psci_ops.cpu_suspend(power_state, virt_to_phys(mcpm_entry_point)); ++ ++ /* On success this function never returns */ ++ BUG(); ++} ++ ++static const struct mcpm_platform_ops tc2_pm_power_ops = { ++ .power_up = tc2_pm_psci_power_up, ++ .power_down = tc2_pm_psci_power_down, ++ .suspend = tc2_pm_psci_suspend, ++}; ++ ++static void __init tc2_pm_usage_count_init(void) ++{ ++ unsigned int mpidr, cpu, cluster; ++ ++ mpidr = read_cpuid_mpidr(); ++ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); ++ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); ++ ++ pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); ++ BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER); ++ ++ atomic_set(&tc2_pm_use_count[cpu][cluster], 1); ++} ++ ++static int __init tc2_pm_psci_init(void) ++{ ++ int ret; ++ ++ ret = psci_probe(); ++ if (ret) { ++ pr_debug("psci not found. Aborting psci init\n"); ++ return -ENODEV; ++ } ++ ++ if (!of_machine_is_compatible("arm,vexpress,v2p-ca15_a7")) ++ return -ENODEV; ++ ++ tc2_pm_usage_count_init(); ++ ++ ret = mcpm_platform_register(&tc2_pm_power_ops); ++ if (!ret) ++ ret = mcpm_sync_init(NULL); ++ if (!ret) ++ pr_info("TC2 power management using PSCI initialized\n"); ++ return ret; ++} ++ ++early_initcall(tc2_pm_psci_init); +diff -Nur linux-3.14.36/arch/arm/mach-vexpress/v2m.c linux-openelec/arch/arm/mach-vexpress/v2m.c +--- linux-3.14.36/arch/arm/mach-vexpress/v2m.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-vexpress/v2m.c 2015-05-06 12:05:43.000000000 -0500 +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -369,6 +370,31 @@ + .init_machine = v2m_init, + MACHINE_END + ++static void __init v2m_dt_hdlcd_init(void) ++{ ++ struct device_node *node; ++ int len, na, ns; ++ const __be32 *prop; ++ phys_addr_t fb_base, fb_size; ++ ++ node = of_find_compatible_node(NULL, NULL, "arm,hdlcd"); ++ if (!node) ++ return; ++ ++ na = of_n_addr_cells(node); ++ ns = of_n_size_cells(node); ++ ++ prop = of_get_property(node, "framebuffer", &len); ++ if (WARN_ON(!prop || len < (na + ns) * sizeof(*prop))) ++ return; ++ ++ fb_base = of_read_number(prop, na); ++ fb_size = of_read_number(prop + na, ns); ++ ++ if (WARN_ON(memblock_remove(fb_base, fb_size))) ++ return; ++}; ++ + static struct map_desc v2m_rs1_io_desc __initdata = { + .virtual = V2M_PERIPH, + .pfn = __phys_to_pfn(0x1c000000), +@@ -421,6 +447,8 @@ + } + + versatile_sched_clock_init(vexpress_get_24mhz_clock_base(), 24000000); ++ ++ v2m_dt_hdlcd_init(); + } + + static const struct of_device_id v2m_dt_bus_match[] __initconst = { +diff -Nur linux-3.14.36/arch/arm/mach-zynq/common.c linux-openelec/arch/arm/mach-zynq/common.c +--- linux-3.14.36/arch/arm/mach-zynq/common.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mach-zynq/common.c 2015-05-06 12:05:43.000000000 -0500 +@@ -67,7 +67,7 @@ + /* + * 64KB way size, 8-way associativity, parity disabled + */ +- l2x0_of_init(0x02060000, 0xF0F0FFFF); ++ l2x0_of_init(0x02000000, 0xf0ffffff); + + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + +diff -Nur linux-3.14.36/arch/arm/mm/cache-feroceon-l2.c linux-openelec/arch/arm/mm/cache-feroceon-l2.c +--- linux-3.14.36/arch/arm/mm/cache-feroceon-l2.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/cache-feroceon-l2.c 2015-05-06 12:05:43.000000000 -0500 +@@ -343,7 +343,6 @@ + outer_cache.inv_range = feroceon_l2_inv_range; + outer_cache.clean_range = feroceon_l2_clean_range; + outer_cache.flush_range = feroceon_l2_flush_range; +- outer_cache.inv_all = l2_inv_all; + + enable_l2(); + +diff -Nur linux-3.14.36/arch/arm/mm/cache-l2x0.c linux-openelec/arch/arm/mm/cache-l2x0.c +--- linux-3.14.36/arch/arm/mm/cache-l2x0.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/cache-l2x0.c 2015-05-06 12:05:43.000000000 -0500 +@@ -16,18 +16,33 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ ++#include + #include + #include ++#include + #include + #include + #include + #include + + #include ++#include ++#include + #include + #include "cache-tauros3.h" + #include "cache-aurora-l2.h" + ++struct l2c_init_data { ++ const char *type; ++ unsigned way_size_0; ++ unsigned num_lock; ++ void (*of_parse)(const struct device_node *, u32 *, u32 *); ++ void (*enable)(void __iomem *, u32, unsigned); ++ void (*fixup)(void __iomem *, u32, struct outer_cache_fns *); ++ void (*save)(void __iomem *); ++ struct outer_cache_fns outer_cache; ++}; ++ + #define CACHE_LINE_SIZE 32 + + static void __iomem *l2x0_base; +@@ -36,96 +51,116 @@ + static u32 l2x0_size; + static unsigned long sync_reg_offset = L2X0_CACHE_SYNC; + +-/* Aurora don't have the cache ID register available, so we have to +- * pass it though the device tree */ +-static u32 cache_id_part_number_from_dt; +- + struct l2x0_regs l2x0_saved_regs; + +-struct l2x0_of_data { +- void (*setup)(const struct device_node *, u32 *, u32 *); +- void (*save)(void); +- struct outer_cache_fns outer_cache; +-}; +- +-static bool of_init = false; +- +-static inline void cache_wait_way(void __iomem *reg, unsigned long mask) ++/* ++ * Common code for all cache controllers. ++ */ ++static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask) + { + /* wait for cache operation by line or way to complete */ + while (readl_relaxed(reg) & mask) + cpu_relax(); + } + +-#ifdef CONFIG_CACHE_PL310 +-static inline void cache_wait(void __iomem *reg, unsigned long mask) ++/* ++ * By default, we write directly to secure registers. Platforms must ++ * override this if they are running non-secure. ++ */ ++static void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg) + { +- /* cache operations by line are atomic on PL310 */ ++ if (val == readl_relaxed(base + reg)) ++ return; ++ if (outer_cache.write_sec) ++ outer_cache.write_sec(val, reg); ++ else ++ writel_relaxed(val, base + reg); + } +-#else +-#define cache_wait cache_wait_way +-#endif + +-static inline void cache_sync(void) ++/* ++ * This should only be called when we have a requirement that the ++ * register be written due to a work-around, as platforms running ++ * in non-secure mode may not be able to access this register. ++ */ ++static inline void l2c_set_debug(void __iomem *base, unsigned long val) + { +- void __iomem *base = l2x0_base; +- +- writel_relaxed(0, base + sync_reg_offset); +- cache_wait(base + L2X0_CACHE_SYNC, 1); ++ l2c_write_sec(val, base, L2X0_DEBUG_CTRL); + } + +-static inline void l2x0_clean_line(unsigned long addr) ++static void __l2c_op_way(void __iomem *reg) + { +- void __iomem *base = l2x0_base; +- cache_wait(base + L2X0_CLEAN_LINE_PA, 1); +- writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA); ++ writel_relaxed(l2x0_way_mask, reg); ++ l2c_wait_mask(reg, l2x0_way_mask); + } + +-static inline void l2x0_inv_line(unsigned long addr) ++static inline void l2c_unlock(void __iomem *base, unsigned num) + { +- void __iomem *base = l2x0_base; +- cache_wait(base + L2X0_INV_LINE_PA, 1); +- writel_relaxed(addr, base + L2X0_INV_LINE_PA); ++ unsigned i; ++ ++ for (i = 0; i < num; i++) { ++ writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_D_BASE + ++ i * L2X0_LOCKDOWN_STRIDE); ++ writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_I_BASE + ++ i * L2X0_LOCKDOWN_STRIDE); ++ } + } + +-#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) +-static inline void debug_writel(unsigned long val) ++/* ++ * Enable the L2 cache controller. This function must only be ++ * called when the cache controller is known to be disabled. ++ */ ++static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) + { +- if (outer_cache.set_debug) +- outer_cache.set_debug(val); ++ unsigned long flags; ++ ++ l2c_write_sec(aux, base, L2X0_AUX_CTRL); ++ ++ l2c_unlock(base, num_lock); ++ ++ local_irq_save(flags); ++ __l2c_op_way(base + L2X0_INV_WAY); ++ writel_relaxed(0, base + sync_reg_offset); ++ l2c_wait_mask(base + sync_reg_offset, 1); ++ local_irq_restore(flags); ++ ++ l2c_write_sec(L2X0_CTRL_EN, base, L2X0_CTRL); + } + +-static void pl310_set_debug(unsigned long val) ++static void l2c_disable(void) + { +- writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); ++ void __iomem *base = l2x0_base; ++ ++ outer_cache.flush_all(); ++ l2c_write_sec(0, base, L2X0_CTRL); ++ dsb(st); + } +-#else +-/* Optimised out for non-errata case */ +-static inline void debug_writel(unsigned long val) ++ ++#ifdef CONFIG_CACHE_PL310 ++static inline void cache_wait(void __iomem *reg, unsigned long mask) + { ++ /* cache operations by line are atomic on PL310 */ + } +- +-#define pl310_set_debug NULL ++#else ++#define cache_wait l2c_wait_mask + #endif + +-#ifdef CONFIG_PL310_ERRATA_588369 +-static inline void l2x0_flush_line(unsigned long addr) ++static inline void cache_sync(void) + { + void __iomem *base = l2x0_base; + +- /* Clean by PA followed by Invalidate by PA */ +- cache_wait(base + L2X0_CLEAN_LINE_PA, 1); +- writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA); +- cache_wait(base + L2X0_INV_LINE_PA, 1); +- writel_relaxed(addr, base + L2X0_INV_LINE_PA); ++ writel_relaxed(0, base + sync_reg_offset); ++ cache_wait(base + L2X0_CACHE_SYNC, 1); + } +-#else + +-static inline void l2x0_flush_line(unsigned long addr) ++#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) ++static inline void debug_writel(unsigned long val) ++{ ++ l2c_set_debug(l2x0_base, val); ++} ++#else ++/* Optimised out for non-errata case */ ++static inline void debug_writel(unsigned long val) + { +- void __iomem *base = l2x0_base; +- cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1); +- writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA); + } + #endif + +@@ -141,8 +176,7 @@ + static void __l2x0_flush_all(void) + { + debug_writel(0x03); +- writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY); +- cache_wait_way(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask); ++ __l2c_op_way(l2x0_base + L2X0_CLEAN_INV_WAY); + cache_sync(); + debug_writel(0x00); + } +@@ -157,274 +191,882 @@ + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } + +-static void l2x0_clean_all(void) ++static void l2x0_disable(void) + { + unsigned long flags; + +- /* clean all ways */ + raw_spin_lock_irqsave(&l2x0_lock, flags); +- writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY); +- cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask); +- cache_sync(); ++ __l2x0_flush_all(); ++ l2c_write_sec(0, l2x0_base, L2X0_CTRL); ++ dsb(st); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } + +-static void l2x0_inv_all(void) ++static void l2c_save(void __iomem *base) + { +- unsigned long flags; ++ l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); ++} + +- /* invalidate all ways */ +- raw_spin_lock_irqsave(&l2x0_lock, flags); +- /* Invalidating when L2 is enabled is a nono */ +- BUG_ON(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN); +- writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY); +- cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask); +- cache_sync(); +- raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++/* ++ * L2C-210 specific code. ++ * ++ * The L2C-2x0 PA, set/way and sync operations are atomic, but we must ++ * ensure that no background operation is running. The way operations ++ * are all background tasks. ++ * ++ * While a background operation is in progress, any new operation is ++ * ignored (unspecified whether this causes an error.) Thankfully, not ++ * used on SMP. ++ * ++ * Never has a different sync register other than L2X0_CACHE_SYNC, but ++ * we use sync_reg_offset here so we can share some of this with L2C-310. ++ */ ++static void __l2c210_cache_sync(void __iomem *base) ++{ ++ writel_relaxed(0, base + sync_reg_offset); + } + +-static void l2x0_inv_range(unsigned long start, unsigned long end) ++static void __l2c210_op_pa_range(void __iomem *reg, unsigned long start, ++ unsigned long end) ++{ ++ while (start < end) { ++ writel_relaxed(start, reg); ++ start += CACHE_LINE_SIZE; ++ } ++} ++ ++static void l2c210_inv_range(unsigned long start, unsigned long end) + { + void __iomem *base = l2x0_base; +- unsigned long flags; + +- raw_spin_lock_irqsave(&l2x0_lock, flags); + if (start & (CACHE_LINE_SIZE - 1)) { + start &= ~(CACHE_LINE_SIZE - 1); +- debug_writel(0x03); +- l2x0_flush_line(start); +- debug_writel(0x00); ++ writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } + + if (end & (CACHE_LINE_SIZE - 1)) { + end &= ~(CACHE_LINE_SIZE - 1); +- debug_writel(0x03); +- l2x0_flush_line(end); +- debug_writel(0x00); ++ writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA); + } + ++ __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end); ++ __l2c210_cache_sync(base); ++} ++ ++static void l2c210_clean_range(unsigned long start, unsigned long end) ++{ ++ void __iomem *base = l2x0_base; ++ ++ start &= ~(CACHE_LINE_SIZE - 1); ++ __l2c210_op_pa_range(base + L2X0_CLEAN_LINE_PA, start, end); ++ __l2c210_cache_sync(base); ++} ++ ++static void l2c210_flush_range(unsigned long start, unsigned long end) ++{ ++ void __iomem *base = l2x0_base; ++ ++ start &= ~(CACHE_LINE_SIZE - 1); ++ __l2c210_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, start, end); ++ __l2c210_cache_sync(base); ++} ++ ++static void l2c210_flush_all(void) ++{ ++ void __iomem *base = l2x0_base; ++ ++ BUG_ON(!irqs_disabled()); ++ ++ __l2c_op_way(base + L2X0_CLEAN_INV_WAY); ++ __l2c210_cache_sync(base); ++} ++ ++static void l2c210_sync(void) ++{ ++ __l2c210_cache_sync(l2x0_base); ++} ++ ++static void l2c210_resume(void) ++{ ++ void __iomem *base = l2x0_base; ++ ++ if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) ++ l2c_enable(base, l2x0_saved_regs.aux_ctrl, 1); ++} ++ ++static const struct l2c_init_data l2c210_data __initconst = { ++ .type = "L2C-210", ++ .way_size_0 = SZ_8K, ++ .num_lock = 1, ++ .enable = l2c_enable, ++ .save = l2c_save, ++ .outer_cache = { ++ .inv_range = l2c210_inv_range, ++ .clean_range = l2c210_clean_range, ++ .flush_range = l2c210_flush_range, ++ .flush_all = l2c210_flush_all, ++ .disable = l2c_disable, ++ .sync = l2c210_sync, ++ .resume = l2c210_resume, ++ }, ++}; ++ ++/* ++ * L2C-220 specific code. ++ * ++ * All operations are background operations: they have to be waited for. ++ * Conflicting requests generate a slave error (which will cause an ++ * imprecise abort.) Never uses sync_reg_offset, so we hard-code the ++ * sync register here. ++ * ++ * However, we can re-use the l2c210_resume call. ++ */ ++static inline void __l2c220_cache_sync(void __iomem *base) ++{ ++ writel_relaxed(0, base + L2X0_CACHE_SYNC); ++ l2c_wait_mask(base + L2X0_CACHE_SYNC, 1); ++} ++ ++static void l2c220_op_way(void __iomem *base, unsigned reg) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&l2x0_lock, flags); ++ __l2c_op_way(base + reg); ++ __l2c220_cache_sync(base); ++ raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++} ++ ++static unsigned long l2c220_op_pa_range(void __iomem *reg, unsigned long start, ++ unsigned long end, unsigned long flags) ++{ ++ raw_spinlock_t *lock = &l2x0_lock; ++ + while (start < end) { + unsigned long blk_end = start + min(end - start, 4096UL); + + while (start < blk_end) { +- l2x0_inv_line(start); ++ l2c_wait_mask(reg, 1); ++ writel_relaxed(start, reg); + start += CACHE_LINE_SIZE; + } + + if (blk_end < end) { +- raw_spin_unlock_irqrestore(&l2x0_lock, flags); +- raw_spin_lock_irqsave(&l2x0_lock, flags); ++ raw_spin_unlock_irqrestore(lock, flags); ++ raw_spin_lock_irqsave(lock, flags); + } + } +- cache_wait(base + L2X0_INV_LINE_PA, 1); +- cache_sync(); +- raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++ ++ return flags; + } + +-static void l2x0_clean_range(unsigned long start, unsigned long end) ++static void l2c220_inv_range(unsigned long start, unsigned long end) + { + void __iomem *base = l2x0_base; + unsigned long flags; + +- if ((end - start) >= l2x0_size) { +- l2x0_clean_all(); +- return; +- } +- + raw_spin_lock_irqsave(&l2x0_lock, flags); +- start &= ~(CACHE_LINE_SIZE - 1); +- while (start < end) { +- unsigned long blk_end = start + min(end - start, 4096UL); +- +- while (start < blk_end) { +- l2x0_clean_line(start); ++ if ((start | end) & (CACHE_LINE_SIZE - 1)) { ++ if (start & (CACHE_LINE_SIZE - 1)) { ++ start &= ~(CACHE_LINE_SIZE - 1); ++ writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } + +- if (blk_end < end) { +- raw_spin_unlock_irqrestore(&l2x0_lock, flags); +- raw_spin_lock_irqsave(&l2x0_lock, flags); ++ if (end & (CACHE_LINE_SIZE - 1)) { ++ end &= ~(CACHE_LINE_SIZE - 1); ++ l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); ++ writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA); + } + } +- cache_wait(base + L2X0_CLEAN_LINE_PA, 1); +- cache_sync(); ++ ++ flags = l2c220_op_pa_range(base + L2X0_INV_LINE_PA, ++ start, end, flags); ++ l2c_wait_mask(base + L2X0_INV_LINE_PA, 1); ++ __l2c220_cache_sync(base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } + +-static void l2x0_flush_range(unsigned long start, unsigned long end) ++static void l2c220_clean_range(unsigned long start, unsigned long end) + { + void __iomem *base = l2x0_base; + unsigned long flags; + ++ start &= ~(CACHE_LINE_SIZE - 1); + if ((end - start) >= l2x0_size) { +- l2x0_flush_all(); ++ l2c220_op_way(base, L2X0_CLEAN_WAY); + return; + } + + raw_spin_lock_irqsave(&l2x0_lock, flags); ++ flags = l2c220_op_pa_range(base + L2X0_CLEAN_LINE_PA, ++ start, end, flags); ++ l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); ++ __l2c220_cache_sync(base); ++ raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++} ++ ++static void l2c220_flush_range(unsigned long start, unsigned long end) ++{ ++ void __iomem *base = l2x0_base; ++ unsigned long flags; ++ + start &= ~(CACHE_LINE_SIZE - 1); ++ if ((end - start) >= l2x0_size) { ++ l2c220_op_way(base, L2X0_CLEAN_INV_WAY); ++ return; ++ } ++ ++ raw_spin_lock_irqsave(&l2x0_lock, flags); ++ flags = l2c220_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, ++ start, end, flags); ++ l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); ++ __l2c220_cache_sync(base); ++ raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++} ++ ++static void l2c220_flush_all(void) ++{ ++ l2c220_op_way(l2x0_base, L2X0_CLEAN_INV_WAY); ++} ++ ++static void l2c220_sync(void) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&l2x0_lock, flags); ++ __l2c220_cache_sync(l2x0_base); ++ raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++} ++ ++static void l2c220_enable(void __iomem *base, u32 aux, unsigned num_lock) ++{ ++ /* ++ * Always enable non-secure access to the lockdown registers - ++ * we write to them as part of the L2C enable sequence so they ++ * need to be accessible. ++ */ ++ aux |= L220_AUX_CTRL_NS_LOCKDOWN; ++ ++ l2c_enable(base, aux, num_lock); ++} ++ ++static const struct l2c_init_data l2c220_data = { ++ .type = "L2C-220", ++ .way_size_0 = SZ_8K, ++ .num_lock = 1, ++ .enable = l2c220_enable, ++ .save = l2c_save, ++ .outer_cache = { ++ .inv_range = l2c220_inv_range, ++ .clean_range = l2c220_clean_range, ++ .flush_range = l2c220_flush_range, ++ .flush_all = l2c220_flush_all, ++ .disable = l2c_disable, ++ .sync = l2c220_sync, ++ .resume = l2c210_resume, ++ }, ++}; ++ ++/* ++ * L2C-310 specific code. ++ * ++ * Very similar to L2C-210, the PA, set/way and sync operations are atomic, ++ * and the way operations are all background tasks. However, issuing an ++ * operation while a background operation is in progress results in a ++ * SLVERR response. We can reuse: ++ * ++ * __l2c210_cache_sync (using sync_reg_offset) ++ * l2c210_sync ++ * l2c210_inv_range (if 588369 is not applicable) ++ * l2c210_clean_range ++ * l2c210_flush_range (if 588369 is not applicable) ++ * l2c210_flush_all (if 727915 is not applicable) ++ * ++ * Errata: ++ * 588369: PL310 R0P0->R1P0, fixed R2P0. ++ * Affects: all clean+invalidate operations ++ * clean and invalidate skips the invalidate step, so we need to issue ++ * separate operations. We also require the above debug workaround ++ * enclosing this code fragment on affected parts. On unaffected parts, ++ * we must not use this workaround without the debug register writes ++ * to avoid exposing a problem similar to 727915. ++ * ++ * 727915: PL310 R2P0->R3P0, fixed R3P1. ++ * Affects: clean+invalidate by way ++ * clean and invalidate by way runs in the background, and a store can ++ * hit the line between the clean operation and invalidate operation, ++ * resulting in the store being lost. ++ * ++ * 752271: PL310 R3P0->R3P1-50REL0, fixed R3P2. ++ * Affects: 8x64-bit (double fill) line fetches ++ * double fill line fetches can fail to cause dirty data to be evicted ++ * from the cache before the new data overwrites the second line. ++ * ++ * 753970: PL310 R3P0, fixed R3P1. ++ * Affects: sync ++ * prevents merging writes after the sync operation, until another L2C ++ * operation is performed (or a number of other conditions.) ++ * ++ * 769419: PL310 R0P0->R3P1, fixed R3P2. ++ * Affects: store buffer ++ * store buffer is not automatically drained. ++ */ ++static void l2c310_inv_range_erratum(unsigned long start, unsigned long end) ++{ ++ void __iomem *base = l2x0_base; ++ ++ if ((start | end) & (CACHE_LINE_SIZE - 1)) { ++ unsigned long flags; ++ ++ /* Erratum 588369 for both clean+invalidate operations */ ++ raw_spin_lock_irqsave(&l2x0_lock, flags); ++ l2c_set_debug(base, 0x03); ++ ++ if (start & (CACHE_LINE_SIZE - 1)) { ++ start &= ~(CACHE_LINE_SIZE - 1); ++ writel_relaxed(start, base + L2X0_CLEAN_LINE_PA); ++ writel_relaxed(start, base + L2X0_INV_LINE_PA); ++ start += CACHE_LINE_SIZE; ++ } ++ ++ if (end & (CACHE_LINE_SIZE - 1)) { ++ end &= ~(CACHE_LINE_SIZE - 1); ++ writel_relaxed(end, base + L2X0_CLEAN_LINE_PA); ++ writel_relaxed(end, base + L2X0_INV_LINE_PA); ++ } ++ ++ l2c_set_debug(base, 0x00); ++ raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++ } ++ ++ __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end); ++ __l2c210_cache_sync(base); ++} ++ ++static void l2c310_flush_range_erratum(unsigned long start, unsigned long end) ++{ ++ raw_spinlock_t *lock = &l2x0_lock; ++ unsigned long flags; ++ void __iomem *base = l2x0_base; ++ ++ raw_spin_lock_irqsave(lock, flags); + while (start < end) { + unsigned long blk_end = start + min(end - start, 4096UL); + +- debug_writel(0x03); ++ l2c_set_debug(base, 0x03); + while (start < blk_end) { +- l2x0_flush_line(start); ++ writel_relaxed(start, base + L2X0_CLEAN_LINE_PA); ++ writel_relaxed(start, base + L2X0_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } +- debug_writel(0x00); ++ l2c_set_debug(base, 0x00); + + if (blk_end < end) { +- raw_spin_unlock_irqrestore(&l2x0_lock, flags); +- raw_spin_lock_irqsave(&l2x0_lock, flags); ++ raw_spin_unlock_irqrestore(lock, flags); ++ raw_spin_lock_irqsave(lock, flags); + } + } +- cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1); +- cache_sync(); +- raw_spin_unlock_irqrestore(&l2x0_lock, flags); ++ raw_spin_unlock_irqrestore(lock, flags); ++ __l2c210_cache_sync(base); + } + +-static void l2x0_disable(void) ++static void l2c310_flush_all_erratum(void) + { ++ void __iomem *base = l2x0_base; + unsigned long flags; + + raw_spin_lock_irqsave(&l2x0_lock, flags); +- __l2x0_flush_all(); +- writel_relaxed(0, l2x0_base + L2X0_CTRL); +- dsb(st); ++ l2c_set_debug(base, 0x03); ++ __l2c_op_way(base + L2X0_CLEAN_INV_WAY); ++ l2c_set_debug(base, 0x00); ++ __l2c210_cache_sync(base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } + +-static void l2x0_unlock(u32 cache_id) ++static void __init l2c310_save(void __iomem *base) + { +- int lockregs; +- int i; ++ unsigned revision; + +- switch (cache_id & L2X0_CACHE_ID_PART_MASK) { +- case L2X0_CACHE_ID_PART_L310: +- lockregs = 8; +- break; +- case AURORA_CACHE_ID: +- lockregs = 4; ++ l2c_save(base); ++ ++ l2x0_saved_regs.tag_latency = readl_relaxed(base + ++ L310_TAG_LATENCY_CTRL); ++ l2x0_saved_regs.data_latency = readl_relaxed(base + ++ L310_DATA_LATENCY_CTRL); ++ l2x0_saved_regs.filter_end = readl_relaxed(base + ++ L310_ADDR_FILTER_END); ++ l2x0_saved_regs.filter_start = readl_relaxed(base + ++ L310_ADDR_FILTER_START); ++ ++ revision = readl_relaxed(base + L2X0_CACHE_ID) & ++ L2X0_CACHE_ID_RTL_MASK; ++ ++ /* From r2p0, there is Prefetch offset/control register */ ++ if (revision >= L310_CACHE_ID_RTL_R2P0) ++ l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + ++ L310_PREFETCH_CTRL); ++ ++ /* From r3p0, there is Power control register */ ++ if (revision >= L310_CACHE_ID_RTL_R3P0) ++ l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + ++ L310_POWER_CTRL); ++} ++ ++static void l2c310_resume(void) ++{ ++ void __iomem *base = l2x0_base; ++ ++ if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) { ++ unsigned revision; ++ ++ /* restore pl310 setup */ ++ writel_relaxed(l2x0_saved_regs.tag_latency, ++ base + L310_TAG_LATENCY_CTRL); ++ writel_relaxed(l2x0_saved_regs.data_latency, ++ base + L310_DATA_LATENCY_CTRL); ++ writel_relaxed(l2x0_saved_regs.filter_end, ++ base + L310_ADDR_FILTER_END); ++ writel_relaxed(l2x0_saved_regs.filter_start, ++ base + L310_ADDR_FILTER_START); ++ ++ revision = readl_relaxed(base + L2X0_CACHE_ID) & ++ L2X0_CACHE_ID_RTL_MASK; ++ ++ if (revision >= L310_CACHE_ID_RTL_R2P0) ++ l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base, ++ L310_PREFETCH_CTRL); ++ if (revision >= L310_CACHE_ID_RTL_R3P0) ++ l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base, ++ L310_POWER_CTRL); ++ ++ l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); ++ ++ /* Re-enable full-line-of-zeros for Cortex-A9 */ ++ if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO) ++ set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); ++ } ++} ++ ++static int l2c310_cpu_enable_flz(struct notifier_block *nb, unsigned long act, void *data) ++{ ++ switch (act & ~CPU_TASKS_FROZEN) { ++ case CPU_STARTING: ++ set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); + break; +- default: +- /* L210 and unknown types */ +- lockregs = 1; ++ case CPU_DYING: ++ set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1))); + break; + } ++ return NOTIFY_OK; ++} + +- for (i = 0; i < lockregs; i++) { +- writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE + +- i * L2X0_LOCKDOWN_STRIDE); +- writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE + +- i * L2X0_LOCKDOWN_STRIDE); ++static void __init l2c310_enable(void __iomem *base, u32 aux, unsigned num_lock) ++{ ++ unsigned rev = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK; ++ bool cortex_a9 = read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9; ++ ++ if (rev >= L310_CACHE_ID_RTL_R2P0) { ++ if (cortex_a9) { ++ aux |= L310_AUX_CTRL_EARLY_BRESP; ++ pr_info("L2C-310 enabling early BRESP for Cortex-A9\n"); ++ } else if (aux & L310_AUX_CTRL_EARLY_BRESP) { ++ pr_warn("L2C-310 early BRESP only supported with Cortex-A9\n"); ++ aux &= ~L310_AUX_CTRL_EARLY_BRESP; ++ } ++ } ++ ++ if (cortex_a9) { ++ u32 aux_cur = readl_relaxed(base + L2X0_AUX_CTRL); ++ u32 acr = get_auxcr(); ++ ++ pr_debug("Cortex-A9 ACR=0x%08x\n", acr); ++ ++ if (acr & BIT(3) && !(aux_cur & L310_AUX_CTRL_FULL_LINE_ZERO)) ++ pr_err("L2C-310: full line of zeros enabled in Cortex-A9 but not L2C-310 - invalid\n"); ++ ++ if (aux & L310_AUX_CTRL_FULL_LINE_ZERO && !(acr & BIT(3))) ++ pr_err("L2C-310: enabling full line of zeros but not enabled in Cortex-A9\n"); ++ ++ if (!(aux & L310_AUX_CTRL_FULL_LINE_ZERO) && !outer_cache.write_sec) { ++ aux |= L310_AUX_CTRL_FULL_LINE_ZERO; ++ pr_info("L2C-310 full line of zeros enabled for Cortex-A9\n"); ++ } ++ } else if (aux & (L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP)) { ++ pr_err("L2C-310: disabling Cortex-A9 specific feature bits\n"); ++ aux &= ~(L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP); ++ } ++ ++ if (aux & (L310_AUX_CTRL_DATA_PREFETCH | L310_AUX_CTRL_INSTR_PREFETCH)) { ++ u32 prefetch = readl_relaxed(base + L310_PREFETCH_CTRL); ++ ++ pr_info("L2C-310 %s%s prefetch enabled, offset %u lines\n", ++ aux & L310_AUX_CTRL_INSTR_PREFETCH ? "I" : "", ++ aux & L310_AUX_CTRL_DATA_PREFETCH ? "D" : "", ++ 1 + (prefetch & L310_PREFETCH_CTRL_OFFSET_MASK)); ++ } ++ ++ /* r3p0 or later has power control register */ ++ if (rev >= L310_CACHE_ID_RTL_R3P0) { ++ u32 power_ctrl; ++ ++ l2c_write_sec(L310_DYNAMIC_CLK_GATING_EN | L310_STNDBY_MODE_EN, ++ base, L310_POWER_CTRL); ++ power_ctrl = readl_relaxed(base + L310_POWER_CTRL); ++ pr_info("L2C-310 dynamic clock gating %sabled, standby mode %sabled\n", ++ power_ctrl & L310_DYNAMIC_CLK_GATING_EN ? "en" : "dis", ++ power_ctrl & L310_STNDBY_MODE_EN ? "en" : "dis"); ++ } ++ ++ /* ++ * Always enable non-secure access to the lockdown registers - ++ * we write to them as part of the L2C enable sequence so they ++ * need to be accessible. ++ */ ++ aux |= L310_AUX_CTRL_NS_LOCKDOWN; ++ ++ l2c_enable(base, aux, num_lock); ++ ++ if (aux & L310_AUX_CTRL_FULL_LINE_ZERO) { ++ set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); ++ cpu_notifier(l2c310_cpu_enable_flz, 0); + } + } + +-void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) ++static void __init l2c310_fixup(void __iomem *base, u32 cache_id, ++ struct outer_cache_fns *fns) + { +- u32 aux; +- u32 cache_id; +- u32 way_size = 0; +- int ways; +- int way_size_shift = L2X0_WAY_SIZE_SHIFT; +- const char *type; ++ unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK; ++ const char *errata[8]; ++ unsigned n = 0; + +- l2x0_base = base; +- if (cache_id_part_number_from_dt) +- cache_id = cache_id_part_number_from_dt; +- else +- cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); +- aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); ++ if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) && ++ revision < L310_CACHE_ID_RTL_R2P0 && ++ /* For bcm compatibility */ ++ fns->inv_range == l2c210_inv_range) { ++ fns->inv_range = l2c310_inv_range_erratum; ++ fns->flush_range = l2c310_flush_range_erratum; ++ errata[n++] = "588369"; ++ } ++ ++ if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) && ++ revision >= L310_CACHE_ID_RTL_R2P0 && ++ revision < L310_CACHE_ID_RTL_R3P1) { ++ fns->flush_all = l2c310_flush_all_erratum; ++ errata[n++] = "727915"; ++ } ++ ++ if (revision >= L310_CACHE_ID_RTL_R3P0 && ++ revision < L310_CACHE_ID_RTL_R3P2) { ++ u32 val = readl_relaxed(base + L310_PREFETCH_CTRL); ++ /* I don't think bit23 is required here... but iMX6 does so */ ++ if (val & (BIT(30) | BIT(23))) { ++ val &= ~(BIT(30) | BIT(23)); ++ l2c_write_sec(val, base, L310_PREFETCH_CTRL); ++ errata[n++] = "752271"; ++ } ++ } ++ ++ if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) && ++ revision == L310_CACHE_ID_RTL_R3P0) { ++ sync_reg_offset = L2X0_DUMMY_REG; ++ errata[n++] = "753970"; ++ } ++ ++ if (IS_ENABLED(CONFIG_PL310_ERRATA_769419)) ++ errata[n++] = "769419"; ++ ++ if (n) { ++ unsigned i; ++ ++ pr_info("L2C-310 errat%s", n > 1 ? "a" : "um"); ++ for (i = 0; i < n; i++) ++ pr_cont(" %s", errata[i]); ++ pr_cont(" enabled\n"); ++ } ++} ++ ++static void l2c310_disable(void) ++{ ++ /* ++ * If full-line-of-zeros is enabled, we must first disable it in the ++ * Cortex-A9 auxiliary control register before disabling the L2 cache. ++ */ ++ if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO) ++ set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1))); + ++ l2c_disable(); ++} ++ ++static const struct l2c_init_data l2c310_init_fns __initconst = { ++ .type = "L2C-310", ++ .way_size_0 = SZ_8K, ++ .num_lock = 8, ++ .enable = l2c310_enable, ++ .fixup = l2c310_fixup, ++ .save = l2c310_save, ++ .outer_cache = { ++ .inv_range = l2c210_inv_range, ++ .clean_range = l2c210_clean_range, ++ .flush_range = l2c210_flush_range, ++ .flush_all = l2c210_flush_all, ++ .disable = l2c310_disable, ++ .sync = l2c210_sync, ++ .resume = l2c310_resume, ++ }, ++}; ++ ++static void __init __l2c_init(const struct l2c_init_data *data, ++ u32 aux_val, u32 aux_mask, u32 cache_id) ++{ ++ struct outer_cache_fns fns; ++ unsigned way_size_bits, ways; ++ u32 aux, old_aux; ++ ++ /* ++ * Sanity check the aux values. aux_mask is the bits we preserve ++ * from reading the hardware register, and aux_val is the bits we ++ * set. ++ */ ++ if (aux_val & aux_mask) ++ pr_alert("L2C: platform provided aux values permit register corruption.\n"); ++ ++ old_aux = aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); + aux &= aux_mask; + aux |= aux_val; + ++ if (old_aux != aux) ++ pr_warn("L2C: DT/platform modifies aux control register: 0x%08x -> 0x%08x\n", ++ old_aux, aux); ++ + /* Determine the number of ways */ + switch (cache_id & L2X0_CACHE_ID_PART_MASK) { + case L2X0_CACHE_ID_PART_L310: ++ if ((aux_val | ~aux_mask) & (L2C_AUX_CTRL_WAY_SIZE_MASK | L310_AUX_CTRL_ASSOCIATIVITY_16)) ++ pr_warn("L2C: DT/platform tries to modify or specify cache size\n"); + if (aux & (1 << 16)) + ways = 16; + else + ways = 8; +- type = "L310"; +-#ifdef CONFIG_PL310_ERRATA_753970 +- /* Unmapped register. */ +- sync_reg_offset = L2X0_DUMMY_REG; +-#endif +- if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0) +- outer_cache.set_debug = pl310_set_debug; + break; ++ + case L2X0_CACHE_ID_PART_L210: ++ case L2X0_CACHE_ID_PART_L220: + ways = (aux >> 13) & 0xf; +- type = "L210"; + break; + + case AURORA_CACHE_ID: +- sync_reg_offset = AURORA_SYNC_REG; + ways = (aux >> 13) & 0xf; + ways = 2 << ((ways + 1) >> 2); +- way_size_shift = AURORA_WAY_SIZE_SHIFT; +- type = "Aurora"; + break; ++ + default: + /* Assume unknown chips have 8 ways */ + ways = 8; +- type = "L2x0 series"; + break; + } + + l2x0_way_mask = (1 << ways) - 1; + + /* +- * L2 cache Size = Way size * Number of ways ++ * way_size_0 is the size that a way_size value of zero would be ++ * given the calculation: way_size = way_size_0 << way_size_bits. ++ * So, if way_size_bits=0 is reserved, but way_size_bits=1 is 16k, ++ * then way_size_0 would be 8k. ++ * ++ * L2 cache size = number of ways * way size. ++ */ ++ way_size_bits = (aux & L2C_AUX_CTRL_WAY_SIZE_MASK) >> ++ L2C_AUX_CTRL_WAY_SIZE_SHIFT; ++ l2x0_size = ways * (data->way_size_0 << way_size_bits); ++ ++ fns = data->outer_cache; ++ fns.write_sec = outer_cache.write_sec; ++ if (data->fixup) ++ data->fixup(l2x0_base, cache_id, &fns); ++ ++ /* ++ * Check if l2x0 controller is already enabled. If we are booting ++ * in non-secure mode accessing the below registers will fault. + */ +- way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17; +- way_size = 1 << (way_size + way_size_shift); ++ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) ++ data->enable(l2x0_base, aux, data->num_lock); + +- l2x0_size = ways * way_size * SZ_1K; ++ outer_cache = fns; + + /* +- * Check if l2x0 controller is already enabled. +- * If you are booting from non-secure mode +- * accessing the below registers will fault. ++ * It is strange to save the register state before initialisation, ++ * but hey, this is what the DT implementations decided to do. + */ +- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { +- /* Make sure that I&D is not locked down when starting */ +- l2x0_unlock(cache_id); ++ if (data->save) ++ data->save(l2x0_base); ++ ++ /* Re-read it in case some bits are reserved. */ ++ aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); ++ ++ pr_info("%s cache controller enabled, %d ways, %d kB\n", ++ data->type, ways, l2x0_size >> 10); ++ pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n", ++ data->type, cache_id, aux); ++} + +- /* l2x0 controller is disabled */ +- writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); ++void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) ++{ ++ const struct l2c_init_data *data; ++ u32 cache_id; + +- l2x0_inv_all(); ++ l2x0_base = base; + +- /* enable L2X0 */ +- writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); ++ cache_id = readl_relaxed(base + L2X0_CACHE_ID); ++ ++ switch (cache_id & L2X0_CACHE_ID_PART_MASK) { ++ default: ++ case L2X0_CACHE_ID_PART_L210: ++ data = &l2c210_data; ++ break; ++ ++ case L2X0_CACHE_ID_PART_L220: ++ data = &l2c220_data; ++ break; ++ ++ case L2X0_CACHE_ID_PART_L310: ++ data = &l2c310_init_fns; ++ break; + } + +- /* Re-read it in case some bits are reserved. */ +- aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); ++ __l2c_init(data, aux_val, aux_mask, cache_id); ++} ++ ++#ifdef CONFIG_OF ++static int l2_wt_override; ++ ++/* Aurora don't have the cache ID register available, so we have to ++ * pass it though the device tree */ ++static u32 cache_id_part_number_from_dt; ++ ++static void __init l2x0_of_parse(const struct device_node *np, ++ u32 *aux_val, u32 *aux_mask) ++{ ++ u32 data[2] = { 0, 0 }; ++ u32 tag = 0; ++ u32 dirty = 0; ++ u32 val = 0, mask = 0; ++ ++ of_property_read_u32(np, "arm,tag-latency", &tag); ++ if (tag) { ++ mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK; ++ val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT; ++ } ++ ++ of_property_read_u32_array(np, "arm,data-latency", ++ data, ARRAY_SIZE(data)); ++ if (data[0] && data[1]) { ++ mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK | ++ L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK; ++ val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) | ++ ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT); ++ } ++ ++ of_property_read_u32(np, "arm,dirty-latency", &dirty); ++ if (dirty) { ++ mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK; ++ val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT; ++ } + +- /* Save the value for resuming. */ +- l2x0_saved_regs.aux_ctrl = aux; ++ *aux_val &= ~mask; ++ *aux_val |= val; ++ *aux_mask &= ~mask; ++} ++ ++static const struct l2c_init_data of_l2c210_data __initconst = { ++ .type = "L2C-210", ++ .way_size_0 = SZ_8K, ++ .num_lock = 1, ++ .of_parse = l2x0_of_parse, ++ .enable = l2c_enable, ++ .save = l2c_save, ++ .outer_cache = { ++ .inv_range = l2c210_inv_range, ++ .clean_range = l2c210_clean_range, ++ .flush_range = l2c210_flush_range, ++ .flush_all = l2c210_flush_all, ++ .disable = l2c_disable, ++ .sync = l2c210_sync, ++ .resume = l2c210_resume, ++ }, ++}; ++ ++static const struct l2c_init_data of_l2c220_data __initconst = { ++ .type = "L2C-220", ++ .way_size_0 = SZ_8K, ++ .num_lock = 1, ++ .of_parse = l2x0_of_parse, ++ .enable = l2c220_enable, ++ .save = l2c_save, ++ .outer_cache = { ++ .inv_range = l2c220_inv_range, ++ .clean_range = l2c220_clean_range, ++ .flush_range = l2c220_flush_range, ++ .flush_all = l2c220_flush_all, ++ .disable = l2c_disable, ++ .sync = l2c220_sync, ++ .resume = l2c210_resume, ++ }, ++}; ++ ++static void __init l2c310_of_parse(const struct device_node *np, ++ u32 *aux_val, u32 *aux_mask) ++{ ++ u32 data[3] = { 0, 0, 0 }; ++ u32 tag[3] = { 0, 0, 0 }; ++ u32 filter[2] = { 0, 0 }; ++ ++ of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); ++ if (tag[0] && tag[1] && tag[2]) ++ writel_relaxed( ++ L310_LATENCY_CTRL_RD(tag[0] - 1) | ++ L310_LATENCY_CTRL_WR(tag[1] - 1) | ++ L310_LATENCY_CTRL_SETUP(tag[2] - 1), ++ l2x0_base + L310_TAG_LATENCY_CTRL); ++ ++ of_property_read_u32_array(np, "arm,data-latency", ++ data, ARRAY_SIZE(data)); ++ if (data[0] && data[1] && data[2]) ++ writel_relaxed( ++ L310_LATENCY_CTRL_RD(data[0] - 1) | ++ L310_LATENCY_CTRL_WR(data[1] - 1) | ++ L310_LATENCY_CTRL_SETUP(data[2] - 1), ++ l2x0_base + L310_DATA_LATENCY_CTRL); + +- if (!of_init) { +- outer_cache.inv_range = l2x0_inv_range; +- outer_cache.clean_range = l2x0_clean_range; +- outer_cache.flush_range = l2x0_flush_range; +- outer_cache.sync = l2x0_cache_sync; +- outer_cache.flush_all = l2x0_flush_all; +- outer_cache.inv_all = l2x0_inv_all; +- outer_cache.disable = l2x0_disable; +- } +- +- pr_info("%s cache controller enabled\n", type); +- pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n", +- ways, cache_id, aux, l2x0_size >> 10); ++ of_property_read_u32_array(np, "arm,filter-ranges", ++ filter, ARRAY_SIZE(filter)); ++ if (filter[1]) { ++ writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M), ++ l2x0_base + L310_ADDR_FILTER_END); ++ writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN, ++ l2x0_base + L310_ADDR_FILTER_START); ++ } + } + +-#ifdef CONFIG_OF +-static int l2_wt_override; ++static const struct l2c_init_data of_l2c310_data __initconst = { ++ .type = "L2C-310", ++ .way_size_0 = SZ_8K, ++ .num_lock = 8, ++ .of_parse = l2c310_of_parse, ++ .enable = l2c310_enable, ++ .fixup = l2c310_fixup, ++ .save = l2c310_save, ++ .outer_cache = { ++ .inv_range = l2c210_inv_range, ++ .clean_range = l2c210_clean_range, ++ .flush_range = l2c210_flush_range, ++ .flush_all = l2c210_flush_all, ++ .disable = l2c310_disable, ++ .sync = l2c210_sync, ++ .resume = l2c310_resume, ++ }, ++}; + + /* + * Note that the end addresses passed to Linux primitives are +@@ -524,6 +1166,100 @@ + } + } + ++static void aurora_save(void __iomem *base) ++{ ++ l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL); ++ l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL); ++} ++ ++static void aurora_resume(void) ++{ ++ void __iomem *base = l2x0_base; ++ ++ if (!(readl(base + L2X0_CTRL) & L2X0_CTRL_EN)) { ++ writel_relaxed(l2x0_saved_regs.aux_ctrl, base + L2X0_AUX_CTRL); ++ writel_relaxed(l2x0_saved_regs.ctrl, base + L2X0_CTRL); ++ } ++} ++ ++/* ++ * For Aurora cache in no outer mode, enable via the CP15 coprocessor ++ * broadcasting of cache commands to L2. ++ */ ++static void __init aurora_enable_no_outer(void __iomem *base, u32 aux, ++ unsigned num_lock) ++{ ++ u32 u; ++ ++ asm volatile("mrc p15, 1, %0, c15, c2, 0" : "=r" (u)); ++ u |= AURORA_CTRL_FW; /* Set the FW bit */ ++ asm volatile("mcr p15, 1, %0, c15, c2, 0" : : "r" (u)); ++ ++ isb(); ++ ++ l2c_enable(base, aux, num_lock); ++} ++ ++static void __init aurora_fixup(void __iomem *base, u32 cache_id, ++ struct outer_cache_fns *fns) ++{ ++ sync_reg_offset = AURORA_SYNC_REG; ++} ++ ++static void __init aurora_of_parse(const struct device_node *np, ++ u32 *aux_val, u32 *aux_mask) ++{ ++ u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU; ++ u32 mask = AURORA_ACR_REPLACEMENT_MASK; ++ ++ of_property_read_u32(np, "cache-id-part", ++ &cache_id_part_number_from_dt); ++ ++ /* Determine and save the write policy */ ++ l2_wt_override = of_property_read_bool(np, "wt-override"); ++ ++ if (l2_wt_override) { ++ val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY; ++ mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK; ++ } ++ ++ *aux_val &= ~mask; ++ *aux_val |= val; ++ *aux_mask &= ~mask; ++} ++ ++static const struct l2c_init_data of_aurora_with_outer_data __initconst = { ++ .type = "Aurora", ++ .way_size_0 = SZ_4K, ++ .num_lock = 4, ++ .of_parse = aurora_of_parse, ++ .enable = l2c_enable, ++ .fixup = aurora_fixup, ++ .save = aurora_save, ++ .outer_cache = { ++ .inv_range = aurora_inv_range, ++ .clean_range = aurora_clean_range, ++ .flush_range = aurora_flush_range, ++ .flush_all = l2x0_flush_all, ++ .disable = l2x0_disable, ++ .sync = l2x0_cache_sync, ++ .resume = aurora_resume, ++ }, ++}; ++ ++static const struct l2c_init_data of_aurora_no_outer_data __initconst = { ++ .type = "Aurora", ++ .way_size_0 = SZ_4K, ++ .num_lock = 4, ++ .of_parse = aurora_of_parse, ++ .enable = aurora_enable_no_outer, ++ .fixup = aurora_fixup, ++ .save = aurora_save, ++ .outer_cache = { ++ .resume = aurora_resume, ++ }, ++}; ++ + /* + * For certain Broadcom SoCs, depending on the address range, different offsets + * need to be added to the address before passing it to L2 for +@@ -588,16 +1324,16 @@ + + /* normal case, no cross section between start and end */ + if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { +- l2x0_inv_range(new_start, new_end); ++ l2c210_inv_range(new_start, new_end); + return; + } + + /* They cross sections, so it can only be a cross from section + * 2 to section 3 + */ +- l2x0_inv_range(new_start, ++ l2c210_inv_range(new_start, + bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); +- l2x0_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), ++ l2c210_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + new_end); + } + +@@ -610,26 +1346,21 @@ + if (unlikely(end <= start)) + return; + +- if ((end - start) >= l2x0_size) { +- l2x0_clean_all(); +- return; +- } +- + new_start = bcm_l2_phys_addr(start); + new_end = bcm_l2_phys_addr(end); + + /* normal case, no cross section between start and end */ + if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { +- l2x0_clean_range(new_start, new_end); ++ l2c210_clean_range(new_start, new_end); + return; + } + + /* They cross sections, so it can only be a cross from section + * 2 to section 3 + */ +- l2x0_clean_range(new_start, ++ l2c210_clean_range(new_start, + bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); +- l2x0_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), ++ l2c210_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + new_end); + } + +@@ -643,7 +1374,7 @@ + return; + + if ((end - start) >= l2x0_size) { +- l2x0_flush_all(); ++ outer_cache.flush_all(); + return; + } + +@@ -652,283 +1383,67 @@ + + /* normal case, no cross section between start and end */ + if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { +- l2x0_flush_range(new_start, new_end); ++ l2c210_flush_range(new_start, new_end); + return; + } + + /* They cross sections, so it can only be a cross from section + * 2 to section 3 + */ +- l2x0_flush_range(new_start, ++ l2c210_flush_range(new_start, + bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); +- l2x0_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), ++ l2c210_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + new_end); + } + +-static void __init l2x0_of_setup(const struct device_node *np, +- u32 *aux_val, u32 *aux_mask) +-{ +- u32 data[2] = { 0, 0 }; +- u32 tag = 0; +- u32 dirty = 0; +- u32 val = 0, mask = 0; +- +- of_property_read_u32(np, "arm,tag-latency", &tag); +- if (tag) { +- mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK; +- val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT; +- } +- +- of_property_read_u32_array(np, "arm,data-latency", +- data, ARRAY_SIZE(data)); +- if (data[0] && data[1]) { +- mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK | +- L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK; +- val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) | +- ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT); +- } +- +- of_property_read_u32(np, "arm,dirty-latency", &dirty); +- if (dirty) { +- mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK; +- val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT; +- } +- +- *aux_val &= ~mask; +- *aux_val |= val; +- *aux_mask &= ~mask; +-} +- +-static void __init pl310_of_setup(const struct device_node *np, +- u32 *aux_val, u32 *aux_mask) +-{ +- u32 data[3] = { 0, 0, 0 }; +- u32 tag[3] = { 0, 0, 0 }; +- u32 filter[2] = { 0, 0 }; +- +- of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); +- if (tag[0] && tag[1] && tag[2]) +- writel_relaxed( +- ((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | +- ((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | +- ((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), +- l2x0_base + L2X0_TAG_LATENCY_CTRL); +- +- of_property_read_u32_array(np, "arm,data-latency", +- data, ARRAY_SIZE(data)); +- if (data[0] && data[1] && data[2]) +- writel_relaxed( +- ((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | +- ((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | +- ((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), +- l2x0_base + L2X0_DATA_LATENCY_CTRL); +- +- of_property_read_u32_array(np, "arm,filter-ranges", +- filter, ARRAY_SIZE(filter)); +- if (filter[1]) { +- writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M), +- l2x0_base + L2X0_ADDR_FILTER_END); +- writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN, +- l2x0_base + L2X0_ADDR_FILTER_START); +- } +-} +- +-static void __init pl310_save(void) +-{ +- u32 l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & +- L2X0_CACHE_ID_RTL_MASK; +- +- l2x0_saved_regs.tag_latency = readl_relaxed(l2x0_base + +- L2X0_TAG_LATENCY_CTRL); +- l2x0_saved_regs.data_latency = readl_relaxed(l2x0_base + +- L2X0_DATA_LATENCY_CTRL); +- l2x0_saved_regs.filter_end = readl_relaxed(l2x0_base + +- L2X0_ADDR_FILTER_END); +- l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base + +- L2X0_ADDR_FILTER_START); +- +- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) { +- /* +- * From r2p0, there is Prefetch offset/control register +- */ +- l2x0_saved_regs.prefetch_ctrl = readl_relaxed(l2x0_base + +- L2X0_PREFETCH_CTRL); +- /* +- * From r3p0, there is Power control register +- */ +- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0) +- l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base + +- L2X0_POWER_CTRL); +- } +-} ++/* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */ ++static const struct l2c_init_data of_bcm_l2x0_data __initconst = { ++ .type = "BCM-L2C-310", ++ .way_size_0 = SZ_8K, ++ .num_lock = 8, ++ .of_parse = l2c310_of_parse, ++ .enable = l2c310_enable, ++ .save = l2c310_save, ++ .outer_cache = { ++ .inv_range = bcm_inv_range, ++ .clean_range = bcm_clean_range, ++ .flush_range = bcm_flush_range, ++ .flush_all = l2c210_flush_all, ++ .disable = l2c310_disable, ++ .sync = l2c210_sync, ++ .resume = l2c310_resume, ++ }, ++}; + +-static void aurora_save(void) ++static void __init tauros3_save(void __iomem *base) + { +- l2x0_saved_regs.ctrl = readl_relaxed(l2x0_base + L2X0_CTRL); +- l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); +-} ++ l2c_save(base); + +-static void __init tauros3_save(void) +-{ + l2x0_saved_regs.aux2_ctrl = +- readl_relaxed(l2x0_base + TAUROS3_AUX2_CTRL); ++ readl_relaxed(base + TAUROS3_AUX2_CTRL); + l2x0_saved_regs.prefetch_ctrl = +- readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL); +-} +- +-static void l2x0_resume(void) +-{ +- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { +- /* restore aux ctrl and enable l2 */ +- l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID)); +- +- writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base + +- L2X0_AUX_CTRL); +- +- l2x0_inv_all(); +- +- writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); +- } +-} +- +-static void pl310_resume(void) +-{ +- u32 l2x0_revision; +- +- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { +- /* restore pl310 setup */ +- writel_relaxed(l2x0_saved_regs.tag_latency, +- l2x0_base + L2X0_TAG_LATENCY_CTRL); +- writel_relaxed(l2x0_saved_regs.data_latency, +- l2x0_base + L2X0_DATA_LATENCY_CTRL); +- writel_relaxed(l2x0_saved_regs.filter_end, +- l2x0_base + L2X0_ADDR_FILTER_END); +- writel_relaxed(l2x0_saved_regs.filter_start, +- l2x0_base + L2X0_ADDR_FILTER_START); +- +- l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & +- L2X0_CACHE_ID_RTL_MASK; +- +- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) { +- writel_relaxed(l2x0_saved_regs.prefetch_ctrl, +- l2x0_base + L2X0_PREFETCH_CTRL); +- if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0) +- writel_relaxed(l2x0_saved_regs.pwr_ctrl, +- l2x0_base + L2X0_POWER_CTRL); +- } +- } +- +- l2x0_resume(); +-} +- +-static void aurora_resume(void) +-{ +- if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { +- writel_relaxed(l2x0_saved_regs.aux_ctrl, +- l2x0_base + L2X0_AUX_CTRL); +- writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL); +- } ++ readl_relaxed(base + L310_PREFETCH_CTRL); + } + + static void tauros3_resume(void) + { +- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { ++ void __iomem *base = l2x0_base; ++ ++ if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) { + writel_relaxed(l2x0_saved_regs.aux2_ctrl, +- l2x0_base + TAUROS3_AUX2_CTRL); ++ base + TAUROS3_AUX2_CTRL); + writel_relaxed(l2x0_saved_regs.prefetch_ctrl, +- l2x0_base + L2X0_PREFETCH_CTRL); +- } +- +- l2x0_resume(); +-} +- +-static void __init aurora_broadcast_l2_commands(void) +-{ +- __u32 u; +- /* Enable Broadcasting of cache commands to L2*/ +- __asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u)); +- u |= AURORA_CTRL_FW; /* Set the FW bit */ +- __asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u)); +- isb(); +-} +- +-static void __init aurora_of_setup(const struct device_node *np, +- u32 *aux_val, u32 *aux_mask) +-{ +- u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU; +- u32 mask = AURORA_ACR_REPLACEMENT_MASK; ++ base + L310_PREFETCH_CTRL); + +- of_property_read_u32(np, "cache-id-part", +- &cache_id_part_number_from_dt); +- +- /* Determine and save the write policy */ +- l2_wt_override = of_property_read_bool(np, "wt-override"); +- +- if (l2_wt_override) { +- val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY; +- mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK; ++ l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); + } +- +- *aux_val &= ~mask; +- *aux_val |= val; +- *aux_mask &= ~mask; + } + +-static const struct l2x0_of_data pl310_data = { +- .setup = pl310_of_setup, +- .save = pl310_save, +- .outer_cache = { +- .resume = pl310_resume, +- .inv_range = l2x0_inv_range, +- .clean_range = l2x0_clean_range, +- .flush_range = l2x0_flush_range, +- .sync = l2x0_cache_sync, +- .flush_all = l2x0_flush_all, +- .inv_all = l2x0_inv_all, +- .disable = l2x0_disable, +- }, +-}; +- +-static const struct l2x0_of_data l2x0_data = { +- .setup = l2x0_of_setup, +- .save = NULL, +- .outer_cache = { +- .resume = l2x0_resume, +- .inv_range = l2x0_inv_range, +- .clean_range = l2x0_clean_range, +- .flush_range = l2x0_flush_range, +- .sync = l2x0_cache_sync, +- .flush_all = l2x0_flush_all, +- .inv_all = l2x0_inv_all, +- .disable = l2x0_disable, +- }, +-}; +- +-static const struct l2x0_of_data aurora_with_outer_data = { +- .setup = aurora_of_setup, +- .save = aurora_save, +- .outer_cache = { +- .resume = aurora_resume, +- .inv_range = aurora_inv_range, +- .clean_range = aurora_clean_range, +- .flush_range = aurora_flush_range, +- .sync = l2x0_cache_sync, +- .flush_all = l2x0_flush_all, +- .inv_all = l2x0_inv_all, +- .disable = l2x0_disable, +- }, +-}; +- +-static const struct l2x0_of_data aurora_no_outer_data = { +- .setup = aurora_of_setup, +- .save = aurora_save, +- .outer_cache = { +- .resume = aurora_resume, +- }, +-}; +- +-static const struct l2x0_of_data tauros3_data = { +- .setup = NULL, ++static const struct l2c_init_data of_tauros3_data __initconst = { ++ .type = "Tauros3", ++ .way_size_0 = SZ_8K, ++ .num_lock = 8, ++ .enable = l2c_enable, + .save = tauros3_save, + /* Tauros3 broadcasts L1 cache operations to L2 */ + .outer_cache = { +@@ -936,43 +1451,26 @@ + }, + }; + +-static const struct l2x0_of_data bcm_l2x0_data = { +- .setup = pl310_of_setup, +- .save = pl310_save, +- .outer_cache = { +- .resume = pl310_resume, +- .inv_range = bcm_inv_range, +- .clean_range = bcm_clean_range, +- .flush_range = bcm_flush_range, +- .sync = l2x0_cache_sync, +- .flush_all = l2x0_flush_all, +- .inv_all = l2x0_inv_all, +- .disable = l2x0_disable, +- }, +-}; +- ++#define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } + static const struct of_device_id l2x0_ids[] __initconst = { +- { .compatible = "arm,l210-cache", .data = (void *)&l2x0_data }, +- { .compatible = "arm,l220-cache", .data = (void *)&l2x0_data }, +- { .compatible = "arm,pl310-cache", .data = (void *)&pl310_data }, +- { .compatible = "bcm,bcm11351-a2-pl310-cache", /* deprecated name */ +- .data = (void *)&bcm_l2x0_data}, +- { .compatible = "brcm,bcm11351-a2-pl310-cache", +- .data = (void *)&bcm_l2x0_data}, +- { .compatible = "marvell,aurora-outer-cache", +- .data = (void *)&aurora_with_outer_data}, +- { .compatible = "marvell,aurora-system-cache", +- .data = (void *)&aurora_no_outer_data}, +- { .compatible = "marvell,tauros3-cache", +- .data = (void *)&tauros3_data }, ++ L2C_ID("arm,l210-cache", of_l2c210_data), ++ L2C_ID("arm,l220-cache", of_l2c220_data), ++ L2C_ID("arm,pl310-cache", of_l2c310_data), ++ L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), ++ L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data), ++ L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data), ++ L2C_ID("marvell,tauros3-cache", of_tauros3_data), ++ /* Deprecated IDs */ ++ L2C_ID("bcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), + {} + }; + + int __init l2x0_of_init(u32 aux_val, u32 aux_mask) + { ++ const struct l2c_init_data *data; + struct device_node *np; +- const struct l2x0_of_data *data; + struct resource res; ++ u32 cache_id, old_aux; + + np = of_find_matching_node(NULL, l2x0_ids); + if (!np) +@@ -989,23 +1487,29 @@ + + data = of_match_node(l2x0_ids, np)->data; + +- /* L2 configuration can only be changed if the cache is disabled */ +- if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { +- if (data->setup) +- data->setup(np, &aux_val, &aux_mask); +- +- /* For aurora cache in no outer mode select the +- * correct mode using the coprocessor*/ +- if (data == &aurora_no_outer_data) +- aurora_broadcast_l2_commands(); ++ old_aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); ++ if (old_aux != ((old_aux & aux_mask) | aux_val)) { ++ pr_warn("L2C: platform modifies aux control register: 0x%08x -> 0x%08x\n", ++ old_aux, (old_aux & aux_mask) | aux_val); ++ } else if (aux_mask != ~0U && aux_val != 0) { ++ pr_alert("L2C: platform provided aux values match the hardware, so have no effect. Please remove them.\n"); + } + +- if (data->save) +- data->save(); ++ /* All L2 caches are unified, so this property should be specified */ ++ if (!of_property_read_bool(np, "cache-unified")) ++ pr_err("L2C: device tree omits to specify unified cache\n"); ++ ++ /* L2 configuration can only be changed if the cache is disabled */ ++ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) ++ if (data->of_parse) ++ data->of_parse(np, &aux_val, &aux_mask); ++ ++ if (cache_id_part_number_from_dt) ++ cache_id = cache_id_part_number_from_dt; ++ else ++ cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); + +- of_init = true; +- memcpy(&outer_cache, &data->outer_cache, sizeof(outer_cache)); +- l2x0_init(l2x0_base, aux_val, aux_mask); ++ __l2c_init(data, aux_val, aux_mask, cache_id); + + return 0; + } +diff -Nur linux-3.14.36/arch/arm/mm/dma-mapping.c linux-openelec/arch/arm/mm/dma-mapping.c +--- linux-3.14.36/arch/arm/mm/dma-mapping.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/dma-mapping.c 2015-07-24 18:03:29.580842002 -0500 +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #include + #include +diff -Nur linux-3.14.36/arch/arm/mm/dma-mapping.c.orig linux-openelec/arch/arm/mm/dma-mapping.c.orig +--- linux-3.14.36/arch/arm/mm/dma-mapping.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mm/dma-mapping.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,2001 @@ ++/* ++ * linux/arch/arm/mm/dma-mapping.c ++ * ++ * Copyright (C) 2000-2004 Russell King ++ * ++ * 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. ++ * ++ * DMA uncached mapping support. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mm.h" ++ ++/* ++ * The DMA API is built upon the notion of "buffer ownership". A buffer ++ * is either exclusively owned by the CPU (and therefore may be accessed ++ * by it) or exclusively owned by the DMA device. These helper functions ++ * represent the transitions between these two ownership states. ++ * ++ * Note, however, that on later ARMs, this notion does not work due to ++ * speculative prefetches. We model our approach on the assumption that ++ * the CPU does do speculative prefetches, which means we clean caches ++ * before transfers and delay cache invalidation until transfer completion. ++ * ++ */ ++static void __dma_page_cpu_to_dev(struct page *, unsigned long, ++ size_t, enum dma_data_direction); ++static void __dma_page_dev_to_cpu(struct page *, unsigned long, ++ size_t, enum dma_data_direction); ++ ++/** ++ * arm_dma_map_page - map a portion of a page for streaming DMA ++ * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices ++ * @page: page that buffer resides in ++ * @offset: offset into page for start of buffer ++ * @size: size of buffer to map ++ * @dir: DMA transfer direction ++ * ++ * Ensure that any data held in the cache is appropriately discarded ++ * or written back. ++ * ++ * The device owns this memory once this call has completed. The CPU ++ * can regain ownership by calling dma_unmap_page(). ++ */ ++static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page, ++ unsigned long offset, size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) ++ __dma_page_cpu_to_dev(page, offset, size, dir); ++ return pfn_to_dma(dev, page_to_pfn(page)) + offset; ++} ++ ++static dma_addr_t arm_coherent_dma_map_page(struct device *dev, struct page *page, ++ unsigned long offset, size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ return pfn_to_dma(dev, page_to_pfn(page)) + offset; ++} ++ ++/** ++ * arm_dma_unmap_page - unmap a buffer previously mapped through dma_map_page() ++ * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices ++ * @handle: DMA address of buffer ++ * @size: size of buffer (same as passed to dma_map_page) ++ * @dir: DMA transfer direction (same as passed to dma_map_page) ++ * ++ * Unmap a page streaming mode DMA translation. The handle and size ++ * must match what was provided in the previous dma_map_page() call. ++ * All other usages are undefined. ++ * ++ * After this call, reads by the CPU to the buffer are guaranteed to see ++ * whatever the device wrote there. ++ */ ++static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle, ++ size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) ++ __dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)), ++ handle & ~PAGE_MASK, size, dir); ++} ++ ++static void arm_dma_sync_single_for_cpu(struct device *dev, ++ dma_addr_t handle, size_t size, enum dma_data_direction dir) ++{ ++ unsigned int offset = handle & (PAGE_SIZE - 1); ++ struct page *page = pfn_to_page(dma_to_pfn(dev, handle-offset)); ++ __dma_page_dev_to_cpu(page, offset, size, dir); ++} ++ ++static void arm_dma_sync_single_for_device(struct device *dev, ++ dma_addr_t handle, size_t size, enum dma_data_direction dir) ++{ ++ unsigned int offset = handle & (PAGE_SIZE - 1); ++ struct page *page = pfn_to_page(dma_to_pfn(dev, handle-offset)); ++ __dma_page_cpu_to_dev(page, offset, size, dir); ++} ++ ++struct dma_map_ops arm_dma_ops = { ++ .alloc = arm_dma_alloc, ++ .free = arm_dma_free, ++ .mmap = arm_dma_mmap, ++ .get_sgtable = arm_dma_get_sgtable, ++ .map_page = arm_dma_map_page, ++ .unmap_page = arm_dma_unmap_page, ++ .map_sg = arm_dma_map_sg, ++ .unmap_sg = arm_dma_unmap_sg, ++ .sync_single_for_cpu = arm_dma_sync_single_for_cpu, ++ .sync_single_for_device = arm_dma_sync_single_for_device, ++ .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, ++ .sync_sg_for_device = arm_dma_sync_sg_for_device, ++ .set_dma_mask = arm_dma_set_mask, ++}; ++EXPORT_SYMBOL(arm_dma_ops); ++ ++static void *arm_coherent_dma_alloc(struct device *dev, size_t size, ++ dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs); ++static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_addr, ++ dma_addr_t handle, struct dma_attrs *attrs); ++ ++struct dma_map_ops arm_coherent_dma_ops = { ++ .alloc = arm_coherent_dma_alloc, ++ .free = arm_coherent_dma_free, ++ .mmap = arm_dma_mmap, ++ .get_sgtable = arm_dma_get_sgtable, ++ .map_page = arm_coherent_dma_map_page, ++ .map_sg = arm_dma_map_sg, ++ .set_dma_mask = arm_dma_set_mask, ++}; ++EXPORT_SYMBOL(arm_coherent_dma_ops); ++ ++static int __dma_supported(struct device *dev, u64 mask, bool warn) ++{ ++ unsigned long max_dma_pfn; ++ ++ /* ++ * If the mask allows for more memory than we can address, ++ * and we actually have that much memory, then we must ++ * indicate that DMA to this device is not supported. ++ */ ++ if (sizeof(mask) != sizeof(dma_addr_t) && ++ mask > (dma_addr_t)~0 && ++ dma_to_pfn(dev, ~0) < max_pfn) { ++ if (warn) { ++ dev_warn(dev, "Coherent DMA mask %#llx is larger than dma_addr_t allows\n", ++ mask); ++ dev_warn(dev, "Driver did not use or check the return value from dma_set_coherent_mask()?\n"); ++ } ++ return 0; ++ } ++ ++ max_dma_pfn = min(max_pfn, arm_dma_pfn_limit); ++ ++ /* ++ * Translate the device's DMA mask to a PFN limit. This ++ * PFN number includes the page which we can DMA to. ++ */ ++ if (dma_to_pfn(dev, mask) < max_dma_pfn) { ++ if (warn) ++ dev_warn(dev, "Coherent DMA mask %#llx (pfn %#lx-%#lx) covers a smaller range of system memory than the DMA zone pfn 0x0-%#lx\n", ++ mask, ++ dma_to_pfn(dev, 0), dma_to_pfn(dev, mask) + 1, ++ max_dma_pfn + 1); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static u64 get_coherent_dma_mask(struct device *dev) ++{ ++ u64 mask = (u64)DMA_BIT_MASK(32); ++ ++ if (dev) { ++ mask = dev->coherent_dma_mask; ++ ++ /* ++ * Sanity check the DMA mask - it must be non-zero, and ++ * must be able to be satisfied by a DMA allocation. ++ */ ++ if (mask == 0) { ++ dev_warn(dev, "coherent DMA mask is unset\n"); ++ return 0; ++ } ++ ++ if (!__dma_supported(dev, mask, true)) ++ return 0; ++ } ++ ++ return mask; ++} ++ ++static void __dma_clear_buffer(struct page *page, size_t size) ++{ ++ /* ++ * Ensure that the allocated pages are zeroed, and that any data ++ * lurking in the kernel direct-mapped region is invalidated. ++ */ ++ if (PageHighMem(page)) { ++ phys_addr_t base = __pfn_to_phys(page_to_pfn(page)); ++ phys_addr_t end = base + size; ++ while (size > 0) { ++ void *ptr = kmap_atomic(page); ++ memset(ptr, 0, PAGE_SIZE); ++ dmac_flush_range(ptr, ptr + PAGE_SIZE); ++ kunmap_atomic(ptr); ++ page++; ++ size -= PAGE_SIZE; ++ } ++ outer_flush_range(base, end); ++ } else { ++ void *ptr = page_address(page); ++ memset(ptr, 0, size); ++ dmac_flush_range(ptr, ptr + size); ++ outer_flush_range(__pa(ptr), __pa(ptr) + size); ++ } ++} ++ ++/* ++ * Allocate a DMA buffer for 'dev' of size 'size' using the ++ * specified gfp mask. Note that 'size' must be page aligned. ++ */ ++static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gfp) ++{ ++ unsigned long order = get_order(size); ++ struct page *page, *p, *e; ++ ++ page = alloc_pages(gfp, order); ++ if (!page) ++ return NULL; ++ ++ /* ++ * Now split the huge page and free the excess pages ++ */ ++ split_page(page, order); ++ for (p = page + (size >> PAGE_SHIFT), e = page + (1 << order); p < e; p++) ++ __free_page(p); ++ ++ __dma_clear_buffer(page, size); ++ ++ return page; ++} ++ ++/* ++ * Free a DMA buffer. 'size' must be page aligned. ++ */ ++static void __dma_free_buffer(struct page *page, size_t size) ++{ ++ struct page *e = page + (size >> PAGE_SHIFT); ++ ++ while (page < e) { ++ __free_page(page); ++ page++; ++ } ++} ++ ++#ifdef CONFIG_MMU ++#ifdef CONFIG_HUGETLB_PAGE ++#warning ARM Coherent DMA allocator does not (yet) support huge TLB ++#endif ++ ++static void *__alloc_from_contiguous(struct device *dev, size_t size, ++ pgprot_t prot, struct page **ret_page, ++ const void *caller); ++ ++static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp, ++ pgprot_t prot, struct page **ret_page, ++ const void *caller); ++ ++static void * ++__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, ++ const void *caller) ++{ ++ struct vm_struct *area; ++ unsigned long addr; ++ ++ /* ++ * DMA allocation can be mapped to user space, so lets ++ * set VM_USERMAP flags too. ++ */ ++ area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP, ++ caller); ++ if (!area) ++ return NULL; ++ addr = (unsigned long)area->addr; ++ area->phys_addr = __pfn_to_phys(page_to_pfn(page)); ++ ++ if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) { ++ vunmap((void *)addr); ++ return NULL; ++ } ++ return (void *)addr; ++} ++ ++static void __dma_free_remap(void *cpu_addr, size_t size) ++{ ++ unsigned int flags = VM_ARM_DMA_CONSISTENT | VM_USERMAP; ++ struct vm_struct *area = find_vm_area(cpu_addr); ++ if (!area || (area->flags & flags) != flags) { ++ WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); ++ return; ++ } ++ unmap_kernel_range((unsigned long)cpu_addr, size); ++ vunmap(cpu_addr); ++} ++ ++#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K ++ ++struct dma_pool { ++ size_t size; ++ spinlock_t lock; ++ unsigned long *bitmap; ++ unsigned long nr_pages; ++ void *vaddr; ++ struct page **pages; ++}; ++ ++static struct dma_pool atomic_pool = { ++ .size = DEFAULT_DMA_COHERENT_POOL_SIZE, ++}; ++ ++static int __init early_coherent_pool(char *p) ++{ ++ atomic_pool.size = memparse(p, &p); ++ return 0; ++} ++early_param("coherent_pool", early_coherent_pool); ++ ++void __init init_dma_coherent_pool_size(unsigned long size) ++{ ++ /* ++ * Catch any attempt to set the pool size too late. ++ */ ++ BUG_ON(atomic_pool.vaddr); ++ ++ /* ++ * Set architecture specific coherent pool size only if ++ * it has not been changed by kernel command line parameter. ++ */ ++ if (atomic_pool.size == DEFAULT_DMA_COHERENT_POOL_SIZE) ++ atomic_pool.size = size; ++} ++ ++/* ++ * Initialise the coherent pool for atomic allocations. ++ */ ++static int __init atomic_pool_init(void) ++{ ++ struct dma_pool *pool = &atomic_pool; ++ pgprot_t prot = pgprot_dmacoherent(PAGE_KERNEL); ++ gfp_t gfp = GFP_KERNEL | GFP_DMA; ++ unsigned long nr_pages = pool->size >> PAGE_SHIFT; ++ unsigned long *bitmap; ++ struct page *page; ++ struct page **pages; ++ void *ptr; ++ int bitmap_size = BITS_TO_LONGS(nr_pages) * sizeof(long); ++ ++ bitmap = kzalloc(bitmap_size, GFP_KERNEL); ++ if (!bitmap) ++ goto no_bitmap; ++ ++ pages = kzalloc(nr_pages * sizeof(struct page *), GFP_KERNEL); ++ if (!pages) ++ goto no_pages; ++ ++ if (IS_ENABLED(CONFIG_DMA_CMA)) ++ ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page, ++ atomic_pool_init); ++ else ++ ptr = __alloc_remap_buffer(NULL, pool->size, gfp, prot, &page, ++ atomic_pool_init); ++ if (ptr) { ++ int i; ++ ++ for (i = 0; i < nr_pages; i++) ++ pages[i] = page + i; ++ ++ spin_lock_init(&pool->lock); ++ pool->vaddr = ptr; ++ pool->pages = pages; ++ pool->bitmap = bitmap; ++ pool->nr_pages = nr_pages; ++ pr_info("DMA: preallocated %u KiB pool for atomic coherent allocations\n", ++ (unsigned)pool->size / 1024); ++ return 0; ++ } ++ ++ kfree(pages); ++no_pages: ++ kfree(bitmap); ++no_bitmap: ++ pr_err("DMA: failed to allocate %u KiB pool for atomic coherent allocation\n", ++ (unsigned)pool->size / 1024); ++ return -ENOMEM; ++} ++/* ++ * CMA is activated by core_initcall, so we must be called after it. ++ */ ++postcore_initcall(atomic_pool_init); ++ ++struct dma_contig_early_reserve { ++ phys_addr_t base; ++ unsigned long size; ++}; ++ ++static struct dma_contig_early_reserve dma_mmu_remap[MAX_CMA_AREAS] __initdata; ++ ++static int dma_mmu_remap_num __initdata; ++ ++void __init dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) ++{ ++ dma_mmu_remap[dma_mmu_remap_num].base = base; ++ dma_mmu_remap[dma_mmu_remap_num].size = size; ++ dma_mmu_remap_num++; ++} ++ ++void __init dma_contiguous_remap(void) ++{ ++ int i; ++ for (i = 0; i < dma_mmu_remap_num; i++) { ++ phys_addr_t start = dma_mmu_remap[i].base; ++ phys_addr_t end = start + dma_mmu_remap[i].size; ++ struct map_desc map; ++ unsigned long addr; ++ ++ if (end > arm_lowmem_limit) ++ end = arm_lowmem_limit; ++ if (start >= end) ++ continue; ++ ++ map.pfn = __phys_to_pfn(start); ++ map.virtual = __phys_to_virt(start); ++ map.length = end - start; ++ map.type = MT_MEMORY_DMA_READY; ++ ++ /* ++ * Clear previous low-memory mapping ++ */ ++ for (addr = __phys_to_virt(start); addr < __phys_to_virt(end); ++ addr += PMD_SIZE) ++ pmd_clear(pmd_off_k(addr)); ++ ++ iotable_init(&map, 1); ++ } ++} ++ ++static int __dma_update_pte(pte_t *pte, pgtable_t token, unsigned long addr, ++ void *data) ++{ ++ struct page *page = virt_to_page(addr); ++ pgprot_t prot = *(pgprot_t *)data; ++ ++ set_pte_ext(pte, mk_pte(page, prot), 0); ++ return 0; ++} ++ ++static void __dma_remap(struct page *page, size_t size, pgprot_t prot) ++{ ++ unsigned long start = (unsigned long) page_address(page); ++ unsigned end = start + size; ++ ++ apply_to_page_range(&init_mm, start, size, __dma_update_pte, &prot); ++ flush_tlb_kernel_range(start, end); ++} ++ ++static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp, ++ pgprot_t prot, struct page **ret_page, ++ const void *caller) ++{ ++ struct page *page; ++ void *ptr; ++ page = __dma_alloc_buffer(dev, size, gfp); ++ if (!page) ++ return NULL; ++ ++ ptr = __dma_alloc_remap(page, size, gfp, prot, caller); ++ if (!ptr) { ++ __dma_free_buffer(page, size); ++ return NULL; ++ } ++ ++ *ret_page = page; ++ return ptr; ++} ++ ++static void *__alloc_from_pool(size_t size, struct page **ret_page) ++{ ++ struct dma_pool *pool = &atomic_pool; ++ unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; ++ unsigned int pageno; ++ unsigned long flags; ++ void *ptr = NULL; ++ unsigned long align_mask; ++ ++ if (!pool->vaddr) { ++ WARN(1, "coherent pool not initialised!\n"); ++ return NULL; ++ } ++ ++ /* ++ * Align the region allocation - allocations from pool are rather ++ * small, so align them to their order in pages, minimum is a page ++ * size. This helps reduce fragmentation of the DMA space. ++ */ ++ align_mask = (1 << get_order(size)) - 1; ++ ++ spin_lock_irqsave(&pool->lock, flags); ++ pageno = bitmap_find_next_zero_area(pool->bitmap, pool->nr_pages, ++ 0, count, align_mask); ++ if (pageno < pool->nr_pages) { ++ bitmap_set(pool->bitmap, pageno, count); ++ ptr = pool->vaddr + PAGE_SIZE * pageno; ++ *ret_page = pool->pages[pageno]; ++ } else { ++ pr_err_once("ERROR: %u KiB atomic DMA coherent pool is too small!\n" ++ "Please increase it with coherent_pool= kernel parameter!\n", ++ (unsigned)pool->size / 1024); ++ } ++ spin_unlock_irqrestore(&pool->lock, flags); ++ ++ return ptr; ++} ++ ++static bool __in_atomic_pool(void *start, size_t size) ++{ ++ struct dma_pool *pool = &atomic_pool; ++ void *end = start + size; ++ void *pool_start = pool->vaddr; ++ void *pool_end = pool->vaddr + pool->size; ++ ++ if (start < pool_start || start >= pool_end) ++ return false; ++ ++ if (end <= pool_end) ++ return true; ++ ++ WARN(1, "Wrong coherent size(%p-%p) from atomic pool(%p-%p)\n", ++ start, end - 1, pool_start, pool_end - 1); ++ ++ return false; ++} ++ ++static int __free_from_pool(void *start, size_t size) ++{ ++ struct dma_pool *pool = &atomic_pool; ++ unsigned long pageno, count; ++ unsigned long flags; ++ ++ if (!__in_atomic_pool(start, size)) ++ return 0; ++ ++ pageno = (start - pool->vaddr) >> PAGE_SHIFT; ++ count = size >> PAGE_SHIFT; ++ ++ spin_lock_irqsave(&pool->lock, flags); ++ bitmap_clear(pool->bitmap, pageno, count); ++ spin_unlock_irqrestore(&pool->lock, flags); ++ ++ return 1; ++} ++ ++static void *__alloc_from_contiguous(struct device *dev, size_t size, ++ pgprot_t prot, struct page **ret_page, ++ const void *caller) ++{ ++ unsigned long order = get_order(size); ++ size_t count = size >> PAGE_SHIFT; ++ struct page *page; ++ void *ptr; ++ ++ page = dma_alloc_from_contiguous(dev, count, order); ++ if (!page) ++ return NULL; ++ ++ __dma_clear_buffer(page, size); ++ ++ if (PageHighMem(page)) { ++ ptr = __dma_alloc_remap(page, size, GFP_KERNEL, prot, caller); ++ if (!ptr) { ++ dma_release_from_contiguous(dev, page, count); ++ return NULL; ++ } ++ } else { ++ __dma_remap(page, size, prot); ++ ptr = page_address(page); ++ } ++ *ret_page = page; ++ return ptr; ++} ++ ++static void __free_from_contiguous(struct device *dev, struct page *page, ++ void *cpu_addr, size_t size) ++{ ++ if (PageHighMem(page)) ++ __dma_free_remap(cpu_addr, size); ++ else ++ __dma_remap(page, size, PAGE_KERNEL); ++ dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT); ++} ++ ++static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot) ++{ ++ prot = dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs) ? ++ pgprot_writecombine(prot) : ++ pgprot_dmacoherent(prot); ++ return prot; ++} ++ ++#define nommu() 0 ++ ++#else /* !CONFIG_MMU */ ++ ++#define nommu() 1 ++ ++#define __get_dma_pgprot(attrs, prot) __pgprot(0) ++#define __alloc_remap_buffer(dev, size, gfp, prot, ret, c) NULL ++#define __alloc_from_pool(size, ret_page) NULL ++#define __alloc_from_contiguous(dev, size, prot, ret, c) NULL ++#define __free_from_pool(cpu_addr, size) 0 ++#define __free_from_contiguous(dev, page, cpu_addr, size) do { } while (0) ++#define __dma_free_remap(cpu_addr, size) do { } while (0) ++ ++#endif /* CONFIG_MMU */ ++ ++static void *__alloc_simple_buffer(struct device *dev, size_t size, gfp_t gfp, ++ struct page **ret_page) ++{ ++ struct page *page; ++ page = __dma_alloc_buffer(dev, size, gfp); ++ if (!page) ++ return NULL; ++ ++ *ret_page = page; ++ return page_address(page); ++} ++ ++ ++ ++static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, ++ gfp_t gfp, pgprot_t prot, bool is_coherent, const void *caller) ++{ ++ u64 mask = get_coherent_dma_mask(dev); ++ struct page *page = NULL; ++ void *addr; ++ ++#ifdef CONFIG_DMA_API_DEBUG ++ u64 limit = (mask + 1) & ~mask; ++ if (limit && size >= limit) { ++ dev_warn(dev, "coherent allocation too big (requested %#x mask %#llx)\n", ++ size, mask); ++ return NULL; ++ } ++#endif ++ ++ if (!mask) ++ return NULL; ++ ++ if (mask < 0xffffffffULL) ++ gfp |= GFP_DMA; ++ ++ /* ++ * Following is a work-around (a.k.a. hack) to prevent pages ++ * with __GFP_COMP being passed to split_page() which cannot ++ * handle them. The real problem is that this flag probably ++ * should be 0 on ARM as it is not supported on this ++ * platform; see CONFIG_HUGETLBFS. ++ */ ++ gfp &= ~(__GFP_COMP); ++ ++ *handle = DMA_ERROR_CODE; ++ size = PAGE_ALIGN(size); ++ ++ if (is_coherent || nommu()) ++ addr = __alloc_simple_buffer(dev, size, gfp, &page); ++ else if (!(gfp & __GFP_WAIT)) ++ addr = __alloc_from_pool(size, &page); ++ else if (!IS_ENABLED(CONFIG_DMA_CMA)) ++ addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller); ++ else ++ addr = __alloc_from_contiguous(dev, size, prot, &page, caller); ++ ++ if (addr) ++ *handle = pfn_to_dma(dev, page_to_pfn(page)); ++ ++ return addr; ++} ++ ++/* ++ * Allocate DMA-coherent memory space and return both the kernel remapped ++ * virtual and bus address for that space. ++ */ ++void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, ++ gfp_t gfp, struct dma_attrs *attrs) ++{ ++ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); ++ void *memory; ++ ++ if (dma_alloc_from_coherent(dev, size, handle, &memory)) ++ return memory; ++ ++ return __dma_alloc(dev, size, handle, gfp, prot, false, ++ __builtin_return_address(0)); ++} ++ ++static void *arm_coherent_dma_alloc(struct device *dev, size_t size, ++ dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) ++{ ++ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); ++ void *memory; ++ ++ if (dma_alloc_from_coherent(dev, size, handle, &memory)) ++ return memory; ++ ++ return __dma_alloc(dev, size, handle, gfp, prot, true, ++ __builtin_return_address(0)); ++} ++ ++/* ++ * Create userspace mapping for the DMA-coherent memory. ++ */ ++int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma, ++ void *cpu_addr, dma_addr_t dma_addr, size_t size, ++ struct dma_attrs *attrs) ++{ ++ int ret = -ENXIO; ++#ifdef CONFIG_MMU ++ unsigned long nr_vma_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; ++ unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; ++ unsigned long pfn = dma_to_pfn(dev, dma_addr); ++ unsigned long off = vma->vm_pgoff; ++ ++ vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot); ++ ++ if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) ++ return ret; ++ ++ if (off < nr_pages && nr_vma_pages <= (nr_pages - off)) { ++ ret = remap_pfn_range(vma, vma->vm_start, ++ pfn + off, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot); ++ } ++#endif /* CONFIG_MMU */ ++ ++ return ret; ++} ++ ++/* ++ * Free a buffer as defined by the above mapping. ++ */ ++static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr, ++ dma_addr_t handle, struct dma_attrs *attrs, ++ bool is_coherent) ++{ ++ struct page *page = pfn_to_page(dma_to_pfn(dev, handle)); ++ ++ if (dma_release_from_coherent(dev, get_order(size), cpu_addr)) ++ return; ++ ++ size = PAGE_ALIGN(size); ++ ++ if (is_coherent || nommu()) { ++ __dma_free_buffer(page, size); ++ } else if (__free_from_pool(cpu_addr, size)) { ++ return; ++ } else if (!IS_ENABLED(CONFIG_DMA_CMA)) { ++ __dma_free_remap(cpu_addr, size); ++ __dma_free_buffer(page, size); ++ } else { ++ /* ++ * Non-atomic allocations cannot be freed with IRQs disabled ++ */ ++ WARN_ON(irqs_disabled()); ++ __free_from_contiguous(dev, page, cpu_addr, size); ++ } ++} ++ ++void arm_dma_free(struct device *dev, size_t size, void *cpu_addr, ++ dma_addr_t handle, struct dma_attrs *attrs) ++{ ++ __arm_dma_free(dev, size, cpu_addr, handle, attrs, false); ++} ++ ++static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_addr, ++ dma_addr_t handle, struct dma_attrs *attrs) ++{ ++ __arm_dma_free(dev, size, cpu_addr, handle, attrs, true); ++} ++ ++int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt, ++ void *cpu_addr, dma_addr_t handle, size_t size, ++ struct dma_attrs *attrs) ++{ ++ struct page *page = pfn_to_page(dma_to_pfn(dev, handle)); ++ int ret; ++ ++ ret = sg_alloc_table(sgt, 1, GFP_KERNEL); ++ if (unlikely(ret)) ++ return ret; ++ ++ sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); ++ return 0; ++} ++ ++static void dma_cache_maint_page(struct page *page, unsigned long offset, ++ size_t size, enum dma_data_direction dir, ++ void (*op)(const void *, size_t, int)) ++{ ++ unsigned long pfn; ++ size_t left = size; ++ ++ pfn = page_to_pfn(page) + offset / PAGE_SIZE; ++ offset %= PAGE_SIZE; ++ ++ /* ++ * A single sg entry may refer to multiple physically contiguous ++ * pages. But we still need to process highmem pages individually. ++ * If highmem is not configured then the bulk of this loop gets ++ * optimized out. ++ */ ++ do { ++ size_t len = left; ++ void *vaddr; ++ ++ page = pfn_to_page(pfn); ++ ++ if (PageHighMem(page)) { ++ if (len + offset > PAGE_SIZE) ++ len = PAGE_SIZE - offset; ++ ++ if (cache_is_vipt_nonaliasing()) { ++ vaddr = kmap_atomic(page); ++ op(vaddr + offset, len, dir); ++ kunmap_atomic(vaddr); ++ } else { ++ vaddr = kmap_high_get(page); ++ if (vaddr) { ++ op(vaddr + offset, len, dir); ++ kunmap_high(page); ++ } ++ } ++ } else { ++ vaddr = page_address(page) + offset; ++ op(vaddr, len, dir); ++ } ++ offset = 0; ++ pfn++; ++ left -= len; ++ } while (left); ++} ++ ++/* ++ * Make an area consistent for devices. ++ * Note: Drivers should NOT use this function directly, as it will break ++ * platforms with CONFIG_DMABOUNCE. ++ * Use the driver DMA support - see dma-mapping.h (dma_sync_*) ++ */ ++static void __dma_page_cpu_to_dev(struct page *page, unsigned long off, ++ size_t size, enum dma_data_direction dir) ++{ ++ unsigned long paddr; ++ ++ dma_cache_maint_page(page, off, size, dir, dmac_map_area); ++ ++ paddr = page_to_phys(page) + off; ++ if (dir == DMA_FROM_DEVICE) { ++ outer_inv_range(paddr, paddr + size); ++ } else { ++ outer_clean_range(paddr, paddr + size); ++ } ++ /* FIXME: non-speculating: flush on bidirectional mappings? */ ++} ++ ++static void __dma_page_dev_to_cpu(struct page *page, unsigned long off, ++ size_t size, enum dma_data_direction dir) ++{ ++ unsigned long paddr = page_to_phys(page) + off; ++ ++ /* FIXME: non-speculating: not required */ ++ /* don't bother invalidating if DMA to device */ ++ if (dir != DMA_TO_DEVICE) ++ outer_inv_range(paddr, paddr + size); ++ ++ dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); ++ ++ /* ++ * Mark the D-cache clean for these pages to avoid extra flushing. ++ */ ++ if (dir != DMA_TO_DEVICE && size >= PAGE_SIZE) { ++ unsigned long pfn; ++ size_t left = size; ++ ++ pfn = page_to_pfn(page) + off / PAGE_SIZE; ++ off %= PAGE_SIZE; ++ if (off) { ++ pfn++; ++ left -= PAGE_SIZE - off; ++ } ++ while (left >= PAGE_SIZE) { ++ page = pfn_to_page(pfn++); ++ set_bit(PG_dcache_clean, &page->flags); ++ left -= PAGE_SIZE; ++ } ++ } ++} ++ ++/** ++ * arm_dma_map_sg - map a set of SG buffers for streaming mode DMA ++ * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices ++ * @sg: list of buffers ++ * @nents: number of buffers to map ++ * @dir: DMA transfer direction ++ * ++ * Map a set of buffers described by scatterlist in streaming mode for DMA. ++ * This is the scatter-gather version of the dma_map_single interface. ++ * Here the scatter gather list elements are each tagged with the ++ * appropriate dma address and length. They are obtained via ++ * sg_dma_{address,length}. ++ * ++ * Device ownership issues as mentioned for dma_map_single are the same ++ * here. ++ */ ++int arm_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, ++ enum dma_data_direction dir, struct dma_attrs *attrs) ++{ ++ struct dma_map_ops *ops = get_dma_ops(dev); ++ struct scatterlist *s; ++ int i, j; ++ ++ for_each_sg(sg, s, nents, i) { ++#ifdef CONFIG_NEED_SG_DMA_LENGTH ++ s->dma_length = s->length; ++#endif ++ s->dma_address = ops->map_page(dev, sg_page(s), s->offset, ++ s->length, dir, attrs); ++ if (dma_mapping_error(dev, s->dma_address)) ++ goto bad_mapping; ++ } ++ return nents; ++ ++ bad_mapping: ++ for_each_sg(sg, s, i, j) ++ ops->unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir, attrs); ++ return 0; ++} ++ ++/** ++ * arm_dma_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg ++ * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices ++ * @sg: list of buffers ++ * @nents: number of buffers to unmap (same as was passed to dma_map_sg) ++ * @dir: DMA transfer direction (same as was passed to dma_map_sg) ++ * ++ * Unmap a set of streaming mode DMA translations. Again, CPU access ++ * rules concerning calls here are the same as for dma_unmap_single(). ++ */ ++void arm_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, ++ enum dma_data_direction dir, struct dma_attrs *attrs) ++{ ++ struct dma_map_ops *ops = get_dma_ops(dev); ++ struct scatterlist *s; ++ ++ int i; ++ ++ for_each_sg(sg, s, nents, i) ++ ops->unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir, attrs); ++} ++ ++/** ++ * arm_dma_sync_sg_for_cpu ++ * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices ++ * @sg: list of buffers ++ * @nents: number of buffers to map (returned from dma_map_sg) ++ * @dir: DMA transfer direction (same as was passed to dma_map_sg) ++ */ ++void arm_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir) ++{ ++ struct dma_map_ops *ops = get_dma_ops(dev); ++ struct scatterlist *s; ++ int i; ++ ++ for_each_sg(sg, s, nents, i) ++ ops->sync_single_for_cpu(dev, sg_dma_address(s), s->length, ++ dir); ++} ++ ++/** ++ * arm_dma_sync_sg_for_device ++ * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices ++ * @sg: list of buffers ++ * @nents: number of buffers to map (returned from dma_map_sg) ++ * @dir: DMA transfer direction (same as was passed to dma_map_sg) ++ */ ++void arm_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir) ++{ ++ struct dma_map_ops *ops = get_dma_ops(dev); ++ struct scatterlist *s; ++ int i; ++ ++ for_each_sg(sg, s, nents, i) ++ ops->sync_single_for_device(dev, sg_dma_address(s), s->length, ++ dir); ++} ++ ++/* ++ * Return whether the given device DMA address mask can be supported ++ * properly. For example, if your device can only drive the low 24-bits ++ * during bus mastering, then you would pass 0x00ffffff as the mask ++ * to this function. ++ */ ++int dma_supported(struct device *dev, u64 mask) ++{ ++ return __dma_supported(dev, mask, false); ++} ++EXPORT_SYMBOL(dma_supported); ++ ++int arm_dma_set_mask(struct device *dev, u64 dma_mask) ++{ ++ if (!dev->dma_mask || !dma_supported(dev, dma_mask)) ++ return -EIO; ++ ++ *dev->dma_mask = dma_mask; ++ ++ return 0; ++} ++ ++#define PREALLOC_DMA_DEBUG_ENTRIES 4096 ++ ++static int __init dma_debug_do_init(void) ++{ ++ dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); ++ return 0; ++} ++fs_initcall(dma_debug_do_init); ++ ++#ifdef CONFIG_ARM_DMA_USE_IOMMU ++ ++/* IOMMU */ ++ ++static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, ++ size_t size) ++{ ++ unsigned int order = get_order(size); ++ unsigned int align = 0; ++ unsigned int count, start; ++ unsigned long flags; ++ ++ if (order > CONFIG_ARM_DMA_IOMMU_ALIGNMENT) ++ order = CONFIG_ARM_DMA_IOMMU_ALIGNMENT; ++ ++ count = ((PAGE_ALIGN(size) >> PAGE_SHIFT) + ++ (1 << mapping->order) - 1) >> mapping->order; ++ ++ if (order > mapping->order) ++ align = (1 << (order - mapping->order)) - 1; ++ ++ spin_lock_irqsave(&mapping->lock, flags); ++ start = bitmap_find_next_zero_area(mapping->bitmap, mapping->bits, 0, ++ count, align); ++ if (start > mapping->bits) { ++ spin_unlock_irqrestore(&mapping->lock, flags); ++ return DMA_ERROR_CODE; ++ } ++ ++ bitmap_set(mapping->bitmap, start, count); ++ spin_unlock_irqrestore(&mapping->lock, flags); ++ ++ return mapping->base + (start << (mapping->order + PAGE_SHIFT)); ++} ++ ++static inline void __free_iova(struct dma_iommu_mapping *mapping, ++ dma_addr_t addr, size_t size) ++{ ++ unsigned int start = (addr - mapping->base) >> ++ (mapping->order + PAGE_SHIFT); ++ unsigned int count = ((size >> PAGE_SHIFT) + ++ (1 << mapping->order) - 1) >> mapping->order; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&mapping->lock, flags); ++ bitmap_clear(mapping->bitmap, start, count); ++ spin_unlock_irqrestore(&mapping->lock, flags); ++} ++ ++static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, ++ gfp_t gfp, struct dma_attrs *attrs) ++{ ++ struct page **pages; ++ int count = size >> PAGE_SHIFT; ++ int array_size = count * sizeof(struct page *); ++ int i = 0; ++ ++ if (array_size <= PAGE_SIZE) ++ pages = kzalloc(array_size, gfp); ++ else ++ pages = vzalloc(array_size); ++ if (!pages) ++ return NULL; ++ ++ if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs)) ++ { ++ unsigned long order = get_order(size); ++ struct page *page; ++ ++ page = dma_alloc_from_contiguous(dev, count, order); ++ if (!page) ++ goto error; ++ ++ __dma_clear_buffer(page, size); ++ ++ for (i = 0; i < count; i++) ++ pages[i] = page + i; ++ ++ return pages; ++ } ++ ++ /* ++ * IOMMU can map any pages, so himem can also be used here ++ */ ++ gfp |= __GFP_NOWARN | __GFP_HIGHMEM; ++ ++ while (count) { ++ int j, order = __fls(count); ++ ++ pages[i] = alloc_pages(gfp, order); ++ while (!pages[i] && order) ++ pages[i] = alloc_pages(gfp, --order); ++ if (!pages[i]) ++ goto error; ++ ++ if (order) { ++ split_page(pages[i], order); ++ j = 1 << order; ++ while (--j) ++ pages[i + j] = pages[i] + j; ++ } ++ ++ __dma_clear_buffer(pages[i], PAGE_SIZE << order); ++ i += 1 << order; ++ count -= 1 << order; ++ } ++ ++ return pages; ++error: ++ while (i--) ++ if (pages[i]) ++ __free_pages(pages[i], 0); ++ if (array_size <= PAGE_SIZE) ++ kfree(pages); ++ else ++ vfree(pages); ++ return NULL; ++} ++ ++static int __iommu_free_buffer(struct device *dev, struct page **pages, ++ size_t size, struct dma_attrs *attrs) ++{ ++ int count = size >> PAGE_SHIFT; ++ int array_size = count * sizeof(struct page *); ++ int i; ++ ++ if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs)) { ++ dma_release_from_contiguous(dev, pages[0], count); ++ } else { ++ for (i = 0; i < count; i++) ++ if (pages[i]) ++ __free_pages(pages[i], 0); ++ } ++ ++ if (array_size <= PAGE_SIZE) ++ kfree(pages); ++ else ++ vfree(pages); ++ return 0; ++} ++ ++/* ++ * Create a CPU mapping for a specified pages ++ */ ++static void * ++__iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot, ++ const void *caller) ++{ ++ unsigned int i, nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; ++ struct vm_struct *area; ++ unsigned long p; ++ ++ area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP, ++ caller); ++ if (!area) ++ return NULL; ++ ++ area->pages = pages; ++ area->nr_pages = nr_pages; ++ p = (unsigned long)area->addr; ++ ++ for (i = 0; i < nr_pages; i++) { ++ phys_addr_t phys = __pfn_to_phys(page_to_pfn(pages[i])); ++ if (ioremap_page_range(p, p + PAGE_SIZE, phys, prot)) ++ goto err; ++ p += PAGE_SIZE; ++ } ++ return area->addr; ++err: ++ unmap_kernel_range((unsigned long)area->addr, size); ++ vunmap(area->addr); ++ return NULL; ++} ++ ++/* ++ * Create a mapping in device IO address space for specified pages ++ */ ++static dma_addr_t ++__iommu_create_mapping(struct device *dev, struct page **pages, size_t size) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; ++ dma_addr_t dma_addr, iova; ++ int i, ret = DMA_ERROR_CODE; ++ ++ dma_addr = __alloc_iova(mapping, size); ++ if (dma_addr == DMA_ERROR_CODE) ++ return dma_addr; ++ ++ iova = dma_addr; ++ for (i = 0; i < count; ) { ++ unsigned int next_pfn = page_to_pfn(pages[i]) + 1; ++ phys_addr_t phys = page_to_phys(pages[i]); ++ unsigned int len, j; ++ ++ for (j = i + 1; j < count; j++, next_pfn++) ++ if (page_to_pfn(pages[j]) != next_pfn) ++ break; ++ ++ len = (j - i) << PAGE_SHIFT; ++ ret = iommu_map(mapping->domain, iova, phys, len, ++ IOMMU_READ|IOMMU_WRITE); ++ if (ret < 0) ++ goto fail; ++ iova += len; ++ i = j; ++ } ++ return dma_addr; ++fail: ++ iommu_unmap(mapping->domain, dma_addr, iova-dma_addr); ++ __free_iova(mapping, dma_addr, size); ++ return DMA_ERROR_CODE; ++} ++ ++static int __iommu_remove_mapping(struct device *dev, dma_addr_t iova, size_t size) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ ++ /* ++ * add optional in-page offset from iova to size and align ++ * result to page size ++ */ ++ size = PAGE_ALIGN((iova & ~PAGE_MASK) + size); ++ iova &= PAGE_MASK; ++ ++ iommu_unmap(mapping->domain, iova, size); ++ __free_iova(mapping, iova, size); ++ return 0; ++} ++ ++static struct page **__atomic_get_pages(void *addr) ++{ ++ struct dma_pool *pool = &atomic_pool; ++ struct page **pages = pool->pages; ++ int offs = (addr - pool->vaddr) >> PAGE_SHIFT; ++ ++ return pages + offs; ++} ++ ++static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs) ++{ ++ struct vm_struct *area; ++ ++ if (__in_atomic_pool(cpu_addr, PAGE_SIZE)) ++ return __atomic_get_pages(cpu_addr); ++ ++ if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) ++ return cpu_addr; ++ ++ area = find_vm_area(cpu_addr); ++ if (area && (area->flags & VM_ARM_DMA_CONSISTENT)) ++ return area->pages; ++ return NULL; ++} ++ ++static void *__iommu_alloc_atomic(struct device *dev, size_t size, ++ dma_addr_t *handle) ++{ ++ struct page *page; ++ void *addr; ++ ++ addr = __alloc_from_pool(size, &page); ++ if (!addr) ++ return NULL; ++ ++ *handle = __iommu_create_mapping(dev, &page, size); ++ if (*handle == DMA_ERROR_CODE) ++ goto err_mapping; ++ ++ return addr; ++ ++err_mapping: ++ __free_from_pool(addr, size); ++ return NULL; ++} ++ ++static void __iommu_free_atomic(struct device *dev, void *cpu_addr, ++ dma_addr_t handle, size_t size) ++{ ++ __iommu_remove_mapping(dev, handle, size); ++ __free_from_pool(cpu_addr, size); ++} ++ ++static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, ++ dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) ++{ ++ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); ++ struct page **pages; ++ void *addr = NULL; ++ ++ *handle = DMA_ERROR_CODE; ++ size = PAGE_ALIGN(size); ++ ++ if (!(gfp & __GFP_WAIT)) ++ return __iommu_alloc_atomic(dev, size, handle); ++ ++ /* ++ * Following is a work-around (a.k.a. hack) to prevent pages ++ * with __GFP_COMP being passed to split_page() which cannot ++ * handle them. The real problem is that this flag probably ++ * should be 0 on ARM as it is not supported on this ++ * platform; see CONFIG_HUGETLBFS. ++ */ ++ gfp &= ~(__GFP_COMP); ++ ++ pages = __iommu_alloc_buffer(dev, size, gfp, attrs); ++ if (!pages) ++ return NULL; ++ ++ *handle = __iommu_create_mapping(dev, pages, size); ++ if (*handle == DMA_ERROR_CODE) ++ goto err_buffer; ++ ++ if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) ++ return pages; ++ ++ addr = __iommu_alloc_remap(pages, size, gfp, prot, ++ __builtin_return_address(0)); ++ if (!addr) ++ goto err_mapping; ++ ++ return addr; ++ ++err_mapping: ++ __iommu_remove_mapping(dev, *handle, size); ++err_buffer: ++ __iommu_free_buffer(dev, pages, size, attrs); ++ return NULL; ++} ++ ++static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, ++ void *cpu_addr, dma_addr_t dma_addr, size_t size, ++ struct dma_attrs *attrs) ++{ ++ unsigned long uaddr = vma->vm_start; ++ unsigned long usize = vma->vm_end - vma->vm_start; ++ struct page **pages = __iommu_get_pages(cpu_addr, attrs); ++ ++ vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot); ++ ++ if (!pages) ++ return -ENXIO; ++ ++ do { ++ int ret = vm_insert_page(vma, uaddr, *pages++); ++ if (ret) { ++ pr_err("Remapping memory failed: %d\n", ret); ++ return ret; ++ } ++ uaddr += PAGE_SIZE; ++ usize -= PAGE_SIZE; ++ } while (usize > 0); ++ ++ return 0; ++} ++ ++/* ++ * free a page as defined by the above mapping. ++ * Must not be called with IRQs disabled. ++ */ ++void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr, ++ dma_addr_t handle, struct dma_attrs *attrs) ++{ ++ struct page **pages; ++ size = PAGE_ALIGN(size); ++ ++ if (__in_atomic_pool(cpu_addr, size)) { ++ __iommu_free_atomic(dev, cpu_addr, handle, size); ++ return; ++ } ++ ++ pages = __iommu_get_pages(cpu_addr, attrs); ++ if (!pages) { ++ WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); ++ return; ++ } ++ ++ if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) { ++ unmap_kernel_range((unsigned long)cpu_addr, size); ++ vunmap(cpu_addr); ++ } ++ ++ __iommu_remove_mapping(dev, handle, size); ++ __iommu_free_buffer(dev, pages, size, attrs); ++} ++ ++static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt, ++ void *cpu_addr, dma_addr_t dma_addr, ++ size_t size, struct dma_attrs *attrs) ++{ ++ unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; ++ struct page **pages = __iommu_get_pages(cpu_addr, attrs); ++ ++ if (!pages) ++ return -ENXIO; ++ ++ return sg_alloc_table_from_pages(sgt, pages, count, 0, size, ++ GFP_KERNEL); ++} ++ ++static int __dma_direction_to_prot(enum dma_data_direction dir) ++{ ++ int prot; ++ ++ switch (dir) { ++ case DMA_BIDIRECTIONAL: ++ prot = IOMMU_READ | IOMMU_WRITE; ++ break; ++ case DMA_TO_DEVICE: ++ prot = IOMMU_READ; ++ break; ++ case DMA_FROM_DEVICE: ++ prot = IOMMU_WRITE; ++ break; ++ default: ++ prot = 0; ++ } ++ ++ return prot; ++} ++ ++/* ++ * Map a part of the scatter-gather list into contiguous io address space ++ */ ++static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, ++ size_t size, dma_addr_t *handle, ++ enum dma_data_direction dir, struct dma_attrs *attrs, ++ bool is_coherent) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ dma_addr_t iova, iova_base; ++ int ret = 0; ++ unsigned int count; ++ struct scatterlist *s; ++ int prot; ++ ++ size = PAGE_ALIGN(size); ++ *handle = DMA_ERROR_CODE; ++ ++ iova_base = iova = __alloc_iova(mapping, size); ++ if (iova == DMA_ERROR_CODE) ++ return -ENOMEM; ++ ++ for (count = 0, s = sg; count < (size >> PAGE_SHIFT); s = sg_next(s)) { ++ phys_addr_t phys = page_to_phys(sg_page(s)); ++ unsigned int len = PAGE_ALIGN(s->offset + s->length); ++ ++ if (!is_coherent && ++ !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) ++ __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir); ++ ++ prot = __dma_direction_to_prot(dir); ++ ++ ret = iommu_map(mapping->domain, iova, phys, len, prot); ++ if (ret < 0) ++ goto fail; ++ count += len >> PAGE_SHIFT; ++ iova += len; ++ } ++ *handle = iova_base; ++ ++ return 0; ++fail: ++ iommu_unmap(mapping->domain, iova_base, count * PAGE_SIZE); ++ __free_iova(mapping, iova_base, size); ++ return ret; ++} ++ ++static int __iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, ++ enum dma_data_direction dir, struct dma_attrs *attrs, ++ bool is_coherent) ++{ ++ struct scatterlist *s = sg, *dma = sg, *start = sg; ++ int i, count = 0; ++ unsigned int offset = s->offset; ++ unsigned int size = s->offset + s->length; ++ unsigned int max = dma_get_max_seg_size(dev); ++ ++ for (i = 1; i < nents; i++) { ++ s = sg_next(s); ++ ++ s->dma_address = DMA_ERROR_CODE; ++ s->dma_length = 0; ++ ++ if (s->offset || (size & ~PAGE_MASK) || size + s->length > max) { ++ if (__map_sg_chunk(dev, start, size, &dma->dma_address, ++ dir, attrs, is_coherent) < 0) ++ goto bad_mapping; ++ ++ dma->dma_address += offset; ++ dma->dma_length = size - offset; ++ ++ size = offset = s->offset; ++ start = s; ++ dma = sg_next(dma); ++ count += 1; ++ } ++ size += s->length; ++ } ++ if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs, ++ is_coherent) < 0) ++ goto bad_mapping; ++ ++ dma->dma_address += offset; ++ dma->dma_length = size - offset; ++ ++ return count+1; ++ ++bad_mapping: ++ for_each_sg(sg, s, count, i) ++ __iommu_remove_mapping(dev, sg_dma_address(s), sg_dma_len(s)); ++ return 0; ++} ++ ++/** ++ * arm_coherent_iommu_map_sg - map a set of SG buffers for streaming mode DMA ++ * @dev: valid struct device pointer ++ * @sg: list of buffers ++ * @nents: number of buffers to map ++ * @dir: DMA transfer direction ++ * ++ * Map a set of i/o coherent buffers described by scatterlist in streaming ++ * mode for DMA. The scatter gather list elements are merged together (if ++ * possible) and tagged with the appropriate dma address and length. They are ++ * obtained via sg_dma_{address,length}. ++ */ ++int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir, struct dma_attrs *attrs) ++{ ++ return __iommu_map_sg(dev, sg, nents, dir, attrs, true); ++} ++ ++/** ++ * arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA ++ * @dev: valid struct device pointer ++ * @sg: list of buffers ++ * @nents: number of buffers to map ++ * @dir: DMA transfer direction ++ * ++ * Map a set of buffers described by scatterlist in streaming mode for DMA. ++ * The scatter gather list elements are merged together (if possible) and ++ * tagged with the appropriate dma address and length. They are obtained via ++ * sg_dma_{address,length}. ++ */ ++int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir, struct dma_attrs *attrs) ++{ ++ return __iommu_map_sg(dev, sg, nents, dir, attrs, false); ++} ++ ++static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir, struct dma_attrs *attrs, ++ bool is_coherent) ++{ ++ struct scatterlist *s; ++ int i; ++ ++ for_each_sg(sg, s, nents, i) { ++ if (sg_dma_len(s)) ++ __iommu_remove_mapping(dev, sg_dma_address(s), ++ sg_dma_len(s)); ++ if (!is_coherent && ++ !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) ++ __dma_page_dev_to_cpu(sg_page(s), s->offset, ++ s->length, dir); ++ } ++} ++ ++/** ++ * arm_coherent_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg ++ * @dev: valid struct device pointer ++ * @sg: list of buffers ++ * @nents: number of buffers to unmap (same as was passed to dma_map_sg) ++ * @dir: DMA transfer direction (same as was passed to dma_map_sg) ++ * ++ * Unmap a set of streaming mode DMA translations. Again, CPU access ++ * rules concerning calls here are the same as for dma_unmap_single(). ++ */ ++void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir, struct dma_attrs *attrs) ++{ ++ __iommu_unmap_sg(dev, sg, nents, dir, attrs, true); ++} ++ ++/** ++ * arm_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg ++ * @dev: valid struct device pointer ++ * @sg: list of buffers ++ * @nents: number of buffers to unmap (same as was passed to dma_map_sg) ++ * @dir: DMA transfer direction (same as was passed to dma_map_sg) ++ * ++ * Unmap a set of streaming mode DMA translations. Again, CPU access ++ * rules concerning calls here are the same as for dma_unmap_single(). ++ */ ++void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, ++ enum dma_data_direction dir, struct dma_attrs *attrs) ++{ ++ __iommu_unmap_sg(dev, sg, nents, dir, attrs, false); ++} ++ ++/** ++ * arm_iommu_sync_sg_for_cpu ++ * @dev: valid struct device pointer ++ * @sg: list of buffers ++ * @nents: number of buffers to map (returned from dma_map_sg) ++ * @dir: DMA transfer direction (same as was passed to dma_map_sg) ++ */ ++void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir) ++{ ++ struct scatterlist *s; ++ int i; ++ ++ for_each_sg(sg, s, nents, i) ++ __dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir); ++ ++} ++ ++/** ++ * arm_iommu_sync_sg_for_device ++ * @dev: valid struct device pointer ++ * @sg: list of buffers ++ * @nents: number of buffers to map (returned from dma_map_sg) ++ * @dir: DMA transfer direction (same as was passed to dma_map_sg) ++ */ ++void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg, ++ int nents, enum dma_data_direction dir) ++{ ++ struct scatterlist *s; ++ int i; ++ ++ for_each_sg(sg, s, nents, i) ++ __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir); ++} ++ ++ ++/** ++ * arm_coherent_iommu_map_page ++ * @dev: valid struct device pointer ++ * @page: page that buffer resides in ++ * @offset: offset into page for start of buffer ++ * @size: size of buffer to map ++ * @dir: DMA transfer direction ++ * ++ * Coherent IOMMU aware version of arm_dma_map_page() ++ */ ++static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *page, ++ unsigned long offset, size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ dma_addr_t dma_addr; ++ int ret, prot, len = PAGE_ALIGN(size + offset); ++ ++ dma_addr = __alloc_iova(mapping, len); ++ if (dma_addr == DMA_ERROR_CODE) ++ return dma_addr; ++ ++ prot = __dma_direction_to_prot(dir); ++ ++ ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, prot); ++ if (ret < 0) ++ goto fail; ++ ++ return dma_addr + offset; ++fail: ++ __free_iova(mapping, dma_addr, len); ++ return DMA_ERROR_CODE; ++} ++ ++/** ++ * arm_iommu_map_page ++ * @dev: valid struct device pointer ++ * @page: page that buffer resides in ++ * @offset: offset into page for start of buffer ++ * @size: size of buffer to map ++ * @dir: DMA transfer direction ++ * ++ * IOMMU aware version of arm_dma_map_page() ++ */ ++static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page, ++ unsigned long offset, size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) ++ __dma_page_cpu_to_dev(page, offset, size, dir); ++ ++ return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs); ++} ++ ++/** ++ * arm_coherent_iommu_unmap_page ++ * @dev: valid struct device pointer ++ * @handle: DMA address of buffer ++ * @size: size of buffer (same as passed to dma_map_page) ++ * @dir: DMA transfer direction (same as passed to dma_map_page) ++ * ++ * Coherent IOMMU aware version of arm_dma_unmap_page() ++ */ ++static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle, ++ size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ dma_addr_t iova = handle & PAGE_MASK; ++ int offset = handle & ~PAGE_MASK; ++ int len = PAGE_ALIGN(size + offset); ++ ++ if (!iova) ++ return; ++ ++ iommu_unmap(mapping->domain, iova, len); ++ __free_iova(mapping, iova, len); ++} ++ ++/** ++ * arm_iommu_unmap_page ++ * @dev: valid struct device pointer ++ * @handle: DMA address of buffer ++ * @size: size of buffer (same as passed to dma_map_page) ++ * @dir: DMA transfer direction (same as passed to dma_map_page) ++ * ++ * IOMMU aware version of arm_dma_unmap_page() ++ */ ++static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle, ++ size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ dma_addr_t iova = handle & PAGE_MASK; ++ struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); ++ int offset = handle & ~PAGE_MASK; ++ int len = PAGE_ALIGN(size + offset); ++ ++ if (!iova) ++ return; ++ ++ if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) ++ __dma_page_dev_to_cpu(page, offset, size, dir); ++ ++ iommu_unmap(mapping->domain, iova, len); ++ __free_iova(mapping, iova, len); ++} ++ ++static void arm_iommu_sync_single_for_cpu(struct device *dev, ++ dma_addr_t handle, size_t size, enum dma_data_direction dir) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ dma_addr_t iova = handle & PAGE_MASK; ++ struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); ++ unsigned int offset = handle & ~PAGE_MASK; ++ ++ if (!iova) ++ return; ++ ++ __dma_page_dev_to_cpu(page, offset, size, dir); ++} ++ ++static void arm_iommu_sync_single_for_device(struct device *dev, ++ dma_addr_t handle, size_t size, enum dma_data_direction dir) ++{ ++ struct dma_iommu_mapping *mapping = dev->archdata.mapping; ++ dma_addr_t iova = handle & PAGE_MASK; ++ struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); ++ unsigned int offset = handle & ~PAGE_MASK; ++ ++ if (!iova) ++ return; ++ ++ __dma_page_cpu_to_dev(page, offset, size, dir); ++} ++ ++struct dma_map_ops iommu_ops = { ++ .alloc = arm_iommu_alloc_attrs, ++ .free = arm_iommu_free_attrs, ++ .mmap = arm_iommu_mmap_attrs, ++ .get_sgtable = arm_iommu_get_sgtable, ++ ++ .map_page = arm_iommu_map_page, ++ .unmap_page = arm_iommu_unmap_page, ++ .sync_single_for_cpu = arm_iommu_sync_single_for_cpu, ++ .sync_single_for_device = arm_iommu_sync_single_for_device, ++ ++ .map_sg = arm_iommu_map_sg, ++ .unmap_sg = arm_iommu_unmap_sg, ++ .sync_sg_for_cpu = arm_iommu_sync_sg_for_cpu, ++ .sync_sg_for_device = arm_iommu_sync_sg_for_device, ++ ++ .set_dma_mask = arm_dma_set_mask, ++}; ++ ++struct dma_map_ops iommu_coherent_ops = { ++ .alloc = arm_iommu_alloc_attrs, ++ .free = arm_iommu_free_attrs, ++ .mmap = arm_iommu_mmap_attrs, ++ .get_sgtable = arm_iommu_get_sgtable, ++ ++ .map_page = arm_coherent_iommu_map_page, ++ .unmap_page = arm_coherent_iommu_unmap_page, ++ ++ .map_sg = arm_coherent_iommu_map_sg, ++ .unmap_sg = arm_coherent_iommu_unmap_sg, ++ ++ .set_dma_mask = arm_dma_set_mask, ++}; ++ ++/** ++ * arm_iommu_create_mapping ++ * @bus: pointer to the bus holding the client device (for IOMMU calls) ++ * @base: start address of the valid IO address space ++ * @size: size of the valid IO address space ++ * @order: accuracy of the IO addresses allocations ++ * ++ * Creates a mapping structure which holds information about used/unused ++ * IO address ranges, which is required to perform memory allocation and ++ * mapping with IOMMU aware functions. ++ * ++ * The client device need to be attached to the mapping with ++ * arm_iommu_attach_device function. ++ */ ++struct dma_iommu_mapping * ++arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size, ++ int order) ++{ ++ unsigned int count = size >> (PAGE_SHIFT + order); ++ unsigned int bitmap_size = BITS_TO_LONGS(count) * sizeof(long); ++ struct dma_iommu_mapping *mapping; ++ int err = -ENOMEM; ++ ++ if (!count) ++ return ERR_PTR(-EINVAL); ++ ++ mapping = kzalloc(sizeof(struct dma_iommu_mapping), GFP_KERNEL); ++ if (!mapping) ++ goto err; ++ ++ mapping->bitmap = kzalloc(bitmap_size, GFP_KERNEL); ++ if (!mapping->bitmap) ++ goto err2; ++ ++ mapping->base = base; ++ mapping->bits = BITS_PER_BYTE * bitmap_size; ++ mapping->order = order; ++ spin_lock_init(&mapping->lock); ++ ++ mapping->domain = iommu_domain_alloc(bus); ++ if (!mapping->domain) ++ goto err3; ++ ++ kref_init(&mapping->kref); ++ return mapping; ++err3: ++ kfree(mapping->bitmap); ++err2: ++ kfree(mapping); ++err: ++ return ERR_PTR(err); ++} ++EXPORT_SYMBOL_GPL(arm_iommu_create_mapping); ++ ++static void release_iommu_mapping(struct kref *kref) ++{ ++ struct dma_iommu_mapping *mapping = ++ container_of(kref, struct dma_iommu_mapping, kref); ++ ++ iommu_domain_free(mapping->domain); ++ kfree(mapping->bitmap); ++ kfree(mapping); ++} ++ ++void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping) ++{ ++ if (mapping) ++ kref_put(&mapping->kref, release_iommu_mapping); ++} ++EXPORT_SYMBOL_GPL(arm_iommu_release_mapping); ++ ++/** ++ * arm_iommu_attach_device ++ * @dev: valid struct device pointer ++ * @mapping: io address space mapping structure (returned from ++ * arm_iommu_create_mapping) ++ * ++ * Attaches specified io address space mapping to the provided device, ++ * this replaces the dma operations (dma_map_ops pointer) with the ++ * IOMMU aware version. More than one client might be attached to ++ * the same io address space mapping. ++ */ ++int arm_iommu_attach_device(struct device *dev, ++ struct dma_iommu_mapping *mapping) ++{ ++ int err; ++ ++ err = iommu_attach_device(mapping->domain, dev); ++ if (err) ++ return err; ++ ++ kref_get(&mapping->kref); ++ dev->archdata.mapping = mapping; ++ set_dma_ops(dev, &iommu_ops); ++ ++ pr_debug("Attached IOMMU controller to %s device.\n", dev_name(dev)); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(arm_iommu_attach_device); ++ ++/** ++ * arm_iommu_detach_device ++ * @dev: valid struct device pointer ++ * ++ * Detaches the provided device from a previously attached map. ++ * This voids the dma operations (dma_map_ops pointer) ++ */ ++void arm_iommu_detach_device(struct device *dev) ++{ ++ struct dma_iommu_mapping *mapping; ++ ++ mapping = to_dma_iommu_mapping(dev); ++ if (!mapping) { ++ dev_warn(dev, "Not attached\n"); ++ return; ++ } ++ ++ iommu_detach_device(mapping->domain, dev); ++ kref_put(&mapping->kref, release_iommu_mapping); ++ dev->archdata.mapping = NULL; ++ set_dma_ops(dev, NULL); ++ ++ pr_debug("Detached IOMMU controller from %s device.\n", dev_name(dev)); ++} ++EXPORT_SYMBOL_GPL(arm_iommu_detach_device); ++ ++#endif +diff -Nur linux-3.14.36/arch/arm/mm/fault.c linux-openelec/arch/arm/mm/fault.c +--- linux-3.14.36/arch/arm/mm/fault.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/fault.c 2015-05-06 12:05:43.000000000 -0500 +@@ -449,8 +449,16 @@ + + if (pud_none(*pud_k)) + goto bad_area; +- if (!pud_present(*pud)) ++ if (!pud_present(*pud)) { + set_pud(pud, *pud_k); ++ /* ++ * There is a small window during free_pgtables() where the ++ * user *pud entry is 0 but the TLB has not been invalidated ++ * and we get a level 2 (pmd) translation fault caused by the ++ * intermediate TLB caching of the old level 1 (pud) entry. ++ */ ++ flush_tlb_kernel_page(addr); ++ } + + pmd = pmd_offset(pud, addr); + pmd_k = pmd_offset(pud_k, addr); +@@ -473,8 +481,9 @@ + #endif + if (pmd_none(pmd_k[index])) + goto bad_area; ++ if (!pmd_present(pmd[index])) ++ copy_pmd(pmd, pmd_k); + +- copy_pmd(pmd, pmd_k); + return 0; + + bad_area: +diff -Nur linux-3.14.36/arch/arm/mm/init.c linux-openelec/arch/arm/mm/init.c +--- linux-3.14.36/arch/arm/mm/init.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/init.c 2015-05-06 12:05:43.000000000 -0500 +@@ -327,7 +327,7 @@ + * reserve memory for DMA contigouos allocations, + * must come from DMA area inside low memory + */ +- dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit)); ++ dma_contiguous_reserve(arm_dma_limit); + + arm_memblock_steal_permitted = false; + memblock_dump_all(); +diff -Nur linux-3.14.36/arch/arm/mm/Kconfig linux-openelec/arch/arm/mm/Kconfig +--- linux-3.14.36/arch/arm/mm/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/Kconfig 2015-07-24 18:03:29.012842002 -0500 +@@ -898,6 +898,57 @@ + This option enables optimisations for the PL310 cache + controller. + ++config PL310_ERRATA_588369 ++ bool "PL310 errata: Clean & Invalidate maintenance operations do not invalidate clean lines" ++ depends on CACHE_L2X0 ++ help ++ The PL310 L2 cache controller implements three types of Clean & ++ Invalidate maintenance operations: by Physical Address ++ (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC). ++ They are architecturally defined to behave as the execution of a ++ clean operation followed immediately by an invalidate operation, ++ both performing to the same memory location. This functionality ++ is not correctly implemented in PL310 as clean lines are not ++ invalidated as a result of these operations. ++ ++config PL310_ERRATA_727915 ++ bool "PL310 errata: Background Clean & Invalidate by Way operation can cause data corruption" ++ depends on CACHE_L2X0 ++ help ++ PL310 implements the Clean & Invalidate by Way L2 cache maintenance ++ operation (offset 0x7FC). This operation runs in background so that ++ PL310 can handle normal accesses while it is in progress. Under very ++ rare circumstances, due to this erratum, write data can be lost when ++ PL310 treats a cacheable write transaction during a Clean & ++ Invalidate by Way operation. ++ ++config PL310_ERRATA_753970 ++ bool "PL310 errata: cache sync operation may be faulty" ++ depends on CACHE_PL310 ++ help ++ This option enables the workaround for the 753970 PL310 (r3p0) erratum. ++ ++ Under some condition the effect of cache sync operation on ++ the store buffer still remains when the operation completes. ++ This means that the store buffer is always asked to drain and ++ this prevents it from merging any further writes. The workaround ++ is to replace the normal offset of cache sync operation (0x730) ++ by another offset targeting an unmapped PL310 register 0x740. ++ This has the same effect as the cache sync operation: store buffer ++ drain and waiting for all buffers empty. ++ ++config PL310_ERRATA_769419 ++ bool "PL310 errata: no automatic Store Buffer drain" ++ depends on CACHE_L2X0 ++ help ++ On revisions of the PL310 prior to r3p2, the Store Buffer does ++ not automatically drain. This can cause normal, non-cacheable ++ writes to be retained when the memory system is idle, leading ++ to suboptimal I/O performance for drivers using coherent DMA. ++ This option adds a write barrier to the cpu_idle loop so that, ++ on systems with an outer cache, the store buffer is drained ++ explicitly. ++ + config CACHE_TAUROS2 + bool "Enable the Tauros2 L2 cache controller" + depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4) +diff -Nur linux-3.14.36/arch/arm/mm/l2c-common.c linux-openelec/arch/arm/mm/l2c-common.c +--- linux-3.14.36/arch/arm/mm/l2c-common.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mm/l2c-common.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (C) 2010 ARM Ltd. ++ * Written by Catalin Marinas ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++ ++void outer_disable(void) ++{ ++ WARN_ON(!irqs_disabled()); ++ WARN_ON(num_online_cpus() > 1); ++ ++ if (outer_cache.disable) ++ outer_cache.disable(); ++} +diff -Nur linux-3.14.36/arch/arm/mm/l2c-l2x0-resume.S linux-openelec/arch/arm/mm/l2c-l2x0-resume.S +--- linux-3.14.36/arch/arm/mm/l2c-l2x0-resume.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm/mm/l2c-l2x0-resume.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,58 @@ ++/* ++ * L2C-310 early resume code. This can be used by platforms to restore ++ * the settings of their L2 cache controller before restoring the ++ * processor state. ++ * ++ * This code can only be used to if you are running in the secure world. ++ */ ++#include ++#include ++ ++ .text ++ ++ENTRY(l2c310_early_resume) ++ adr r0, 1f ++ ldr r2, [r0] ++ add r0, r2, r0 ++ ++ ldmia r0, {r1, r2, r3, r4, r5, r6, r7, r8} ++ @ r1 = phys address of L2C-310 controller ++ @ r2 = aux_ctrl ++ @ r3 = tag_latency ++ @ r4 = data_latency ++ @ r5 = filter_start ++ @ r6 = filter_end ++ @ r7 = prefetch_ctrl ++ @ r8 = pwr_ctrl ++ ++ @ Check that the address has been initialised ++ teq r1, #0 ++ moveq pc, lr ++ ++ @ The prefetch and power control registers are revision dependent ++ @ and can be written whether or not the L2 cache is enabled ++ ldr r0, [r1, #L2X0_CACHE_ID] ++ and r0, r0, #L2X0_CACHE_ID_RTL_MASK ++ cmp r0, #L310_CACHE_ID_RTL_R2P0 ++ strcs r7, [r1, #L310_PREFETCH_CTRL] ++ cmp r0, #L310_CACHE_ID_RTL_R3P0 ++ strcs r8, [r1, #L310_POWER_CTRL] ++ ++ @ Don't setup the L2 cache if it is already enabled ++ ldr r0, [r1, #L2X0_CTRL] ++ tst r0, #L2X0_CTRL_EN ++ movne pc, lr ++ ++ str r3, [r1, #L310_TAG_LATENCY_CTRL] ++ str r4, [r1, #L310_DATA_LATENCY_CTRL] ++ str r6, [r1, #L310_ADDR_FILTER_END] ++ str r5, [r1, #L310_ADDR_FILTER_START] ++ ++ str r2, [r1, #L2X0_AUX_CTRL] ++ mov r9, #L2X0_CTRL_EN ++ str r9, [r1, #L2X0_CTRL] ++ mov pc, lr ++ENDPROC(l2c310_early_resume) ++ ++ .align ++1: .long l2x0_saved_regs - . +diff -Nur linux-3.14.36/arch/arm/mm/Makefile linux-openelec/arch/arm/mm/Makefile +--- linux-3.14.36/arch/arm/mm/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -95,7 +95,8 @@ + AFLAGS_proc-v6.o :=-Wa,-march=armv6 + AFLAGS_proc-v7.o :=-Wa,-march=armv7-a + ++obj-$(CONFIG_OUTER_CACHE) += l2c-common.o + obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o +-obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o ++obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o l2c-l2x0-resume.o + obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o + obj-$(CONFIG_CACHE_TAUROS2) += cache-tauros2.o +diff -Nur linux-3.14.36/arch/arm/mm/proc-v7.S linux-openelec/arch/arm/mm/proc-v7.S +--- linux-3.14.36/arch/arm/mm/proc-v7.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm/mm/proc-v7.S 2015-07-24 18:03:29.072842002 -0500 +@@ -334,6 +334,17 @@ + mcrlt p15, 0, r10, c15, c0, 1 @ write diagnostic register + 1: + #endif ++#ifdef CONFIG_ARM_ERRATA_794072 ++ mrc p15, 0, r10, c15, c0, 1 @ read diagnostic register ++ orr r10, r10, #1 << 4 @ set bit #4 ++ mcr p15, 0, r10, c15, c0, 1 @ write diagnostic register ++#endif ++#ifdef CONFIG_ARM_ERRATA_761320 ++ cmp r6, #0x40 @ present prior to r4p0 ++ mrclt p15, 0, r10, c15, c0, 1 @ read diagnostic register ++ orrlt r10, r10, #1 << 21 @ set bit #21 ++ mcrlt p15, 0, r10, c15, c0, 1 @ write diagnostic register ++#endif + + /* Cortex-A15 Errata */ + 3: ldr r10, =0x00000c0f @ Cortex-A15 primary part number +diff -Nur linux-3.14.36/arch/arm64/boot/dts/apm-mustang.dts linux-openelec/arch/arm64/boot/dts/apm-mustang.dts +--- linux-3.14.36/arch/arm64/boot/dts/apm-mustang.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/boot/dts/apm-mustang.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -24,3 +24,7 @@ + reg = < 0x1 0x00000000 0x0 0x80000000 >; /* Updated by bootloader */ + }; + }; ++ ++&serial0 { ++ status = "ok"; ++}; +diff -Nur linux-3.14.36/arch/arm64/boot/dts/apm-storm.dtsi linux-openelec/arch/arm64/boot/dts/apm-storm.dtsi +--- linux-3.14.36/arch/arm64/boot/dts/apm-storm.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/boot/dts/apm-storm.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -176,16 +176,226 @@ + reg-names = "csr-reg"; + clock-output-names = "eth8clk"; + }; ++ ++ sataphy1clk: sataphy1clk@1f21c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f21c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sataphy1clk"; ++ status = "disabled"; ++ csr-offset = <0x4>; ++ csr-mask = <0x00>; ++ enable-offset = <0x0>; ++ enable-mask = <0x06>; ++ }; ++ ++ sataphy2clk: sataphy1clk@1f22c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f22c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sataphy2clk"; ++ status = "ok"; ++ csr-offset = <0x4>; ++ csr-mask = <0x3a>; ++ enable-offset = <0x0>; ++ enable-mask = <0x06>; ++ }; ++ ++ sataphy3clk: sataphy1clk@1f23c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f23c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sataphy3clk"; ++ status = "ok"; ++ csr-offset = <0x4>; ++ csr-mask = <0x3a>; ++ enable-offset = <0x0>; ++ enable-mask = <0x06>; ++ }; ++ ++ sata01clk: sata01clk@1f21c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f21c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sata01clk"; ++ csr-offset = <0x4>; ++ csr-mask = <0x05>; ++ enable-offset = <0x0>; ++ enable-mask = <0x39>; ++ }; ++ ++ sata23clk: sata23clk@1f22c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f22c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sata23clk"; ++ csr-offset = <0x4>; ++ csr-mask = <0x05>; ++ enable-offset = <0x0>; ++ enable-mask = <0x39>; ++ }; ++ ++ sata45clk: sata45clk@1f23c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f23c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sata45clk"; ++ csr-offset = <0x4>; ++ csr-mask = <0x05>; ++ enable-offset = <0x0>; ++ enable-mask = <0x39>; ++ }; ++ ++ rtcclk: rtcclk@17000000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x17000000 0x0 0x2000>; ++ reg-names = "csr-reg"; ++ csr-offset = <0xc>; ++ csr-mask = <0x2>; ++ enable-offset = <0x10>; ++ enable-mask = <0x2>; ++ clock-output-names = "rtcclk"; ++ }; + }; + + serial0: serial@1c020000 { ++ status = "disabled"; + device_type = "serial"; +- compatible = "ns16550"; ++ compatible = "ns16550a"; + reg = <0 0x1c020000 0x0 0x1000>; + reg-shift = <2>; + clock-frequency = <10000000>; /* Updated by bootloader */ + interrupt-parent = <&gic>; + interrupts = <0x0 0x4c 0x4>; + }; ++ ++ serial1: serial@1c021000 { ++ status = "disabled"; ++ device_type = "serial"; ++ compatible = "ns16550a"; ++ reg = <0 0x1c021000 0x0 0x1000>; ++ reg-shift = <2>; ++ clock-frequency = <10000000>; /* Updated by bootloader */ ++ interrupt-parent = <&gic>; ++ interrupts = <0x0 0x4d 0x4>; ++ }; ++ ++ serial2: serial@1c022000 { ++ status = "disabled"; ++ device_type = "serial"; ++ compatible = "ns16550a"; ++ reg = <0 0x1c022000 0x0 0x1000>; ++ reg-shift = <2>; ++ clock-frequency = <10000000>; /* Updated by bootloader */ ++ interrupt-parent = <&gic>; ++ interrupts = <0x0 0x4e 0x4>; ++ }; ++ ++ serial3: serial@1c023000 { ++ status = "disabled"; ++ device_type = "serial"; ++ compatible = "ns16550a"; ++ reg = <0 0x1c023000 0x0 0x1000>; ++ reg-shift = <2>; ++ clock-frequency = <10000000>; /* Updated by bootloader */ ++ interrupt-parent = <&gic>; ++ interrupts = <0x0 0x4f 0x4>; ++ }; ++ ++ phy1: phy@1f21a000 { ++ compatible = "apm,xgene-phy"; ++ reg = <0x0 0x1f21a000 0x0 0x100>; ++ #phy-cells = <1>; ++ clocks = <&sataphy1clk 0>; ++ status = "disabled"; ++ apm,tx-boost-gain = <30 30 30 30 30 30>; ++ apm,tx-eye-tuning = <2 10 10 2 10 10>; ++ }; ++ ++ phy2: phy@1f22a000 { ++ compatible = "apm,xgene-phy"; ++ reg = <0x0 0x1f22a000 0x0 0x100>; ++ #phy-cells = <1>; ++ clocks = <&sataphy2clk 0>; ++ status = "ok"; ++ apm,tx-boost-gain = <30 30 30 30 30 30>; ++ apm,tx-eye-tuning = <1 10 10 2 10 10>; ++ }; ++ ++ phy3: phy@1f23a000 { ++ compatible = "apm,xgene-phy"; ++ reg = <0x0 0x1f23a000 0x0 0x100>; ++ #phy-cells = <1>; ++ clocks = <&sataphy3clk 0>; ++ status = "ok"; ++ apm,tx-boost-gain = <31 31 31 31 31 31>; ++ apm,tx-eye-tuning = <2 10 10 2 10 10>; ++ }; ++ ++ sata1: sata@1a000000 { ++ compatible = "apm,xgene-ahci"; ++ reg = <0x0 0x1a000000 0x0 0x1000>, ++ <0x0 0x1f210000 0x0 0x1000>, ++ <0x0 0x1f21d000 0x0 0x1000>, ++ <0x0 0x1f21e000 0x0 0x1000>, ++ <0x0 0x1f217000 0x0 0x1000>; ++ interrupts = <0x0 0x86 0x4>; ++ dma-coherent; ++ status = "disabled"; ++ clocks = <&sata01clk 0>; ++ phys = <&phy1 0>; ++ phy-names = "sata-phy"; ++ }; ++ ++ sata2: sata@1a400000 { ++ compatible = "apm,xgene-ahci"; ++ reg = <0x0 0x1a400000 0x0 0x1000>, ++ <0x0 0x1f220000 0x0 0x1000>, ++ <0x0 0x1f22d000 0x0 0x1000>, ++ <0x0 0x1f22e000 0x0 0x1000>, ++ <0x0 0x1f227000 0x0 0x1000>; ++ interrupts = <0x0 0x87 0x4>; ++ dma-coherent; ++ status = "ok"; ++ clocks = <&sata23clk 0>; ++ phys = <&phy2 0>; ++ phy-names = "sata-phy"; ++ }; ++ ++ sata3: sata@1a800000 { ++ compatible = "apm,xgene-ahci"; ++ reg = <0x0 0x1a800000 0x0 0x1000>, ++ <0x0 0x1f230000 0x0 0x1000>, ++ <0x0 0x1f23d000 0x0 0x1000>, ++ <0x0 0x1f23e000 0x0 0x1000>; ++ interrupts = <0x0 0x88 0x4>; ++ dma-coherent; ++ status = "ok"; ++ clocks = <&sata45clk 0>; ++ phys = <&phy3 0>; ++ phy-names = "sata-phy"; ++ }; ++ ++ rtc: rtc@10510000 { ++ compatible = "apm,xgene-rtc"; ++ reg = <0x0 0x10510000 0x0 0x400>; ++ interrupts = <0x0 0x46 0x4>; ++ #clock-cells = <1>; ++ clocks = <&rtcclk 0>; ++ }; + }; + }; +diff -Nur linux-3.14.36/arch/arm64/boot/dts/clcd-panels.dtsi linux-openelec/arch/arm64/boot/dts/clcd-panels.dtsi +--- linux-3.14.36/arch/arm64/boot/dts/clcd-panels.dtsi 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/boot/dts/clcd-panels.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,52 @@ ++/* ++ * ARM Ltd. Versatile Express ++ * ++ */ ++ ++/ { ++ panels { ++ panel@0 { ++ compatible = "panel"; ++ mode = "VGA"; ++ refresh = <60>; ++ xres = <640>; ++ yres = <480>; ++ pixclock = <39721>; ++ left_margin = <40>; ++ right_margin = <24>; ++ upper_margin = <32>; ++ lower_margin = <11>; ++ hsync_len = <96>; ++ vsync_len = <2>; ++ sync = <0>; ++ vmode = "FB_VMODE_NONINTERLACED"; ++ ++ tim2 = "TIM2_BCD", "TIM2_IPC"; ++ cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)"; ++ caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888"; ++ bpp = <16>; ++ }; ++ ++ panel@1 { ++ compatible = "panel"; ++ mode = "XVGA"; ++ refresh = <60>; ++ xres = <1024>; ++ yres = <768>; ++ pixclock = <15748>; ++ left_margin = <152>; ++ right_margin = <48>; ++ upper_margin = <23>; ++ lower_margin = <3>; ++ hsync_len = <104>; ++ vsync_len = <4>; ++ sync = <0>; ++ vmode = "FB_VMODE_NONINTERLACED"; ++ ++ tim2 = "TIM2_BCD", "TIM2_IPC"; ++ cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)"; ++ caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888"; ++ bpp = <16>; ++ }; ++ }; ++}; +diff -Nur linux-3.14.36/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts linux-openelec/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts +--- linux-3.14.36/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,266 @@ ++/* ++ * Copyright (c) 2013, ARM Limited. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * Redistributions of source code must retain the above copyright notice, this ++ * list of conditions and the following disclaimer. ++ * ++ * Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * ++ * Neither the name of ARM nor the names of its contributors may be used ++ * to endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/dts-v1/; ++ ++/memreserve/ 0x80000000 0x00010000; ++ ++/ { ++}; ++ ++/ { ++ model = "FVP Base"; ++ compatible = "arm,vfp-base", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ psci { ++ compatible = "arm,psci"; ++ method = "smc"; ++ cpu_suspend = <0xc4000001>; ++ cpu_off = <0x84000002>; ++ cpu_on = <0xc4000003>; ++ }; ++ ++ cpus { ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ big0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a57", "arm,armv8"; ++ reg = <0x0 0x0>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ big1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a57", "arm,armv8"; ++ reg = <0x0 0x1>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ big2: cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a57", "arm,armv8"; ++ reg = <0x0 0x2>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ big3: cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a57", "arm,armv8"; ++ reg = <0x0 0x3>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ little0: cpu@100 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ reg = <0x0 0x100>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ little1: cpu@101 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ reg = <0x0 0x101>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ little2: cpu@102 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ reg = <0x0 0x102>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ little3: cpu@103 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ reg = <0x0 0x103>; ++ enable-method = "psci"; ++ clock-frequency = <1000000>; ++ }; ++ ++ cpu-map { ++ cluster0 { ++ core0 { ++ cpu = <&big0>; ++ }; ++ core1 { ++ cpu = <&big1>; ++ }; ++ core2 { ++ cpu = <&big2>; ++ }; ++ core3 { ++ cpu = <&big3>; ++ }; ++ }; ++ cluster1 { ++ core0 { ++ cpu = <&little0>; ++ }; ++ core1 { ++ cpu = <&little1>; ++ }; ++ core2 { ++ cpu = <&little2>; ++ }; ++ core3 { ++ cpu = <&little3>; ++ }; ++ }; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x00000000 0x80000000 0 0x80000000>, ++ <0x00000008 0x80000000 0 0x80000000>; ++ }; ++ ++ gic: interrupt-controller@2f000000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0x0 0x2f000000 0 0x10000>, ++ <0x0 0x2c000000 0 0x2000>, ++ <0x0 0x2c010000 0 0x2000>, ++ <0x0 0x2c02F000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = <1 13 0xff01>, ++ <1 14 0xff01>, ++ <1 11 0xff01>, ++ <1 10 0xff01>; ++ clock-frequency = <100000000>; ++ }; ++ ++ timer@2a810000 { ++ compatible = "arm,armv7-timer-mem"; ++ reg = <0x0 0x2a810000 0x0 0x10000>; ++ clock-frequency = <100000000>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ frame@2a820000 { ++ frame-number = <0>; ++ interrupts = <0 25 4>; ++ reg = <0x0 0x2a820000 0x0 0x10000>; ++ }; ++ }; ++ ++ pmu { ++ compatible = "arm,armv8-pmuv3"; ++ interrupts = <0 60 4>, ++ <0 61 4>, ++ <0 62 4>, ++ <0 63 4>; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm64/boot/dts/juno.dts linux-openelec/arch/arm64/boot/dts/juno.dts +--- linux-3.14.36/arch/arm64/boot/dts/juno.dts 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/boot/dts/juno.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,498 @@ ++/* ++ * ARM Ltd. Juno Plaform ++ * ++ * Fast Models FVP v2 support ++ */ ++ ++/dts-v1/; ++ ++#include ++ ++/ { ++ model = "Juno"; ++ compatible = "arm,juno", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ aliases { ++ serial0 = &soc_uart0; ++ }; ++ ++ cpus { ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ cpu@100 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x100>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@101 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x101>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@102 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x102>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@103 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x103>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a57","arm,armv8"; ++ reg = <0x0 0x0>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a57","arm,armv8"; ++ reg = <0x0 0x1>; ++ enable-method = "psci"; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x00000000 0x80000000 0x0 0x80000000>, ++ <0x00000008 0x80000000 0x1 0x80000000>; ++ }; ++ ++ /* memory@14000000 { ++ device_type = "memory"; ++ reg = <0x00000000 0x14000000 0x0 0x02000000>; ++ }; */ ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0x0 0x2c010000 0 0x1000>, ++ <0x0 0x2c02f000 0 0x1000>, ++ <0x0 0x2c04f000 0 0x2000>, ++ <0x0 0x2c06f000 0 0x2000>; ++ interrupts = ; ++ }; ++ ++ msi0: msi@2c1c0000 { ++ compatible = "arm,gic-msi"; ++ reg = <0x0 0x2c1c0000 0 0x10000 ++ 0x0 0x2c1d0000 0 0x10000 ++ 0x0 0x2c1e0000 0 0x10000 ++ 0x0 0x2c1f0000 0 0x10000>; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = , ++ , ++ , ++ ; ++ }; ++ ++ pmu { ++ compatible = "arm,armv8-pmuv3"; ++ interrupts = , ++ , ++ , ++ ; ++ }; ++ ++ psci { ++ compatible = "arm,psci"; ++ method = "smc"; ++ cpu_suspend = <0xC4000001>; ++ cpu_off = <0x84000002>; ++ cpu_on = <0xC4000003>; ++ migrate = <0xC4000005>; ++ }; ++ ++ pci0: pci@30000000 { ++ compatible = "arm,pcie-xr3"; ++ device_type = "pci"; ++ reg = <0 0x7ff30000 0 0x1000 ++ 0 0x7ff20000 0 0x10000 ++ 0 0x40000000 0 0x10000000>; ++ bus-range = <0 255>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ranges = <0x01000000 0x0 0x00000000 0x00 0x5ff00000 0x0 0x00100000 ++ 0x02000000 0x0 0x00000000 0x40 0x00000000 0x0 0x80000000 ++ 0x42000000 0x0 0x80000000 0x40 0x80000000 0x0 0x80000000>; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &gic 0 136 4 ++ 0 0 0 2 &gic 0 137 4 ++ 0 0 0 3 &gic 0 138 4 ++ 0 0 0 4 &gic 0 139 4>; ++ }; ++ ++ scpi: scpi@2b1f0000 { ++ compatible = "arm,scpi-mhu"; ++ reg = <0x0 0x2b1f0000 0x0 0x10000>, /* MHU registers */ ++ <0x0 0x2e000000 0x0 0x10000>; /* Payload area */ ++ interrupts = <0 36 4>, /* low priority interrupt */ ++ <0 35 4>, /* high priority interrupt */ ++ <0 37 4>; /* secure channel interrupt */ ++ #clock-cells = <1>; ++ clock-output-names = "a57", "a53", "gpu", "hdlcd0", "hdlcd1"; ++ }; ++ ++ hdlcd0_osc: scpi_osc@3 { ++ compatible = "arm,scpi-osc"; ++ #clock-cells = <0>; ++ clocks = <&scpi 3>; ++ frequency-range = <23000000 210000000>; ++ clock-output-names = "pxlclk0"; ++ }; ++ ++ hdlcd1_osc: scpi_osc@4 { ++ compatible = "arm,scpi-osc"; ++ #clock-cells = <0>; ++ clocks = <&scpi 4>; ++ frequency-range = <23000000 210000000>; ++ clock-output-names = "pxlclk1"; ++ }; ++ ++ soc_uartclk: refclk72738khz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <7273800>; ++ clock-output-names = "juno:uartclk"; ++ }; ++ ++ soc_refclk24mhz: clk24mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ clock-output-names = "juno:clk24mhz"; ++ }; ++ ++ mb_eth25mhz: clk25mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <25000000>; ++ clock-output-names = "ethclk25mhz"; ++ }; ++ ++ soc_usb48mhz: clk48mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <48000000>; ++ clock-output-names = "clk48mhz"; ++ }; ++ ++ soc_smc50mhz: clk50mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <50000000>; ++ clock-output-names = "smc_clk"; ++ }; ++ ++ soc_refclk100mhz: refclk100mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <100000000>; ++ clock-output-names = "apb_pclk"; ++ }; ++ ++ soc_faxiclk: refclk533mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <533000000>; ++ clock-output-names = "faxi_clk"; ++ }; ++ ++ soc_fixed_3v3: fixedregulator@0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "3V3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ memory-controller@7ffd0000 { ++ compatible = "arm,pl354", "arm,primecell"; ++ reg = <0 0x7ffd0000 0 0x1000>; ++ interrupts = <0 86 4>, ++ <0 87 4>; ++ clocks = <&soc_smc50mhz>; ++ clock-names = "apb_pclk"; ++ chip5-memwidth = <16>; ++ }; ++ ++ dma0: dma@0x7ff00000 { ++ compatible = "arm,pl330", "arm,primecell"; ++ reg = <0x0 0x7ff00000 0 0x1000>; ++ interrupts = <0 95 4>, ++ <0 88 4>, ++ <0 89 4>, ++ <0 90 4>, ++ <0 91 4>, ++ <0 108 4>, ++ <0 109 4>, ++ <0 110 4>, ++ <0 111 4>; ++ #dma-cells = <1>; ++ #dma-channels = <8>; ++ #dma-requests = <32>; ++ clocks = <&soc_faxiclk>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ soc_uart0: uart@7ff80000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0 0x7ff80000 0x0 0x1000>; ++ interrupts = <0 83 4>; ++ clocks = <&soc_uartclk>, <&soc_refclk100mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ dmas = <&dma0 1 ++ &dma0 2>; ++ dma-names = "rx", "tx"; ++ }; ++ ++ /* this UART is reserved for secure software. ++ soc_uart1: uart@7ff70000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0 0x7ff70000 0x0 0x1000>; ++ interrupts = <0 84 4>; ++ clocks = <&soc_uartclk>, <&soc_refclk100mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; */ ++ ++ ulpi_phy: phy@0 { ++ compatible = "phy-ulpi-generic"; ++ reg = <0x0 0x94 0x0 0x4>; ++ phy-id = <0>; ++ }; ++ ++ ehci@7ffc0000 { ++ compatible = "snps,ehci-h20ahb"; ++ /* compatible = "arm,h20ahb-ehci"; */ ++ reg = <0x0 0x7ffc0000 0x0 0x10000>; ++ interrupts = <0 117 4>; ++ clocks = <&soc_usb48mhz>; ++ clock-names = "otg"; ++ phys = <&ulpi_phy>; ++ }; ++ ++ ohci@0x7ffb0000 { ++ compatible = "generic-ohci"; ++ reg = <0x0 0x7ffb0000 0x0 0x10000>; ++ interrupts = <0 116 4>; ++ clocks = <&soc_usb48mhz>; ++ clock-names = "otg"; ++ }; ++ ++ i2c@0x7ffa0000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x7ffa0000 0x0 0x1000>; ++ interrupts = <0 104 4>; ++ clock-frequency = <400000>; ++ i2c-sda-hold-time-ns = <500>; ++ clocks = <&soc_smc50mhz>; ++ ++ dvi0: dvi-transmitter@70 { ++ compatible = "nxp,tda998x"; ++ reg = <0x70>; ++ }; ++ ++ dvi1: dvi-transmitter@71 { ++ compatible = "nxp,tda998x"; ++ reg = <0x71>; ++ }; ++ }; ++ ++ /* mmci@1c050000 { ++ compatible = "arm,pl180", "arm,primecell"; ++ reg = <0x0 0x1c050000 0x0 0x1000>; ++ interrupts = <0 73 4>, ++ <0 74 4>; ++ max-frequency = <12000000>; ++ vmmc-supply = <&soc_fixed_3v3>; ++ clocks = <&soc_refclk24mhz>, <&soc_refclk100mhz>; ++ clock-names = "mclk", "apb_pclk"; ++ }; */ ++ ++ hdlcd@7ff60000 { ++ compatible = "arm,hdlcd"; ++ reg = <0 0x7ff60000 0 0x1000>; ++ interrupts = <0 85 4>; ++ clocks = <&hdlcd0_osc>; ++ clock-names = "pxlclk"; ++ i2c-slave = <&dvi0>; ++ ++ /* display-timings { ++ native-mode = <&timing0>; ++ timing0: timing@0 { ++ /* 1024 x 768 framebufer, standard VGA timings * / ++ clock-frequency = <65000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hfront-porch = <24>; ++ hback-porch = <160>; ++ hsync-len = <136>; ++ vfront-porch = <3>; ++ vback-porch = <29>; ++ vsync-len = <6>; ++ }; ++ }; */ ++ }; ++ ++ hdlcd@7ff50000 { ++ compatible = "arm,hdlcd"; ++ reg = <0 0x7ff50000 0 0x1000>; ++ interrupts = <0 93 4>; ++ clocks = <&hdlcd1_osc>; ++ clock-names = "pxlclk"; ++ i2c-slave = <&dvi1>; ++ ++ display-timings { ++ native-mode = <&timing1>; ++ timing1: timing@1 { ++ /* 1024 x 768 framebufer, standard VGA timings */ ++ clock-frequency = <65000>; ++ hactive = <1024>; ++ vactive = <768>; ++ hfront-porch = <24>; ++ hback-porch = <160>; ++ hsync-len = <136>; ++ vfront-porch = <3>; ++ vback-porch = <29>; ++ vsync-len = <6>; ++ }; ++ }; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 15>; ++ interrupt-map = <0 0 0 &gic 0 68 4>, ++ <0 0 1 &gic 0 69 4>, ++ <0 0 2 &gic 0 70 4>, ++ <0 0 3 &gic 0 160 4>, ++ <0 0 4 &gic 0 161 4>, ++ <0 0 5 &gic 0 162 4>, ++ <0 0 6 &gic 0 163 4>, ++ <0 0 7 &gic 0 164 4>, ++ <0 0 8 &gic 0 165 4>, ++ <0 0 9 &gic 0 166 4>, ++ <0 0 10 &gic 0 167 4>, ++ <0 0 11 &gic 0 168 4>, ++ <0 0 12 &gic 0 169 4>; ++ ++ motherboard { ++ model = "V2M-Juno"; ++ arm,hbi = <0x252>; ++ arm,vexpress,site = <0>; ++ arm,v2m-memory-map = "rs1"; ++ compatible = "arm,vexpress,v2p-p1", "simple-bus"; ++ #address-cells = <2>; /* SMB chipselect number and offset */ ++ #size-cells = <1>; ++ #interrupt-cells = <1>; ++ ranges; ++ ++ usb@5,00000000 { ++ compatible = "nxp,usb-isp1763"; ++ reg = <5 0x00000000 0x20000>; ++ bus-width = <16>; ++ interrupts = <4>; ++ }; ++ ++ ethernet@2,00000000 { ++ compatible = "smsc,lan9118", "smsc,lan9115"; ++ reg = <2 0x00000000 0x10000>; ++ interrupts = <3>; ++ phy-mode = "mii"; ++ reg-io-width = <4>; ++ smsc,irq-active-high; ++ smsc,irq-push-pull; ++ clocks = <&mb_eth25mhz>; ++ vdd33a-supply = <&soc_fixed_3v3>; /* change this */ ++ vddvario-supply = <&soc_fixed_3v3>; /* and this */ ++ }; ++ ++ iofpga@3,00000000 { ++ compatible = "arm,amba-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 3 0 0x200000>; ++ ++ kmi@060000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x060000 0x1000>; ++ interrupts = <8>; ++ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ kmi@070000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x070000 0x1000>; ++ interrupts = <8>; ++ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ wdt@0f0000 { ++ compatible = "arm,sp805", "arm,primecell"; ++ reg = <0x0f0000 0x10000>; ++ interrupts = <7>; ++ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>; ++ clock-names = "wdogclk", "apb_pclk"; ++ }; ++ ++ v2m_timer01: timer@110000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x110000 0x10000>; ++ interrupts = <9>; ++ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>; ++ clock-names = "timclken1", "apb_pclk"; ++ }; ++ ++ v2m_timer23: timer@120000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x120000 0x10000>; ++ interrupts = <9>; ++ clocks = <&soc_refclk24mhz>, <&soc_smc50mhz>; ++ clock-names = "timclken1", "apb_pclk"; ++ }; ++ ++ rtc@170000 { ++ compatible = "arm,pl031", "arm,primecell"; ++ reg = <0x170000 0x10000>; ++ interrupts = <0>; ++ clocks = <&soc_smc50mhz>; ++ clock-names = "apb_pclk"; ++ }; ++ }; ++ }; ++ }; ++}; +diff -Nur linux-3.14.36/arch/arm64/boot/dts/Makefile linux-openelec/arch/arm64/boot/dts/Makefile +--- linux-3.14.36/arch/arm64/boot/dts/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/boot/dts/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -1,5 +1,7 @@ +-dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb ++dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb \ ++ fvp-base-gicv2-psci.dtb + dtb-$(CONFIG_ARCH_XGENE) += apm-mustang.dtb ++dtb-$(CONFIG_ARCH_VEXPRESS) += juno.dtb + + targets += dtbs + targets += $(dtb-y) +diff -Nur linux-3.14.36/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts linux-openelec/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts +--- linux-3.14.36/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts 2015-05-06 12:05:43.000000000 -0500 +@@ -157,3 +157,5 @@ + /include/ "rtsm_ve-motherboard.dtsi" + }; + }; ++ ++/include/ "clcd-panels.dtsi" +diff -Nur linux-3.14.36/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi linux-openelec/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi +--- linux-3.14.36/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi 2015-05-06 12:05:43.000000000 -0500 +@@ -182,6 +182,9 @@ + interrupts = <14>; + clocks = <&v2m_oscclk1>, <&v2m_clk24mhz>; + clock-names = "clcdclk", "apb_pclk"; ++ mode = "XVGA"; ++ use_dma = <0>; ++ framebuffer = <0x18000000 0x00180000>; + }; + + virtio_block@0130000 { +diff -Nur linux-3.14.36/arch/arm64/crypto/aes-ce-ccm-core.S linux-openelec/arch/arm64/crypto/aes-ce-ccm-core.S +--- linux-3.14.36/arch/arm64/crypto/aes-ce-ccm-core.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/aes-ce-ccm-core.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,222 @@ ++/* ++ * aesce-ccm-core.S - AES-CCM transform for ARMv8 with Crypto Extensions ++ * ++ * Copyright (C) 2013 - 2014 Linaro Ltd ++ * ++ * 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 ++ ++ .text ++ .arch armv8-a+crypto ++ ++ /* ++ * void ce_aes_ccm_auth_data(u8 mac[], u8 const in[], u32 abytes, ++ * u32 *macp, u8 const rk[], u32 rounds); ++ */ ++ENTRY(ce_aes_ccm_auth_data) ++ ldr w8, [x3] /* leftover from prev round? */ ++ ld1 {v0.2d}, [x0] /* load mac */ ++ cbz w8, 1f ++ sub w8, w8, #16 ++ eor v1.16b, v1.16b, v1.16b ++0: ldrb w7, [x1], #1 /* get 1 byte of input */ ++ subs w2, w2, #1 ++ add w8, w8, #1 ++ ins v1.b[0], w7 ++ ext v1.16b, v1.16b, v1.16b, #1 /* rotate in the input bytes */ ++ beq 8f /* out of input? */ ++ cbnz w8, 0b ++ eor v0.16b, v0.16b, v1.16b ++1: ld1 {v3.2d}, [x4] /* load first round key */ ++ prfm pldl1strm, [x1] ++ cmp w5, #12 /* which key size? */ ++ add x6, x4, #16 ++ sub w7, w5, #2 /* modified # of rounds */ ++ bmi 2f ++ bne 5f ++ mov v5.16b, v3.16b ++ b 4f ++2: mov v4.16b, v3.16b ++ ld1 {v5.2d}, [x6], #16 /* load 2nd round key */ ++3: aese v0.16b, v4.16b ++ aesmc v0.16b, v0.16b ++4: ld1 {v3.2d}, [x6], #16 /* load next round key */ ++ aese v0.16b, v5.16b ++ aesmc v0.16b, v0.16b ++5: ld1 {v4.2d}, [x6], #16 /* load next round key */ ++ subs w7, w7, #3 ++ aese v0.16b, v3.16b ++ aesmc v0.16b, v0.16b ++ ld1 {v5.2d}, [x6], #16 /* load next round key */ ++ bpl 3b ++ aese v0.16b, v4.16b ++ subs w2, w2, #16 /* last data? */ ++ eor v0.16b, v0.16b, v5.16b /* final round */ ++ bmi 6f ++ ld1 {v1.16b}, [x1], #16 /* load next input block */ ++ eor v0.16b, v0.16b, v1.16b /* xor with mac */ ++ bne 1b ++6: st1 {v0.2d}, [x0] /* store mac */ ++ beq 10f ++ adds w2, w2, #16 ++ beq 10f ++ mov w8, w2 ++7: ldrb w7, [x1], #1 ++ umov w6, v0.b[0] ++ eor w6, w6, w7 ++ strb w6, [x0], #1 ++ subs w2, w2, #1 ++ beq 10f ++ ext v0.16b, v0.16b, v0.16b, #1 /* rotate out the mac bytes */ ++ b 7b ++8: mov w7, w8 ++ add w8, w8, #16 ++9: ext v1.16b, v1.16b, v1.16b, #1 ++ adds w7, w7, #1 ++ bne 9b ++ eor v0.16b, v0.16b, v1.16b ++ st1 {v0.2d}, [x0] ++10: str w8, [x3] ++ ret ++ENDPROC(ce_aes_ccm_auth_data) ++ ++ /* ++ * void ce_aes_ccm_final(u8 mac[], u8 const ctr[], u8 const rk[], ++ * u32 rounds); ++ */ ++ENTRY(ce_aes_ccm_final) ++ ld1 {v3.2d}, [x2], #16 /* load first round key */ ++ ld1 {v0.2d}, [x0] /* load mac */ ++ cmp w3, #12 /* which key size? */ ++ sub w3, w3, #2 /* modified # of rounds */ ++ ld1 {v1.2d}, [x1] /* load 1st ctriv */ ++ bmi 0f ++ bne 3f ++ mov v5.16b, v3.16b ++ b 2f ++0: mov v4.16b, v3.16b ++1: ld1 {v5.2d}, [x2], #16 /* load next round key */ ++ aese v0.16b, v4.16b ++ aese v1.16b, v4.16b ++ aesmc v0.16b, v0.16b ++ aesmc v1.16b, v1.16b ++2: ld1 {v3.2d}, [x2], #16 /* load next round key */ ++ aese v0.16b, v5.16b ++ aese v1.16b, v5.16b ++ aesmc v0.16b, v0.16b ++ aesmc v1.16b, v1.16b ++3: ld1 {v4.2d}, [x2], #16 /* load next round key */ ++ subs w3, w3, #3 ++ aese v0.16b, v3.16b ++ aese v1.16b, v3.16b ++ aesmc v0.16b, v0.16b ++ aesmc v1.16b, v1.16b ++ bpl 1b ++ aese v0.16b, v4.16b ++ aese v1.16b, v4.16b ++ /* final round key cancels out */ ++ eor v0.16b, v0.16b, v1.16b /* en-/decrypt the mac */ ++ st1 {v0.2d}, [x0] /* store result */ ++ ret ++ENDPROC(ce_aes_ccm_final) ++ ++ .macro aes_ccm_do_crypt,enc ++ ldr x8, [x6, #8] /* load lower ctr */ ++ ld1 {v0.2d}, [x5] /* load mac */ ++ rev x8, x8 /* keep swabbed ctr in reg */ ++0: /* outer loop */ ++ ld1 {v1.1d}, [x6] /* load upper ctr */ ++ prfm pldl1strm, [x1] ++ add x8, x8, #1 ++ rev x9, x8 ++ cmp w4, #12 /* which key size? */ ++ sub w7, w4, #2 /* get modified # of rounds */ ++ ins v1.d[1], x9 /* no carry in lower ctr */ ++ ld1 {v3.2d}, [x3] /* load first round key */ ++ add x10, x3, #16 ++ bmi 1f ++ bne 4f ++ mov v5.16b, v3.16b ++ b 3f ++1: mov v4.16b, v3.16b ++ ld1 {v5.2d}, [x10], #16 /* load 2nd round key */ ++2: /* inner loop: 3 rounds, 2x interleaved */ ++ aese v0.16b, v4.16b ++ aese v1.16b, v4.16b ++ aesmc v0.16b, v0.16b ++ aesmc v1.16b, v1.16b ++3: ld1 {v3.2d}, [x10], #16 /* load next round key */ ++ aese v0.16b, v5.16b ++ aese v1.16b, v5.16b ++ aesmc v0.16b, v0.16b ++ aesmc v1.16b, v1.16b ++4: ld1 {v4.2d}, [x10], #16 /* load next round key */ ++ subs w7, w7, #3 ++ aese v0.16b, v3.16b ++ aese v1.16b, v3.16b ++ aesmc v0.16b, v0.16b ++ aesmc v1.16b, v1.16b ++ ld1 {v5.2d}, [x10], #16 /* load next round key */ ++ bpl 2b ++ aese v0.16b, v4.16b ++ aese v1.16b, v4.16b ++ subs w2, w2, #16 ++ bmi 6f /* partial block? */ ++ ld1 {v2.16b}, [x1], #16 /* load next input block */ ++ .if \enc == 1 ++ eor v2.16b, v2.16b, v5.16b /* final round enc+mac */ ++ eor v1.16b, v1.16b, v2.16b /* xor with crypted ctr */ ++ .else ++ eor v2.16b, v2.16b, v1.16b /* xor with crypted ctr */ ++ eor v1.16b, v2.16b, v5.16b /* final round enc */ ++ .endif ++ eor v0.16b, v0.16b, v2.16b /* xor mac with pt ^ rk[last] */ ++ st1 {v1.16b}, [x0], #16 /* write output block */ ++ bne 0b ++ rev x8, x8 ++ st1 {v0.2d}, [x5] /* store mac */ ++ str x8, [x6, #8] /* store lsb end of ctr (BE) */ ++5: ret ++ ++6: eor v0.16b, v0.16b, v5.16b /* final round mac */ ++ eor v1.16b, v1.16b, v5.16b /* final round enc */ ++ st1 {v0.2d}, [x5] /* store mac */ ++ add w2, w2, #16 /* process partial tail block */ ++7: ldrb w9, [x1], #1 /* get 1 byte of input */ ++ umov w6, v1.b[0] /* get top crypted ctr byte */ ++ umov w7, v0.b[0] /* get top mac byte */ ++ .if \enc == 1 ++ eor w7, w7, w9 ++ eor w9, w9, w6 ++ .else ++ eor w9, w9, w6 ++ eor w7, w7, w9 ++ .endif ++ strb w9, [x0], #1 /* store out byte */ ++ strb w7, [x5], #1 /* store mac byte */ ++ subs w2, w2, #1 ++ beq 5b ++ ext v0.16b, v0.16b, v0.16b, #1 /* shift out mac byte */ ++ ext v1.16b, v1.16b, v1.16b, #1 /* shift out ctr byte */ ++ b 7b ++ .endm ++ ++ /* ++ * void ce_aes_ccm_encrypt(u8 out[], u8 const in[], u32 cbytes, ++ * u8 const rk[], u32 rounds, u8 mac[], ++ * u8 ctr[]); ++ * void ce_aes_ccm_decrypt(u8 out[], u8 const in[], u32 cbytes, ++ * u8 const rk[], u32 rounds, u8 mac[], ++ * u8 ctr[]); ++ */ ++ENTRY(ce_aes_ccm_encrypt) ++ aes_ccm_do_crypt 1 ++ENDPROC(ce_aes_ccm_encrypt) ++ ++ENTRY(ce_aes_ccm_decrypt) ++ aes_ccm_do_crypt 0 ++ENDPROC(ce_aes_ccm_decrypt) +diff -Nur linux-3.14.36/arch/arm64/crypto/aes-ce-ccm-glue.c linux-openelec/arch/arm64/crypto/aes-ce-ccm-glue.c +--- linux-3.14.36/arch/arm64/crypto/aes-ce-ccm-glue.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/aes-ce-ccm-glue.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,297 @@ ++/* ++ * aes-ccm-glue.c - AES-CCM transform for ARMv8 with Crypto Extensions ++ * ++ * Copyright (C) 2013 - 2014 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int num_rounds(struct crypto_aes_ctx *ctx) ++{ ++ /* ++ * # of rounds specified by AES: ++ * 128 bit key 10 rounds ++ * 192 bit key 12 rounds ++ * 256 bit key 14 rounds ++ * => n byte key => 6 + (n/4) rounds ++ */ ++ return 6 + ctx->key_length / 4; ++} ++ ++asmlinkage void ce_aes_ccm_auth_data(u8 mac[], u8 const in[], u32 abytes, ++ u32 *macp, u32 const rk[], u32 rounds); ++ ++asmlinkage void ce_aes_ccm_encrypt(u8 out[], u8 const in[], u32 cbytes, ++ u32 const rk[], u32 rounds, u8 mac[], ++ u8 ctr[]); ++ ++asmlinkage void ce_aes_ccm_decrypt(u8 out[], u8 const in[], u32 cbytes, ++ u32 const rk[], u32 rounds, u8 mac[], ++ u8 ctr[]); ++ ++asmlinkage void ce_aes_ccm_final(u8 mac[], u8 const ctr[], u32 const rk[], ++ u32 rounds); ++ ++static int ccm_setkey(struct crypto_aead *tfm, const u8 *in_key, ++ unsigned int key_len) ++{ ++ struct crypto_aes_ctx *ctx = crypto_aead_ctx(tfm); ++ int ret; ++ ++ ret = crypto_aes_expand_key(ctx, in_key, key_len); ++ if (!ret) ++ return 0; ++ ++ tfm->base.crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; ++ return -EINVAL; ++} ++ ++static int ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) ++{ ++ if ((authsize & 1) || authsize < 4) ++ return -EINVAL; ++ return 0; ++} ++ ++static int ccm_init_mac(struct aead_request *req, u8 maciv[], u32 msglen) ++{ ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ __be32 *n = (__be32 *)&maciv[AES_BLOCK_SIZE - 8]; ++ u32 l = req->iv[0] + 1; ++ ++ /* verify that CCM dimension 'L' is set correctly in the IV */ ++ if (l < 2 || l > 8) ++ return -EINVAL; ++ ++ /* verify that msglen can in fact be represented in L bytes */ ++ if (l < 4 && msglen >> (8 * l)) ++ return -EOVERFLOW; ++ ++ /* ++ * Even if the CCM spec allows L values of up to 8, the Linux cryptoapi ++ * uses a u32 type to represent msglen so the top 4 bytes are always 0. ++ */ ++ n[0] = 0; ++ n[1] = cpu_to_be32(msglen); ++ ++ memcpy(maciv, req->iv, AES_BLOCK_SIZE - l); ++ ++ /* ++ * Meaning of byte 0 according to CCM spec (RFC 3610/NIST 800-38C) ++ * - bits 0..2 : max # of bytes required to represent msglen, minus 1 ++ * (already set by caller) ++ * - bits 3..5 : size of auth tag (1 => 4 bytes, 2 => 6 bytes, etc) ++ * - bit 6 : indicates presence of authenticate-only data ++ */ ++ maciv[0] |= (crypto_aead_authsize(aead) - 2) << 2; ++ if (req->assoclen) ++ maciv[0] |= 0x40; ++ ++ memset(&req->iv[AES_BLOCK_SIZE - l], 0, l); ++ return 0; ++} ++ ++static void ccm_calculate_auth_mac(struct aead_request *req, u8 mac[]) ++{ ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ struct crypto_aes_ctx *ctx = crypto_aead_ctx(aead); ++ struct __packed { __be16 l; __be32 h; u16 len; } ltag; ++ struct scatter_walk walk; ++ u32 len = req->assoclen; ++ u32 macp = 0; ++ ++ /* prepend the AAD with a length tag */ ++ if (len < 0xff00) { ++ ltag.l = cpu_to_be16(len); ++ ltag.len = 2; ++ } else { ++ ltag.l = cpu_to_be16(0xfffe); ++ put_unaligned_be32(len, <ag.h); ++ ltag.len = 6; ++ } ++ ++ ce_aes_ccm_auth_data(mac, (u8 *)<ag, ltag.len, &macp, ctx->key_enc, ++ num_rounds(ctx)); ++ scatterwalk_start(&walk, req->assoc); ++ ++ do { ++ u32 n = scatterwalk_clamp(&walk, len); ++ u8 *p; ++ ++ if (!n) { ++ scatterwalk_start(&walk, sg_next(walk.sg)); ++ n = scatterwalk_clamp(&walk, len); ++ } ++ p = scatterwalk_map(&walk); ++ ce_aes_ccm_auth_data(mac, p, n, &macp, ctx->key_enc, ++ num_rounds(ctx)); ++ len -= n; ++ ++ scatterwalk_unmap(p); ++ scatterwalk_advance(&walk, n); ++ scatterwalk_done(&walk, 0, len); ++ } while (len); ++} ++ ++static int ccm_encrypt(struct aead_request *req) ++{ ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ struct crypto_aes_ctx *ctx = crypto_aead_ctx(aead); ++ struct blkcipher_desc desc = { .info = req->iv }; ++ struct blkcipher_walk walk; ++ u8 __aligned(8) mac[AES_BLOCK_SIZE]; ++ u8 buf[AES_BLOCK_SIZE]; ++ u32 len = req->cryptlen; ++ int err; ++ ++ err = ccm_init_mac(req, mac, len); ++ if (err) ++ return err; ++ ++ kernel_neon_begin_partial(6); ++ ++ if (req->assoclen) ++ ccm_calculate_auth_mac(req, mac); ++ ++ /* preserve the original iv for the final round */ ++ memcpy(buf, req->iv, AES_BLOCK_SIZE); ++ ++ blkcipher_walk_init(&walk, req->dst, req->src, len); ++ err = blkcipher_aead_walk_virt_block(&desc, &walk, aead, ++ AES_BLOCK_SIZE); ++ ++ while (walk.nbytes) { ++ u32 tail = walk.nbytes % AES_BLOCK_SIZE; ++ ++ if (walk.nbytes == len) ++ tail = 0; ++ ++ ce_aes_ccm_encrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ walk.nbytes - tail, ctx->key_enc, ++ num_rounds(ctx), mac, walk.iv); ++ ++ len -= walk.nbytes - tail; ++ err = blkcipher_walk_done(&desc, &walk, tail); ++ } ++ if (!err) ++ ce_aes_ccm_final(mac, buf, ctx->key_enc, num_rounds(ctx)); ++ ++ kernel_neon_end(); ++ ++ if (err) ++ return err; ++ ++ /* copy authtag to end of dst */ ++ scatterwalk_map_and_copy(mac, req->dst, req->cryptlen, ++ crypto_aead_authsize(aead), 1); ++ ++ return 0; ++} ++ ++static int ccm_decrypt(struct aead_request *req) ++{ ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ struct crypto_aes_ctx *ctx = crypto_aead_ctx(aead); ++ unsigned int authsize = crypto_aead_authsize(aead); ++ struct blkcipher_desc desc = { .info = req->iv }; ++ struct blkcipher_walk walk; ++ u8 __aligned(8) mac[AES_BLOCK_SIZE]; ++ u8 buf[AES_BLOCK_SIZE]; ++ u32 len = req->cryptlen - authsize; ++ int err; ++ ++ err = ccm_init_mac(req, mac, len); ++ if (err) ++ return err; ++ ++ kernel_neon_begin_partial(6); ++ ++ if (req->assoclen) ++ ccm_calculate_auth_mac(req, mac); ++ ++ /* preserve the original iv for the final round */ ++ memcpy(buf, req->iv, AES_BLOCK_SIZE); ++ ++ blkcipher_walk_init(&walk, req->dst, req->src, len); ++ err = blkcipher_aead_walk_virt_block(&desc, &walk, aead, ++ AES_BLOCK_SIZE); ++ ++ while (walk.nbytes) { ++ u32 tail = walk.nbytes % AES_BLOCK_SIZE; ++ ++ if (walk.nbytes == len) ++ tail = 0; ++ ++ ce_aes_ccm_decrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ walk.nbytes - tail, ctx->key_enc, ++ num_rounds(ctx), mac, walk.iv); ++ ++ len -= walk.nbytes - tail; ++ err = blkcipher_walk_done(&desc, &walk, tail); ++ } ++ if (!err) ++ ce_aes_ccm_final(mac, buf, ctx->key_enc, num_rounds(ctx)); ++ ++ kernel_neon_end(); ++ ++ if (err) ++ return err; ++ ++ /* compare calculated auth tag with the stored one */ ++ scatterwalk_map_and_copy(buf, req->src, req->cryptlen - authsize, ++ authsize, 0); ++ ++ if (memcmp(mac, buf, authsize)) ++ return -EBADMSG; ++ return 0; ++} ++ ++static struct crypto_alg ccm_aes_alg = { ++ .cra_name = "ccm(aes)", ++ .cra_driver_name = "ccm-aes-ce", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_TYPE_AEAD, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct crypto_aes_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_aead_type, ++ .cra_module = THIS_MODULE, ++ .cra_aead = { ++ .ivsize = AES_BLOCK_SIZE, ++ .maxauthsize = AES_BLOCK_SIZE, ++ .setkey = ccm_setkey, ++ .setauthsize = ccm_setauthsize, ++ .encrypt = ccm_encrypt, ++ .decrypt = ccm_decrypt, ++ } ++}; ++ ++static int __init aes_mod_init(void) ++{ ++ if (!(elf_hwcap & HWCAP_AES)) ++ return -ENODEV; ++ return crypto_register_alg(&ccm_aes_alg); ++} ++ ++static void __exit aes_mod_exit(void) ++{ ++ crypto_unregister_alg(&ccm_aes_alg); ++} ++ ++module_init(aes_mod_init); ++module_exit(aes_mod_exit); ++ ++MODULE_DESCRIPTION("Synchronous AES in CCM mode using ARMv8 Crypto Extensions"); ++MODULE_AUTHOR("Ard Biesheuvel "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("ccm(aes)"); +diff -Nur linux-3.14.36/arch/arm64/crypto/aes-ce-cipher.c linux-openelec/arch/arm64/crypto/aes-ce-cipher.c +--- linux-3.14.36/arch/arm64/crypto/aes-ce-cipher.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/aes-ce-cipher.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,155 @@ ++/* ++ * aes-ce-cipher.c - core AES cipher using ARMv8 Crypto Extensions ++ * ++ * Copyright (C) 2013 - 2014 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions"); ++MODULE_AUTHOR("Ard Biesheuvel "); ++MODULE_LICENSE("GPL v2"); ++ ++struct aes_block { ++ u8 b[AES_BLOCK_SIZE]; ++}; ++ ++static int num_rounds(struct crypto_aes_ctx *ctx) ++{ ++ /* ++ * # of rounds specified by AES: ++ * 128 bit key 10 rounds ++ * 192 bit key 12 rounds ++ * 256 bit key 14 rounds ++ * => n byte key => 6 + (n/4) rounds ++ */ ++ return 6 + ctx->key_length / 4; ++} ++ ++static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) ++{ ++ struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct aes_block *out = (struct aes_block *)dst; ++ struct aes_block const *in = (struct aes_block *)src; ++ void *dummy0; ++ int dummy1; ++ ++ kernel_neon_begin_partial(4); ++ ++ __asm__(" ld1 {v0.16b}, %[in] ;" ++ " ld1 {v1.2d}, [%[key]], #16 ;" ++ " cmp %w[rounds], #10 ;" ++ " bmi 0f ;" ++ " bne 3f ;" ++ " mov v3.16b, v1.16b ;" ++ " b 2f ;" ++ "0: mov v2.16b, v1.16b ;" ++ " ld1 {v3.2d}, [%[key]], #16 ;" ++ "1: aese v0.16b, v2.16b ;" ++ " aesmc v0.16b, v0.16b ;" ++ "2: ld1 {v1.2d}, [%[key]], #16 ;" ++ " aese v0.16b, v3.16b ;" ++ " aesmc v0.16b, v0.16b ;" ++ "3: ld1 {v2.2d}, [%[key]], #16 ;" ++ " subs %w[rounds], %w[rounds], #3 ;" ++ " aese v0.16b, v1.16b ;" ++ " aesmc v0.16b, v0.16b ;" ++ " ld1 {v3.2d}, [%[key]], #16 ;" ++ " bpl 1b ;" ++ " aese v0.16b, v2.16b ;" ++ " eor v0.16b, v0.16b, v3.16b ;" ++ " st1 {v0.16b}, %[out] ;" ++ ++ : [out] "=Q"(*out), ++ [key] "=r"(dummy0), ++ [rounds] "=r"(dummy1) ++ : [in] "Q"(*in), ++ "1"(ctx->key_enc), ++ "2"(num_rounds(ctx) - 2) ++ : "cc"); ++ ++ kernel_neon_end(); ++} ++ ++static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) ++{ ++ struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct aes_block *out = (struct aes_block *)dst; ++ struct aes_block const *in = (struct aes_block *)src; ++ void *dummy0; ++ int dummy1; ++ ++ kernel_neon_begin_partial(4); ++ ++ __asm__(" ld1 {v0.16b}, %[in] ;" ++ " ld1 {v1.2d}, [%[key]], #16 ;" ++ " cmp %w[rounds], #10 ;" ++ " bmi 0f ;" ++ " bne 3f ;" ++ " mov v3.16b, v1.16b ;" ++ " b 2f ;" ++ "0: mov v2.16b, v1.16b ;" ++ " ld1 {v3.2d}, [%[key]], #16 ;" ++ "1: aesd v0.16b, v2.16b ;" ++ " aesimc v0.16b, v0.16b ;" ++ "2: ld1 {v1.2d}, [%[key]], #16 ;" ++ " aesd v0.16b, v3.16b ;" ++ " aesimc v0.16b, v0.16b ;" ++ "3: ld1 {v2.2d}, [%[key]], #16 ;" ++ " subs %w[rounds], %w[rounds], #3 ;" ++ " aesd v0.16b, v1.16b ;" ++ " aesimc v0.16b, v0.16b ;" ++ " ld1 {v3.2d}, [%[key]], #16 ;" ++ " bpl 1b ;" ++ " aesd v0.16b, v2.16b ;" ++ " eor v0.16b, v0.16b, v3.16b ;" ++ " st1 {v0.16b}, %[out] ;" ++ ++ : [out] "=Q"(*out), ++ [key] "=r"(dummy0), ++ [rounds] "=r"(dummy1) ++ : [in] "Q"(*in), ++ "1"(ctx->key_dec), ++ "2"(num_rounds(ctx) - 2) ++ : "cc"); ++ ++ kernel_neon_end(); ++} ++ ++static struct crypto_alg aes_alg = { ++ .cra_name = "aes", ++ .cra_driver_name = "aes-ce", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_TYPE_CIPHER, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct crypto_aes_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_cipher = { ++ .cia_min_keysize = AES_MIN_KEY_SIZE, ++ .cia_max_keysize = AES_MAX_KEY_SIZE, ++ .cia_setkey = crypto_aes_set_key, ++ .cia_encrypt = aes_cipher_encrypt, ++ .cia_decrypt = aes_cipher_decrypt ++ } ++}; ++ ++static int __init aes_mod_init(void) ++{ ++ return crypto_register_alg(&aes_alg); ++} ++ ++static void __exit aes_mod_exit(void) ++{ ++ crypto_unregister_alg(&aes_alg); ++} ++ ++module_cpu_feature_match(AES, aes_mod_init); ++module_exit(aes_mod_exit); +diff -Nur linux-3.14.36/arch/arm64/crypto/aes-ce.S linux-openelec/arch/arm64/crypto/aes-ce.S +--- linux-3.14.36/arch/arm64/crypto/aes-ce.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/aes-ce.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,133 @@ ++/* ++ * linux/arch/arm64/crypto/aes-ce.S - AES cipher for ARMv8 with ++ * Crypto Extensions ++ * ++ * Copyright (C) 2013 Linaro Ltd ++ * ++ * 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 ++ ++#define AES_ENTRY(func) ENTRY(ce_ ## func) ++#define AES_ENDPROC(func) ENDPROC(ce_ ## func) ++ ++ .arch armv8-a+crypto ++ ++ /* preload all round keys */ ++ .macro load_round_keys, rounds, rk ++ cmp \rounds, #12 ++ blo 2222f /* 128 bits */ ++ beq 1111f /* 192 bits */ ++ ld1 {v17.16b-v18.16b}, [\rk], #32 ++1111: ld1 {v19.16b-v20.16b}, [\rk], #32 ++2222: ld1 {v21.16b-v24.16b}, [\rk], #64 ++ ld1 {v25.16b-v28.16b}, [\rk], #64 ++ ld1 {v29.16b-v31.16b}, [\rk] ++ .endm ++ ++ /* prepare for encryption with key in rk[] */ ++ .macro enc_prepare, rounds, rk, ignore ++ load_round_keys \rounds, \rk ++ .endm ++ ++ /* prepare for encryption (again) but with new key in rk[] */ ++ .macro enc_switch_key, rounds, rk, ignore ++ load_round_keys \rounds, \rk ++ .endm ++ ++ /* prepare for decryption with key in rk[] */ ++ .macro dec_prepare, rounds, rk, ignore ++ load_round_keys \rounds, \rk ++ .endm ++ ++ .macro do_enc_Nx, de, mc, k, i0, i1, i2, i3 ++ aes\de \i0\().16b, \k\().16b ++ .ifnb \i1 ++ aes\de \i1\().16b, \k\().16b ++ .ifnb \i3 ++ aes\de \i2\().16b, \k\().16b ++ aes\de \i3\().16b, \k\().16b ++ .endif ++ .endif ++ aes\mc \i0\().16b, \i0\().16b ++ .ifnb \i1 ++ aes\mc \i1\().16b, \i1\().16b ++ .ifnb \i3 ++ aes\mc \i2\().16b, \i2\().16b ++ aes\mc \i3\().16b, \i3\().16b ++ .endif ++ .endif ++ .endm ++ ++ /* up to 4 interleaved encryption rounds with the same round key */ ++ .macro round_Nx, enc, k, i0, i1, i2, i3 ++ .ifc \enc, e ++ do_enc_Nx e, mc, \k, \i0, \i1, \i2, \i3 ++ .else ++ do_enc_Nx d, imc, \k, \i0, \i1, \i2, \i3 ++ .endif ++ .endm ++ ++ /* up to 4 interleaved final rounds */ ++ .macro fin_round_Nx, de, k, k2, i0, i1, i2, i3 ++ aes\de \i0\().16b, \k\().16b ++ .ifnb \i1 ++ aes\de \i1\().16b, \k\().16b ++ .ifnb \i3 ++ aes\de \i2\().16b, \k\().16b ++ aes\de \i3\().16b, \k\().16b ++ .endif ++ .endif ++ eor \i0\().16b, \i0\().16b, \k2\().16b ++ .ifnb \i1 ++ eor \i1\().16b, \i1\().16b, \k2\().16b ++ .ifnb \i3 ++ eor \i2\().16b, \i2\().16b, \k2\().16b ++ eor \i3\().16b, \i3\().16b, \k2\().16b ++ .endif ++ .endif ++ .endm ++ ++ /* up to 4 interleaved blocks */ ++ .macro do_block_Nx, enc, rounds, i0, i1, i2, i3 ++ cmp \rounds, #12 ++ blo 2222f /* 128 bits */ ++ beq 1111f /* 192 bits */ ++ round_Nx \enc, v17, \i0, \i1, \i2, \i3 ++ round_Nx \enc, v18, \i0, \i1, \i2, \i3 ++1111: round_Nx \enc, v19, \i0, \i1, \i2, \i3 ++ round_Nx \enc, v20, \i0, \i1, \i2, \i3 ++2222: .irp key, v21, v22, v23, v24, v25, v26, v27, v28, v29 ++ round_Nx \enc, \key, \i0, \i1, \i2, \i3 ++ .endr ++ fin_round_Nx \enc, v30, v31, \i0, \i1, \i2, \i3 ++ .endm ++ ++ .macro encrypt_block, in, rounds, t0, t1, t2 ++ do_block_Nx e, \rounds, \in ++ .endm ++ ++ .macro encrypt_block2x, i0, i1, rounds, t0, t1, t2 ++ do_block_Nx e, \rounds, \i0, \i1 ++ .endm ++ ++ .macro encrypt_block4x, i0, i1, i2, i3, rounds, t0, t1, t2 ++ do_block_Nx e, \rounds, \i0, \i1, \i2, \i3 ++ .endm ++ ++ .macro decrypt_block, in, rounds, t0, t1, t2 ++ do_block_Nx d, \rounds, \in ++ .endm ++ ++ .macro decrypt_block2x, i0, i1, rounds, t0, t1, t2 ++ do_block_Nx d, \rounds, \i0, \i1 ++ .endm ++ ++ .macro decrypt_block4x, i0, i1, i2, i3, rounds, t0, t1, t2 ++ do_block_Nx d, \rounds, \i0, \i1, \i2, \i3 ++ .endm ++ ++#include "aes-modes.S" +diff -Nur linux-3.14.36/arch/arm64/crypto/aes-glue.c linux-openelec/arch/arm64/crypto/aes-glue.c +--- linux-3.14.36/arch/arm64/crypto/aes-glue.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/aes-glue.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,446 @@ ++/* ++ * linux/arch/arm64/crypto/aes-glue.c - wrapper code for ARMv8 AES ++ * ++ * Copyright (C) 2013 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef USE_V8_CRYPTO_EXTENSIONS ++#define MODE "ce" ++#define PRIO 300 ++#define aes_ecb_encrypt ce_aes_ecb_encrypt ++#define aes_ecb_decrypt ce_aes_ecb_decrypt ++#define aes_cbc_encrypt ce_aes_cbc_encrypt ++#define aes_cbc_decrypt ce_aes_cbc_decrypt ++#define aes_ctr_encrypt ce_aes_ctr_encrypt ++#define aes_xts_encrypt ce_aes_xts_encrypt ++#define aes_xts_decrypt ce_aes_xts_decrypt ++MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 Crypto Extensions"); ++#else ++#define MODE "neon" ++#define PRIO 200 ++#define aes_ecb_encrypt neon_aes_ecb_encrypt ++#define aes_ecb_decrypt neon_aes_ecb_decrypt ++#define aes_cbc_encrypt neon_aes_cbc_encrypt ++#define aes_cbc_decrypt neon_aes_cbc_decrypt ++#define aes_ctr_encrypt neon_aes_ctr_encrypt ++#define aes_xts_encrypt neon_aes_xts_encrypt ++#define aes_xts_decrypt neon_aes_xts_decrypt ++MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 NEON"); ++MODULE_ALIAS("ecb(aes)"); ++MODULE_ALIAS("cbc(aes)"); ++MODULE_ALIAS("ctr(aes)"); ++MODULE_ALIAS("xts(aes)"); ++#endif ++ ++MODULE_AUTHOR("Ard Biesheuvel "); ++MODULE_LICENSE("GPL v2"); ++ ++/* defined in aes-modes.S */ ++asmlinkage void aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], ++ int rounds, int blocks, int first); ++asmlinkage void aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], ++ int rounds, int blocks, int first); ++ ++asmlinkage void aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], ++ int rounds, int blocks, u8 iv[], int first); ++asmlinkage void aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], ++ int rounds, int blocks, u8 iv[], int first); ++ ++asmlinkage void aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], ++ int rounds, int blocks, u8 ctr[], int first); ++ ++asmlinkage void aes_xts_encrypt(u8 out[], u8 const in[], u8 const rk1[], ++ int rounds, int blocks, u8 const rk2[], u8 iv[], ++ int first); ++asmlinkage void aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], ++ int rounds, int blocks, u8 const rk2[], u8 iv[], ++ int first); ++ ++struct crypto_aes_xts_ctx { ++ struct crypto_aes_ctx key1; ++ struct crypto_aes_ctx __aligned(8) key2; ++}; ++ ++static int xts_set_key(struct crypto_tfm *tfm, const u8 *in_key, ++ unsigned int key_len) ++{ ++ struct crypto_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm); ++ int ret; ++ ++ ret = crypto_aes_expand_key(&ctx->key1, in_key, key_len / 2); ++ if (!ret) ++ ret = crypto_aes_expand_key(&ctx->key2, &in_key[key_len / 2], ++ key_len / 2); ++ if (!ret) ++ return 0; ++ ++ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; ++ return -EINVAL; ++} ++ ++static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); ++ int err, first, rounds = 6 + ctx->key_length / 4; ++ struct blkcipher_walk walk; ++ unsigned int blocks; ++ ++ desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ err = blkcipher_walk_virt(desc, &walk); ++ ++ kernel_neon_begin(); ++ for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { ++ aes_ecb_encrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ (u8 *)ctx->key_enc, rounds, blocks, first); ++ err = blkcipher_walk_done(desc, &walk, 0); ++ } ++ kernel_neon_end(); ++ return err; ++} ++ ++static int ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); ++ int err, first, rounds = 6 + ctx->key_length / 4; ++ struct blkcipher_walk walk; ++ unsigned int blocks; ++ ++ desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ err = blkcipher_walk_virt(desc, &walk); ++ ++ kernel_neon_begin(); ++ for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { ++ aes_ecb_decrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ (u8 *)ctx->key_dec, rounds, blocks, first); ++ err = blkcipher_walk_done(desc, &walk, 0); ++ } ++ kernel_neon_end(); ++ return err; ++} ++ ++static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); ++ int err, first, rounds = 6 + ctx->key_length / 4; ++ struct blkcipher_walk walk; ++ unsigned int blocks; ++ ++ desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ err = blkcipher_walk_virt(desc, &walk); ++ ++ kernel_neon_begin(); ++ for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { ++ aes_cbc_encrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ (u8 *)ctx->key_enc, rounds, blocks, walk.iv, ++ first); ++ err = blkcipher_walk_done(desc, &walk, 0); ++ } ++ kernel_neon_end(); ++ return err; ++} ++ ++static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); ++ int err, first, rounds = 6 + ctx->key_length / 4; ++ struct blkcipher_walk walk; ++ unsigned int blocks; ++ ++ desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ err = blkcipher_walk_virt(desc, &walk); ++ ++ kernel_neon_begin(); ++ for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { ++ aes_cbc_decrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ (u8 *)ctx->key_dec, rounds, blocks, walk.iv, ++ first); ++ err = blkcipher_walk_done(desc, &walk, 0); ++ } ++ kernel_neon_end(); ++ return err; ++} ++ ++static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); ++ int err, first, rounds = 6 + ctx->key_length / 4; ++ struct blkcipher_walk walk; ++ int blocks; ++ ++ desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ err = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); ++ ++ first = 1; ++ kernel_neon_begin(); ++ while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { ++ aes_ctr_encrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ (u8 *)ctx->key_enc, rounds, blocks, walk.iv, ++ first); ++ first = 0; ++ nbytes -= blocks * AES_BLOCK_SIZE; ++ if (nbytes && nbytes == walk.nbytes % AES_BLOCK_SIZE) ++ break; ++ err = blkcipher_walk_done(desc, &walk, ++ walk.nbytes % AES_BLOCK_SIZE); ++ } ++ if (nbytes) { ++ u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE; ++ u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE; ++ u8 __aligned(8) tail[AES_BLOCK_SIZE]; ++ ++ /* ++ * Minimum alignment is 8 bytes, so if nbytes is <= 8, we need ++ * to tell aes_ctr_encrypt() to only read half a block. ++ */ ++ blocks = (nbytes <= 8) ? -1 : 1; ++ ++ aes_ctr_encrypt(tail, tsrc, (u8 *)ctx->key_enc, rounds, ++ blocks, walk.iv, first); ++ memcpy(tdst, tail, nbytes); ++ err = blkcipher_walk_done(desc, &walk, 0); ++ } ++ kernel_neon_end(); ++ ++ return err; ++} ++ ++static int xts_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); ++ int err, first, rounds = 6 + ctx->key1.key_length / 4; ++ struct blkcipher_walk walk; ++ unsigned int blocks; ++ ++ desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ err = blkcipher_walk_virt(desc, &walk); ++ ++ kernel_neon_begin(); ++ for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { ++ aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ (u8 *)ctx->key1.key_enc, rounds, blocks, ++ (u8 *)ctx->key2.key_enc, walk.iv, first); ++ err = blkcipher_walk_done(desc, &walk, 0); ++ } ++ kernel_neon_end(); ++ ++ return err; ++} ++ ++static int xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, ++ struct scatterlist *src, unsigned int nbytes) ++{ ++ struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); ++ int err, first, rounds = 6 + ctx->key1.key_length / 4; ++ struct blkcipher_walk walk; ++ unsigned int blocks; ++ ++ desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ blkcipher_walk_init(&walk, dst, src, nbytes); ++ err = blkcipher_walk_virt(desc, &walk); ++ ++ kernel_neon_begin(); ++ for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { ++ aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr, ++ (u8 *)ctx->key1.key_dec, rounds, blocks, ++ (u8 *)ctx->key2.key_enc, walk.iv, first); ++ err = blkcipher_walk_done(desc, &walk, 0); ++ } ++ kernel_neon_end(); ++ ++ return err; ++} ++ ++static struct crypto_alg aes_algs[] = { { ++ .cra_name = "__ecb-aes-" MODE, ++ .cra_driver_name = "__driver-ecb-aes-" MODE, ++ .cra_priority = 0, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct crypto_aes_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_blkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = crypto_aes_set_key, ++ .encrypt = ecb_encrypt, ++ .decrypt = ecb_decrypt, ++ }, ++}, { ++ .cra_name = "__cbc-aes-" MODE, ++ .cra_driver_name = "__driver-cbc-aes-" MODE, ++ .cra_priority = 0, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct crypto_aes_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_blkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = crypto_aes_set_key, ++ .encrypt = cbc_encrypt, ++ .decrypt = cbc_decrypt, ++ }, ++}, { ++ .cra_name = "__ctr-aes-" MODE, ++ .cra_driver_name = "__driver-ctr-aes-" MODE, ++ .cra_priority = 0, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct crypto_aes_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_blkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = crypto_aes_set_key, ++ .encrypt = ctr_encrypt, ++ .decrypt = ctr_encrypt, ++ }, ++}, { ++ .cra_name = "__xts-aes-" MODE, ++ .cra_driver_name = "__driver-xts-aes-" MODE, ++ .cra_priority = 0, ++ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct crypto_aes_xts_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_blkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_blkcipher = { ++ .min_keysize = 2 * AES_MIN_KEY_SIZE, ++ .max_keysize = 2 * AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = xts_set_key, ++ .encrypt = xts_encrypt, ++ .decrypt = xts_decrypt, ++ }, ++}, { ++ .cra_name = "ecb(aes)", ++ .cra_driver_name = "ecb-aes-" MODE, ++ .cra_priority = PRIO, ++ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct async_helper_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_ablkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_init = ablk_init, ++ .cra_exit = ablk_exit, ++ .cra_ablkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = ablk_set_key, ++ .encrypt = ablk_encrypt, ++ .decrypt = ablk_decrypt, ++ } ++}, { ++ .cra_name = "cbc(aes)", ++ .cra_driver_name = "cbc-aes-" MODE, ++ .cra_priority = PRIO, ++ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct async_helper_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_ablkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_init = ablk_init, ++ .cra_exit = ablk_exit, ++ .cra_ablkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = ablk_set_key, ++ .encrypt = ablk_encrypt, ++ .decrypt = ablk_decrypt, ++ } ++}, { ++ .cra_name = "ctr(aes)", ++ .cra_driver_name = "ctr-aes-" MODE, ++ .cra_priority = PRIO, ++ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct async_helper_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_ablkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_init = ablk_init, ++ .cra_exit = ablk_exit, ++ .cra_ablkcipher = { ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = ablk_set_key, ++ .encrypt = ablk_encrypt, ++ .decrypt = ablk_decrypt, ++ } ++}, { ++ .cra_name = "xts(aes)", ++ .cra_driver_name = "xts-aes-" MODE, ++ .cra_priority = PRIO, ++ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct async_helper_ctx), ++ .cra_alignmask = 7, ++ .cra_type = &crypto_ablkcipher_type, ++ .cra_module = THIS_MODULE, ++ .cra_init = ablk_init, ++ .cra_exit = ablk_exit, ++ .cra_ablkcipher = { ++ .min_keysize = 2 * AES_MIN_KEY_SIZE, ++ .max_keysize = 2 * AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .setkey = ablk_set_key, ++ .encrypt = ablk_encrypt, ++ .decrypt = ablk_decrypt, ++ } ++} }; ++ ++static int __init aes_init(void) ++{ ++ return crypto_register_algs(aes_algs, ARRAY_SIZE(aes_algs)); ++} ++ ++static void __exit aes_exit(void) ++{ ++ crypto_unregister_algs(aes_algs, ARRAY_SIZE(aes_algs)); ++} ++ ++#ifdef USE_V8_CRYPTO_EXTENSIONS ++module_cpu_feature_match(AES, aes_init); ++#else ++module_init(aes_init); ++#endif ++module_exit(aes_exit); +diff -Nur linux-3.14.36/arch/arm64/crypto/aes-modes.S linux-openelec/arch/arm64/crypto/aes-modes.S +--- linux-3.14.36/arch/arm64/crypto/aes-modes.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/aes-modes.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,532 @@ ++/* ++ * linux/arch/arm64/crypto/aes-modes.S - chaining mode wrappers for AES ++ * ++ * Copyright (C) 2013 Linaro Ltd ++ * ++ * 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. ++ */ ++ ++/* included by aes-ce.S and aes-neon.S */ ++ ++ .text ++ .align 4 ++ ++/* ++ * There are several ways to instantiate this code: ++ * - no interleave, all inline ++ * - 2-way interleave, 2x calls out of line (-DINTERLEAVE=2) ++ * - 2-way interleave, all inline (-DINTERLEAVE=2 -DINTERLEAVE_INLINE) ++ * - 4-way interleave, 4x calls out of line (-DINTERLEAVE=4) ++ * - 4-way interleave, all inline (-DINTERLEAVE=4 -DINTERLEAVE_INLINE) ++ * ++ * Macros imported by this code: ++ * - enc_prepare - setup NEON registers for encryption ++ * - dec_prepare - setup NEON registers for decryption ++ * - enc_switch_key - change to new key after having prepared for encryption ++ * - encrypt_block - encrypt a single block ++ * - decrypt block - decrypt a single block ++ * - encrypt_block2x - encrypt 2 blocks in parallel (if INTERLEAVE == 2) ++ * - decrypt_block2x - decrypt 2 blocks in parallel (if INTERLEAVE == 2) ++ * - encrypt_block4x - encrypt 4 blocks in parallel (if INTERLEAVE == 4) ++ * - decrypt_block4x - decrypt 4 blocks in parallel (if INTERLEAVE == 4) ++ */ ++ ++#if defined(INTERLEAVE) && !defined(INTERLEAVE_INLINE) ++#define FRAME_PUSH stp x29, x30, [sp,#-16]! ; mov x29, sp ++#define FRAME_POP ldp x29, x30, [sp],#16 ++ ++#if INTERLEAVE == 2 ++ ++aes_encrypt_block2x: ++ encrypt_block2x v0, v1, w3, x2, x6, w7 ++ ret ++ENDPROC(aes_encrypt_block2x) ++ ++aes_decrypt_block2x: ++ decrypt_block2x v0, v1, w3, x2, x6, w7 ++ ret ++ENDPROC(aes_decrypt_block2x) ++ ++#elif INTERLEAVE == 4 ++ ++aes_encrypt_block4x: ++ encrypt_block4x v0, v1, v2, v3, w3, x2, x6, w7 ++ ret ++ENDPROC(aes_encrypt_block4x) ++ ++aes_decrypt_block4x: ++ decrypt_block4x v0, v1, v2, v3, w3, x2, x6, w7 ++ ret ++ENDPROC(aes_decrypt_block4x) ++ ++#else ++#error INTERLEAVE should equal 2 or 4 ++#endif ++ ++ .macro do_encrypt_block2x ++ bl aes_encrypt_block2x ++ .endm ++ ++ .macro do_decrypt_block2x ++ bl aes_decrypt_block2x ++ .endm ++ ++ .macro do_encrypt_block4x ++ bl aes_encrypt_block4x ++ .endm ++ ++ .macro do_decrypt_block4x ++ bl aes_decrypt_block4x ++ .endm ++ ++#else ++#define FRAME_PUSH ++#define FRAME_POP ++ ++ .macro do_encrypt_block2x ++ encrypt_block2x v0, v1, w3, x2, x6, w7 ++ .endm ++ ++ .macro do_decrypt_block2x ++ decrypt_block2x v0, v1, w3, x2, x6, w7 ++ .endm ++ ++ .macro do_encrypt_block4x ++ encrypt_block4x v0, v1, v2, v3, w3, x2, x6, w7 ++ .endm ++ ++ .macro do_decrypt_block4x ++ decrypt_block4x v0, v1, v2, v3, w3, x2, x6, w7 ++ .endm ++ ++#endif ++ ++ /* ++ * aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, ++ * int blocks, int first) ++ * aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, ++ * int blocks, int first) ++ */ ++ ++AES_ENTRY(aes_ecb_encrypt) ++ FRAME_PUSH ++ cbz w5, .LecbencloopNx ++ ++ enc_prepare w3, x2, x5 ++ ++.LecbencloopNx: ++#if INTERLEAVE >= 2 ++ subs w4, w4, #INTERLEAVE ++ bmi .Lecbenc1x ++#if INTERLEAVE == 2 ++ ld1 {v0.16b-v1.16b}, [x1], #32 /* get 2 pt blocks */ ++ do_encrypt_block2x ++ st1 {v0.16b-v1.16b}, [x0], #32 ++#else ++ ld1 {v0.16b-v3.16b}, [x1], #64 /* get 4 pt blocks */ ++ do_encrypt_block4x ++ st1 {v0.16b-v3.16b}, [x0], #64 ++#endif ++ b .LecbencloopNx ++.Lecbenc1x: ++ adds w4, w4, #INTERLEAVE ++ beq .Lecbencout ++#endif ++.Lecbencloop: ++ ld1 {v0.16b}, [x1], #16 /* get next pt block */ ++ encrypt_block v0, w3, x2, x5, w6 ++ st1 {v0.16b}, [x0], #16 ++ subs w4, w4, #1 ++ bne .Lecbencloop ++.Lecbencout: ++ FRAME_POP ++ ret ++AES_ENDPROC(aes_ecb_encrypt) ++ ++ ++AES_ENTRY(aes_ecb_decrypt) ++ FRAME_PUSH ++ cbz w5, .LecbdecloopNx ++ ++ dec_prepare w3, x2, x5 ++ ++.LecbdecloopNx: ++#if INTERLEAVE >= 2 ++ subs w4, w4, #INTERLEAVE ++ bmi .Lecbdec1x ++#if INTERLEAVE == 2 ++ ld1 {v0.16b-v1.16b}, [x1], #32 /* get 2 ct blocks */ ++ do_decrypt_block2x ++ st1 {v0.16b-v1.16b}, [x0], #32 ++#else ++ ld1 {v0.16b-v3.16b}, [x1], #64 /* get 4 ct blocks */ ++ do_decrypt_block4x ++ st1 {v0.16b-v3.16b}, [x0], #64 ++#endif ++ b .LecbdecloopNx ++.Lecbdec1x: ++ adds w4, w4, #INTERLEAVE ++ beq .Lecbdecout ++#endif ++.Lecbdecloop: ++ ld1 {v0.16b}, [x1], #16 /* get next ct block */ ++ decrypt_block v0, w3, x2, x5, w6 ++ st1 {v0.16b}, [x0], #16 ++ subs w4, w4, #1 ++ bne .Lecbdecloop ++.Lecbdecout: ++ FRAME_POP ++ ret ++AES_ENDPROC(aes_ecb_decrypt) ++ ++ ++ /* ++ * aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, ++ * int blocks, u8 iv[], int first) ++ * aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, ++ * int blocks, u8 iv[], int first) ++ */ ++ ++AES_ENTRY(aes_cbc_encrypt) ++ cbz w6, .Lcbcencloop ++ ++ ld1 {v0.16b}, [x5] /* get iv */ ++ enc_prepare w3, x2, x5 ++ ++.Lcbcencloop: ++ ld1 {v1.16b}, [x1], #16 /* get next pt block */ ++ eor v0.16b, v0.16b, v1.16b /* ..and xor with iv */ ++ encrypt_block v0, w3, x2, x5, w6 ++ st1 {v0.16b}, [x0], #16 ++ subs w4, w4, #1 ++ bne .Lcbcencloop ++ ret ++AES_ENDPROC(aes_cbc_encrypt) ++ ++ ++AES_ENTRY(aes_cbc_decrypt) ++ FRAME_PUSH ++ cbz w6, .LcbcdecloopNx ++ ++ ld1 {v7.16b}, [x5] /* get iv */ ++ dec_prepare w3, x2, x5 ++ ++.LcbcdecloopNx: ++#if INTERLEAVE >= 2 ++ subs w4, w4, #INTERLEAVE ++ bmi .Lcbcdec1x ++#if INTERLEAVE == 2 ++ ld1 {v0.16b-v1.16b}, [x1], #32 /* get 2 ct blocks */ ++ mov v2.16b, v0.16b ++ mov v3.16b, v1.16b ++ do_decrypt_block2x ++ eor v0.16b, v0.16b, v7.16b ++ eor v1.16b, v1.16b, v2.16b ++ mov v7.16b, v3.16b ++ st1 {v0.16b-v1.16b}, [x0], #32 ++#else ++ ld1 {v0.16b-v3.16b}, [x1], #64 /* get 4 ct blocks */ ++ mov v4.16b, v0.16b ++ mov v5.16b, v1.16b ++ mov v6.16b, v2.16b ++ do_decrypt_block4x ++ sub x1, x1, #16 ++ eor v0.16b, v0.16b, v7.16b ++ eor v1.16b, v1.16b, v4.16b ++ ld1 {v7.16b}, [x1], #16 /* reload 1 ct block */ ++ eor v2.16b, v2.16b, v5.16b ++ eor v3.16b, v3.16b, v6.16b ++ st1 {v0.16b-v3.16b}, [x0], #64 ++#endif ++ b .LcbcdecloopNx ++.Lcbcdec1x: ++ adds w4, w4, #INTERLEAVE ++ beq .Lcbcdecout ++#endif ++.Lcbcdecloop: ++ ld1 {v1.16b}, [x1], #16 /* get next ct block */ ++ mov v0.16b, v1.16b /* ...and copy to v0 */ ++ decrypt_block v0, w3, x2, x5, w6 ++ eor v0.16b, v0.16b, v7.16b /* xor with iv => pt */ ++ mov v7.16b, v1.16b /* ct is next iv */ ++ st1 {v0.16b}, [x0], #16 ++ subs w4, w4, #1 ++ bne .Lcbcdecloop ++.Lcbcdecout: ++ FRAME_POP ++ ret ++AES_ENDPROC(aes_cbc_decrypt) ++ ++ ++ /* ++ * aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, ++ * int blocks, u8 ctr[], int first) ++ */ ++ ++AES_ENTRY(aes_ctr_encrypt) ++ FRAME_PUSH ++ cbnz w6, .Lctrfirst /* 1st time around? */ ++ umov x5, v4.d[1] /* keep swabbed ctr in reg */ ++ rev x5, x5 ++#if INTERLEAVE >= 2 ++ cmn w5, w4 /* 32 bit overflow? */ ++ bcs .Lctrinc ++ add x5, x5, #1 /* increment BE ctr */ ++ b .LctrincNx ++#else ++ b .Lctrinc ++#endif ++.Lctrfirst: ++ enc_prepare w3, x2, x6 ++ ld1 {v4.16b}, [x5] ++ umov x5, v4.d[1] /* keep swabbed ctr in reg */ ++ rev x5, x5 ++#if INTERLEAVE >= 2 ++ cmn w5, w4 /* 32 bit overflow? */ ++ bcs .Lctrloop ++.LctrloopNx: ++ subs w4, w4, #INTERLEAVE ++ bmi .Lctr1x ++#if INTERLEAVE == 2 ++ mov v0.8b, v4.8b ++ mov v1.8b, v4.8b ++ rev x7, x5 ++ add x5, x5, #1 ++ ins v0.d[1], x7 ++ rev x7, x5 ++ add x5, x5, #1 ++ ins v1.d[1], x7 ++ ld1 {v2.16b-v3.16b}, [x1], #32 /* get 2 input blocks */ ++ do_encrypt_block2x ++ eor v0.16b, v0.16b, v2.16b ++ eor v1.16b, v1.16b, v3.16b ++ st1 {v0.16b-v1.16b}, [x0], #32 ++#else ++ ldr q8, =0x30000000200000001 /* addends 1,2,3[,0] */ ++ dup v7.4s, w5 ++ mov v0.16b, v4.16b ++ add v7.4s, v7.4s, v8.4s ++ mov v1.16b, v4.16b ++ rev32 v8.16b, v7.16b ++ mov v2.16b, v4.16b ++ mov v3.16b, v4.16b ++ mov v1.s[3], v8.s[0] ++ mov v2.s[3], v8.s[1] ++ mov v3.s[3], v8.s[2] ++ ld1 {v5.16b-v7.16b}, [x1], #48 /* get 3 input blocks */ ++ do_encrypt_block4x ++ eor v0.16b, v5.16b, v0.16b ++ ld1 {v5.16b}, [x1], #16 /* get 1 input block */ ++ eor v1.16b, v6.16b, v1.16b ++ eor v2.16b, v7.16b, v2.16b ++ eor v3.16b, v5.16b, v3.16b ++ st1 {v0.16b-v3.16b}, [x0], #64 ++ add x5, x5, #INTERLEAVE ++#endif ++ cbz w4, .LctroutNx ++.LctrincNx: ++ rev x7, x5 ++ ins v4.d[1], x7 ++ b .LctrloopNx ++.LctroutNx: ++ sub x5, x5, #1 ++ rev x7, x5 ++ ins v4.d[1], x7 ++ b .Lctrout ++.Lctr1x: ++ adds w4, w4, #INTERLEAVE ++ beq .Lctrout ++#endif ++.Lctrloop: ++ mov v0.16b, v4.16b ++ encrypt_block v0, w3, x2, x6, w7 ++ subs w4, w4, #1 ++ bmi .Lctrhalfblock /* blocks < 0 means 1/2 block */ ++ ld1 {v3.16b}, [x1], #16 ++ eor v3.16b, v0.16b, v3.16b ++ st1 {v3.16b}, [x0], #16 ++ beq .Lctrout ++.Lctrinc: ++ adds x5, x5, #1 /* increment BE ctr */ ++ rev x7, x5 ++ ins v4.d[1], x7 ++ bcc .Lctrloop /* no overflow? */ ++ umov x7, v4.d[0] /* load upper word of ctr */ ++ rev x7, x7 /* ... to handle the carry */ ++ add x7, x7, #1 ++ rev x7, x7 ++ ins v4.d[0], x7 ++ b .Lctrloop ++.Lctrhalfblock: ++ ld1 {v3.8b}, [x1] ++ eor v3.8b, v0.8b, v3.8b ++ st1 {v3.8b}, [x0] ++.Lctrout: ++ FRAME_POP ++ ret ++AES_ENDPROC(aes_ctr_encrypt) ++ .ltorg ++ ++ ++ /* ++ * aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, ++ * int blocks, u8 const rk2[], u8 iv[], int first) ++ * aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, ++ * int blocks, u8 const rk2[], u8 iv[], int first) ++ */ ++ ++ .macro next_tweak, out, in, const, tmp ++ sshr \tmp\().2d, \in\().2d, #63 ++ and \tmp\().16b, \tmp\().16b, \const\().16b ++ add \out\().2d, \in\().2d, \in\().2d ++ ext \tmp\().16b, \tmp\().16b, \tmp\().16b, #8 ++ eor \out\().16b, \out\().16b, \tmp\().16b ++ .endm ++ ++.Lxts_mul_x: ++ .word 1, 0, 0x87, 0 ++ ++AES_ENTRY(aes_xts_encrypt) ++ FRAME_PUSH ++ cbz w7, .LxtsencloopNx ++ ++ ld1 {v4.16b}, [x6] ++ enc_prepare w3, x5, x6 ++ encrypt_block v4, w3, x5, x6, w7 /* first tweak */ ++ enc_switch_key w3, x2, x6 ++ ldr q7, .Lxts_mul_x ++ b .LxtsencNx ++ ++.LxtsencloopNx: ++ ldr q7, .Lxts_mul_x ++ next_tweak v4, v4, v7, v8 ++.LxtsencNx: ++#if INTERLEAVE >= 2 ++ subs w4, w4, #INTERLEAVE ++ bmi .Lxtsenc1x ++#if INTERLEAVE == 2 ++ ld1 {v0.16b-v1.16b}, [x1], #32 /* get 2 pt blocks */ ++ next_tweak v5, v4, v7, v8 ++ eor v0.16b, v0.16b, v4.16b ++ eor v1.16b, v1.16b, v5.16b ++ do_encrypt_block2x ++ eor v0.16b, v0.16b, v4.16b ++ eor v1.16b, v1.16b, v5.16b ++ st1 {v0.16b-v1.16b}, [x0], #32 ++ cbz w4, .LxtsencoutNx ++ next_tweak v4, v5, v7, v8 ++ b .LxtsencNx ++.LxtsencoutNx: ++ mov v4.16b, v5.16b ++ b .Lxtsencout ++#else ++ ld1 {v0.16b-v3.16b}, [x1], #64 /* get 4 pt blocks */ ++ next_tweak v5, v4, v7, v8 ++ eor v0.16b, v0.16b, v4.16b ++ next_tweak v6, v5, v7, v8 ++ eor v1.16b, v1.16b, v5.16b ++ eor v2.16b, v2.16b, v6.16b ++ next_tweak v7, v6, v7, v8 ++ eor v3.16b, v3.16b, v7.16b ++ do_encrypt_block4x ++ eor v3.16b, v3.16b, v7.16b ++ eor v0.16b, v0.16b, v4.16b ++ eor v1.16b, v1.16b, v5.16b ++ eor v2.16b, v2.16b, v6.16b ++ st1 {v0.16b-v3.16b}, [x0], #64 ++ mov v4.16b, v7.16b ++ cbz w4, .Lxtsencout ++ b .LxtsencloopNx ++#endif ++.Lxtsenc1x: ++ adds w4, w4, #INTERLEAVE ++ beq .Lxtsencout ++#endif ++.Lxtsencloop: ++ ld1 {v1.16b}, [x1], #16 ++ eor v0.16b, v1.16b, v4.16b ++ encrypt_block v0, w3, x2, x6, w7 ++ eor v0.16b, v0.16b, v4.16b ++ st1 {v0.16b}, [x0], #16 ++ subs w4, w4, #1 ++ beq .Lxtsencout ++ next_tweak v4, v4, v7, v8 ++ b .Lxtsencloop ++.Lxtsencout: ++ FRAME_POP ++ ret ++AES_ENDPROC(aes_xts_encrypt) ++ ++ ++AES_ENTRY(aes_xts_decrypt) ++ FRAME_PUSH ++ cbz w7, .LxtsdecloopNx ++ ++ ld1 {v4.16b}, [x6] ++ enc_prepare w3, x5, x6 ++ encrypt_block v4, w3, x5, x6, w7 /* first tweak */ ++ dec_prepare w3, x2, x6 ++ ldr q7, .Lxts_mul_x ++ b .LxtsdecNx ++ ++.LxtsdecloopNx: ++ ldr q7, .Lxts_mul_x ++ next_tweak v4, v4, v7, v8 ++.LxtsdecNx: ++#if INTERLEAVE >= 2 ++ subs w4, w4, #INTERLEAVE ++ bmi .Lxtsdec1x ++#if INTERLEAVE == 2 ++ ld1 {v0.16b-v1.16b}, [x1], #32 /* get 2 ct blocks */ ++ next_tweak v5, v4, v7, v8 ++ eor v0.16b, v0.16b, v4.16b ++ eor v1.16b, v1.16b, v5.16b ++ do_decrypt_block2x ++ eor v0.16b, v0.16b, v4.16b ++ eor v1.16b, v1.16b, v5.16b ++ st1 {v0.16b-v1.16b}, [x0], #32 ++ cbz w4, .LxtsdecoutNx ++ next_tweak v4, v5, v7, v8 ++ b .LxtsdecNx ++.LxtsdecoutNx: ++ mov v4.16b, v5.16b ++ b .Lxtsdecout ++#else ++ ld1 {v0.16b-v3.16b}, [x1], #64 /* get 4 ct blocks */ ++ next_tweak v5, v4, v7, v8 ++ eor v0.16b, v0.16b, v4.16b ++ next_tweak v6, v5, v7, v8 ++ eor v1.16b, v1.16b, v5.16b ++ eor v2.16b, v2.16b, v6.16b ++ next_tweak v7, v6, v7, v8 ++ eor v3.16b, v3.16b, v7.16b ++ do_decrypt_block4x ++ eor v3.16b, v3.16b, v7.16b ++ eor v0.16b, v0.16b, v4.16b ++ eor v1.16b, v1.16b, v5.16b ++ eor v2.16b, v2.16b, v6.16b ++ st1 {v0.16b-v3.16b}, [x0], #64 ++ mov v4.16b, v7.16b ++ cbz w4, .Lxtsdecout ++ b .LxtsdecloopNx ++#endif ++.Lxtsdec1x: ++ adds w4, w4, #INTERLEAVE ++ beq .Lxtsdecout ++#endif ++.Lxtsdecloop: ++ ld1 {v1.16b}, [x1], #16 ++ eor v0.16b, v1.16b, v4.16b ++ decrypt_block v0, w3, x2, x6, w7 ++ eor v0.16b, v0.16b, v4.16b ++ st1 {v0.16b}, [x0], #16 ++ subs w4, w4, #1 ++ beq .Lxtsdecout ++ next_tweak v4, v4, v7, v8 ++ b .Lxtsdecloop ++.Lxtsdecout: ++ FRAME_POP ++ ret ++AES_ENDPROC(aes_xts_decrypt) +diff -Nur linux-3.14.36/arch/arm64/crypto/aes-neon.S linux-openelec/arch/arm64/crypto/aes-neon.S +--- linux-3.14.36/arch/arm64/crypto/aes-neon.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/aes-neon.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,382 @@ ++/* ++ * linux/arch/arm64/crypto/aes-neon.S - AES cipher for ARMv8 NEON ++ * ++ * Copyright (C) 2013 Linaro Ltd ++ * ++ * 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 ++ ++#define AES_ENTRY(func) ENTRY(neon_ ## func) ++#define AES_ENDPROC(func) ENDPROC(neon_ ## func) ++ ++ /* multiply by polynomial 'x' in GF(2^8) */ ++ .macro mul_by_x, out, in, temp, const ++ sshr \temp, \in, #7 ++ add \out, \in, \in ++ and \temp, \temp, \const ++ eor \out, \out, \temp ++ .endm ++ ++ /* preload the entire Sbox */ ++ .macro prepare, sbox, shiftrows, temp ++ adr \temp, \sbox ++ movi v12.16b, #0x40 ++ ldr q13, \shiftrows ++ movi v14.16b, #0x1b ++ ld1 {v16.16b-v19.16b}, [\temp], #64 ++ ld1 {v20.16b-v23.16b}, [\temp], #64 ++ ld1 {v24.16b-v27.16b}, [\temp], #64 ++ ld1 {v28.16b-v31.16b}, [\temp] ++ .endm ++ ++ /* do preload for encryption */ ++ .macro enc_prepare, ignore0, ignore1, temp ++ prepare .LForward_Sbox, .LForward_ShiftRows, \temp ++ .endm ++ ++ .macro enc_switch_key, ignore0, ignore1, temp ++ /* do nothing */ ++ .endm ++ ++ /* do preload for decryption */ ++ .macro dec_prepare, ignore0, ignore1, temp ++ prepare .LReverse_Sbox, .LReverse_ShiftRows, \temp ++ .endm ++ ++ /* apply SubBytes transformation using the the preloaded Sbox */ ++ .macro sub_bytes, in ++ sub v9.16b, \in\().16b, v12.16b ++ tbl \in\().16b, {v16.16b-v19.16b}, \in\().16b ++ sub v10.16b, v9.16b, v12.16b ++ tbx \in\().16b, {v20.16b-v23.16b}, v9.16b ++ sub v11.16b, v10.16b, v12.16b ++ tbx \in\().16b, {v24.16b-v27.16b}, v10.16b ++ tbx \in\().16b, {v28.16b-v31.16b}, v11.16b ++ .endm ++ ++ /* apply MixColumns transformation */ ++ .macro mix_columns, in ++ mul_by_x v10.16b, \in\().16b, v9.16b, v14.16b ++ rev32 v8.8h, \in\().8h ++ eor \in\().16b, v10.16b, \in\().16b ++ shl v9.4s, v8.4s, #24 ++ shl v11.4s, \in\().4s, #24 ++ sri v9.4s, v8.4s, #8 ++ sri v11.4s, \in\().4s, #8 ++ eor v9.16b, v9.16b, v8.16b ++ eor v10.16b, v10.16b, v9.16b ++ eor \in\().16b, v10.16b, v11.16b ++ .endm ++ ++ /* Inverse MixColumns: pre-multiply by { 5, 0, 4, 0 } */ ++ .macro inv_mix_columns, in ++ mul_by_x v11.16b, \in\().16b, v10.16b, v14.16b ++ mul_by_x v11.16b, v11.16b, v10.16b, v14.16b ++ eor \in\().16b, \in\().16b, v11.16b ++ rev32 v11.8h, v11.8h ++ eor \in\().16b, \in\().16b, v11.16b ++ mix_columns \in ++ .endm ++ ++ .macro do_block, enc, in, rounds, rk, rkp, i ++ ld1 {v15.16b}, [\rk] ++ add \rkp, \rk, #16 ++ mov \i, \rounds ++1111: eor \in\().16b, \in\().16b, v15.16b /* ^round key */ ++ tbl \in\().16b, {\in\().16b}, v13.16b /* ShiftRows */ ++ sub_bytes \in ++ ld1 {v15.16b}, [\rkp], #16 ++ subs \i, \i, #1 ++ beq 2222f ++ .if \enc == 1 ++ mix_columns \in ++ .else ++ inv_mix_columns \in ++ .endif ++ b 1111b ++2222: eor \in\().16b, \in\().16b, v15.16b /* ^round key */ ++ .endm ++ ++ .macro encrypt_block, in, rounds, rk, rkp, i ++ do_block 1, \in, \rounds, \rk, \rkp, \i ++ .endm ++ ++ .macro decrypt_block, in, rounds, rk, rkp, i ++ do_block 0, \in, \rounds, \rk, \rkp, \i ++ .endm ++ ++ /* ++ * Interleaved versions: functionally equivalent to the ++ * ones above, but applied to 2 or 4 AES states in parallel. ++ */ ++ ++ .macro sub_bytes_2x, in0, in1 ++ sub v8.16b, \in0\().16b, v12.16b ++ sub v9.16b, \in1\().16b, v12.16b ++ tbl \in0\().16b, {v16.16b-v19.16b}, \in0\().16b ++ tbl \in1\().16b, {v16.16b-v19.16b}, \in1\().16b ++ sub v10.16b, v8.16b, v12.16b ++ sub v11.16b, v9.16b, v12.16b ++ tbx \in0\().16b, {v20.16b-v23.16b}, v8.16b ++ tbx \in1\().16b, {v20.16b-v23.16b}, v9.16b ++ sub v8.16b, v10.16b, v12.16b ++ sub v9.16b, v11.16b, v12.16b ++ tbx \in0\().16b, {v24.16b-v27.16b}, v10.16b ++ tbx \in1\().16b, {v24.16b-v27.16b}, v11.16b ++ tbx \in0\().16b, {v28.16b-v31.16b}, v8.16b ++ tbx \in1\().16b, {v28.16b-v31.16b}, v9.16b ++ .endm ++ ++ .macro sub_bytes_4x, in0, in1, in2, in3 ++ sub v8.16b, \in0\().16b, v12.16b ++ tbl \in0\().16b, {v16.16b-v19.16b}, \in0\().16b ++ sub v9.16b, \in1\().16b, v12.16b ++ tbl \in1\().16b, {v16.16b-v19.16b}, \in1\().16b ++ sub v10.16b, \in2\().16b, v12.16b ++ tbl \in2\().16b, {v16.16b-v19.16b}, \in2\().16b ++ sub v11.16b, \in3\().16b, v12.16b ++ tbl \in3\().16b, {v16.16b-v19.16b}, \in3\().16b ++ tbx \in0\().16b, {v20.16b-v23.16b}, v8.16b ++ tbx \in1\().16b, {v20.16b-v23.16b}, v9.16b ++ sub v8.16b, v8.16b, v12.16b ++ tbx \in2\().16b, {v20.16b-v23.16b}, v10.16b ++ sub v9.16b, v9.16b, v12.16b ++ tbx \in3\().16b, {v20.16b-v23.16b}, v11.16b ++ sub v10.16b, v10.16b, v12.16b ++ tbx \in0\().16b, {v24.16b-v27.16b}, v8.16b ++ sub v11.16b, v11.16b, v12.16b ++ tbx \in1\().16b, {v24.16b-v27.16b}, v9.16b ++ sub v8.16b, v8.16b, v12.16b ++ tbx \in2\().16b, {v24.16b-v27.16b}, v10.16b ++ sub v9.16b, v9.16b, v12.16b ++ tbx \in3\().16b, {v24.16b-v27.16b}, v11.16b ++ sub v10.16b, v10.16b, v12.16b ++ tbx \in0\().16b, {v28.16b-v31.16b}, v8.16b ++ sub v11.16b, v11.16b, v12.16b ++ tbx \in1\().16b, {v28.16b-v31.16b}, v9.16b ++ tbx \in2\().16b, {v28.16b-v31.16b}, v10.16b ++ tbx \in3\().16b, {v28.16b-v31.16b}, v11.16b ++ .endm ++ ++ .macro mul_by_x_2x, out0, out1, in0, in1, tmp0, tmp1, const ++ sshr \tmp0\().16b, \in0\().16b, #7 ++ add \out0\().16b, \in0\().16b, \in0\().16b ++ sshr \tmp1\().16b, \in1\().16b, #7 ++ and \tmp0\().16b, \tmp0\().16b, \const\().16b ++ add \out1\().16b, \in1\().16b, \in1\().16b ++ and \tmp1\().16b, \tmp1\().16b, \const\().16b ++ eor \out0\().16b, \out0\().16b, \tmp0\().16b ++ eor \out1\().16b, \out1\().16b, \tmp1\().16b ++ .endm ++ ++ .macro mix_columns_2x, in0, in1 ++ mul_by_x_2x v8, v9, \in0, \in1, v10, v11, v14 ++ rev32 v10.8h, \in0\().8h ++ rev32 v11.8h, \in1\().8h ++ eor \in0\().16b, v8.16b, \in0\().16b ++ eor \in1\().16b, v9.16b, \in1\().16b ++ shl v12.4s, v10.4s, #24 ++ shl v13.4s, v11.4s, #24 ++ eor v8.16b, v8.16b, v10.16b ++ sri v12.4s, v10.4s, #8 ++ shl v10.4s, \in0\().4s, #24 ++ eor v9.16b, v9.16b, v11.16b ++ sri v13.4s, v11.4s, #8 ++ shl v11.4s, \in1\().4s, #24 ++ sri v10.4s, \in0\().4s, #8 ++ eor \in0\().16b, v8.16b, v12.16b ++ sri v11.4s, \in1\().4s, #8 ++ eor \in1\().16b, v9.16b, v13.16b ++ eor \in0\().16b, v10.16b, \in0\().16b ++ eor \in1\().16b, v11.16b, \in1\().16b ++ .endm ++ ++ .macro inv_mix_cols_2x, in0, in1 ++ mul_by_x_2x v8, v9, \in0, \in1, v10, v11, v14 ++ mul_by_x_2x v8, v9, v8, v9, v10, v11, v14 ++ eor \in0\().16b, \in0\().16b, v8.16b ++ eor \in1\().16b, \in1\().16b, v9.16b ++ rev32 v8.8h, v8.8h ++ rev32 v9.8h, v9.8h ++ eor \in0\().16b, \in0\().16b, v8.16b ++ eor \in1\().16b, \in1\().16b, v9.16b ++ mix_columns_2x \in0, \in1 ++ .endm ++ ++ .macro inv_mix_cols_4x, in0, in1, in2, in3 ++ mul_by_x_2x v8, v9, \in0, \in1, v10, v11, v14 ++ mul_by_x_2x v10, v11, \in2, \in3, v12, v13, v14 ++ mul_by_x_2x v8, v9, v8, v9, v12, v13, v14 ++ mul_by_x_2x v10, v11, v10, v11, v12, v13, v14 ++ eor \in0\().16b, \in0\().16b, v8.16b ++ eor \in1\().16b, \in1\().16b, v9.16b ++ eor \in2\().16b, \in2\().16b, v10.16b ++ eor \in3\().16b, \in3\().16b, v11.16b ++ rev32 v8.8h, v8.8h ++ rev32 v9.8h, v9.8h ++ rev32 v10.8h, v10.8h ++ rev32 v11.8h, v11.8h ++ eor \in0\().16b, \in0\().16b, v8.16b ++ eor \in1\().16b, \in1\().16b, v9.16b ++ eor \in2\().16b, \in2\().16b, v10.16b ++ eor \in3\().16b, \in3\().16b, v11.16b ++ mix_columns_2x \in0, \in1 ++ mix_columns_2x \in2, \in3 ++ .endm ++ ++ .macro do_block_2x, enc, in0, in1 rounds, rk, rkp, i ++ ld1 {v15.16b}, [\rk] ++ add \rkp, \rk, #16 ++ mov \i, \rounds ++1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */ ++ eor \in1\().16b, \in1\().16b, v15.16b /* ^round key */ ++ sub_bytes_2x \in0, \in1 ++ tbl \in0\().16b, {\in0\().16b}, v13.16b /* ShiftRows */ ++ tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */ ++ ld1 {v15.16b}, [\rkp], #16 ++ subs \i, \i, #1 ++ beq 2222f ++ .if \enc == 1 ++ mix_columns_2x \in0, \in1 ++ ldr q13, .LForward_ShiftRows ++ .else ++ inv_mix_cols_2x \in0, \in1 ++ ldr q13, .LReverse_ShiftRows ++ .endif ++ movi v12.16b, #0x40 ++ b 1111b ++2222: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */ ++ eor \in1\().16b, \in1\().16b, v15.16b /* ^round key */ ++ .endm ++ ++ .macro do_block_4x, enc, in0, in1, in2, in3, rounds, rk, rkp, i ++ ld1 {v15.16b}, [\rk] ++ add \rkp, \rk, #16 ++ mov \i, \rounds ++1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */ ++ eor \in1\().16b, \in1\().16b, v15.16b /* ^round key */ ++ eor \in2\().16b, \in2\().16b, v15.16b /* ^round key */ ++ eor \in3\().16b, \in3\().16b, v15.16b /* ^round key */ ++ sub_bytes_4x \in0, \in1, \in2, \in3 ++ tbl \in0\().16b, {\in0\().16b}, v13.16b /* ShiftRows */ ++ tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */ ++ tbl \in2\().16b, {\in2\().16b}, v13.16b /* ShiftRows */ ++ tbl \in3\().16b, {\in3\().16b}, v13.16b /* ShiftRows */ ++ ld1 {v15.16b}, [\rkp], #16 ++ subs \i, \i, #1 ++ beq 2222f ++ .if \enc == 1 ++ mix_columns_2x \in0, \in1 ++ mix_columns_2x \in2, \in3 ++ ldr q13, .LForward_ShiftRows ++ .else ++ inv_mix_cols_4x \in0, \in1, \in2, \in3 ++ ldr q13, .LReverse_ShiftRows ++ .endif ++ movi v12.16b, #0x40 ++ b 1111b ++2222: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */ ++ eor \in1\().16b, \in1\().16b, v15.16b /* ^round key */ ++ eor \in2\().16b, \in2\().16b, v15.16b /* ^round key */ ++ eor \in3\().16b, \in3\().16b, v15.16b /* ^round key */ ++ .endm ++ ++ .macro encrypt_block2x, in0, in1, rounds, rk, rkp, i ++ do_block_2x 1, \in0, \in1, \rounds, \rk, \rkp, \i ++ .endm ++ ++ .macro decrypt_block2x, in0, in1, rounds, rk, rkp, i ++ do_block_2x 0, \in0, \in1, \rounds, \rk, \rkp, \i ++ .endm ++ ++ .macro encrypt_block4x, in0, in1, in2, in3, rounds, rk, rkp, i ++ do_block_4x 1, \in0, \in1, \in2, \in3, \rounds, \rk, \rkp, \i ++ .endm ++ ++ .macro decrypt_block4x, in0, in1, in2, in3, rounds, rk, rkp, i ++ do_block_4x 0, \in0, \in1, \in2, \in3, \rounds, \rk, \rkp, \i ++ .endm ++ ++#include "aes-modes.S" ++ ++ .text ++ .align 4 ++.LForward_ShiftRows: ++ .byte 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3 ++ .byte 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb ++ ++.LReverse_ShiftRows: ++ .byte 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb ++ .byte 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3 ++ ++.LForward_Sbox: ++ .byte 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5 ++ .byte 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76 ++ .byte 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0 ++ .byte 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0 ++ .byte 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc ++ .byte 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15 ++ .byte 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a ++ .byte 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75 ++ .byte 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0 ++ .byte 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84 ++ .byte 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b ++ .byte 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf ++ .byte 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85 ++ .byte 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8 ++ .byte 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5 ++ .byte 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2 ++ .byte 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17 ++ .byte 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73 ++ .byte 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88 ++ .byte 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb ++ .byte 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c ++ .byte 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79 ++ .byte 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9 ++ .byte 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08 ++ .byte 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6 ++ .byte 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a ++ .byte 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e ++ .byte 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e ++ .byte 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94 ++ .byte 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf ++ .byte 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68 ++ .byte 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ++ ++.LReverse_Sbox: ++ .byte 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38 ++ .byte 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb ++ .byte 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87 ++ .byte 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb ++ .byte 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d ++ .byte 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e ++ .byte 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2 ++ .byte 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 ++ .byte 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16 ++ .byte 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 ++ .byte 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda ++ .byte 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 ++ .byte 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a ++ .byte 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 ++ .byte 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02 ++ .byte 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b ++ .byte 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea ++ .byte 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 ++ .byte 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85 ++ .byte 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e ++ .byte 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89 ++ .byte 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b ++ .byte 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20 ++ .byte 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 ++ .byte 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31 ++ .byte 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f ++ .byte 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d ++ .byte 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef ++ .byte 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0 ++ .byte 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 ++ .byte 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26 ++ .byte 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d +diff -Nur linux-3.14.36/arch/arm64/crypto/ghash-ce-core.S linux-openelec/arch/arm64/crypto/ghash-ce-core.S +--- linux-3.14.36/arch/arm64/crypto/ghash-ce-core.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/ghash-ce-core.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,79 @@ ++/* ++ * Accelerated GHASH implementation with ARMv8 PMULL instructions. ++ * ++ * Copyright (C) 2014 Linaro Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++ SHASH .req v0 ++ SHASH2 .req v1 ++ T1 .req v2 ++ T2 .req v3 ++ MASK .req v4 ++ XL .req v5 ++ XM .req v6 ++ XH .req v7 ++ IN1 .req v7 ++ ++ .text ++ .arch armv8-a+crypto ++ ++ /* ++ * void pmull_ghash_update(int blocks, u64 dg[], const char *src, ++ * struct ghash_key const *k, const char *head) ++ */ ++ENTRY(pmull_ghash_update) ++ ld1 {SHASH.16b}, [x3] ++ ld1 {XL.16b}, [x1] ++ movi MASK.16b, #0xe1 ++ ext SHASH2.16b, SHASH.16b, SHASH.16b, #8 ++ shl MASK.2d, MASK.2d, #57 ++ eor SHASH2.16b, SHASH2.16b, SHASH.16b ++ ++ /* do the head block first, if supplied */ ++ cbz x4, 0f ++ ld1 {T1.2d}, [x4] ++ b 1f ++ ++0: ld1 {T1.2d}, [x2], #16 ++ sub w0, w0, #1 ++ ++1: /* multiply XL by SHASH in GF(2^128) */ ++CPU_LE( rev64 T1.16b, T1.16b ) ++ ++ ext T2.16b, XL.16b, XL.16b, #8 ++ ext IN1.16b, T1.16b, T1.16b, #8 ++ eor T1.16b, T1.16b, T2.16b ++ eor XL.16b, XL.16b, IN1.16b ++ ++ pmull2 XH.1q, SHASH.2d, XL.2d // a1 * b1 ++ eor T1.16b, T1.16b, XL.16b ++ pmull XL.1q, SHASH.1d, XL.1d // a0 * b0 ++ pmull XM.1q, SHASH2.1d, T1.1d // (a1 + a0)(b1 + b0) ++ ++ ext T1.16b, XL.16b, XH.16b, #8 ++ eor T2.16b, XL.16b, XH.16b ++ eor XM.16b, XM.16b, T1.16b ++ eor XM.16b, XM.16b, T2.16b ++ pmull T2.1q, XL.1d, MASK.1d ++ ++ mov XH.d[0], XM.d[1] ++ mov XM.d[1], XL.d[0] ++ ++ eor XL.16b, XM.16b, T2.16b ++ ext T2.16b, XL.16b, XL.16b, #8 ++ pmull XL.1q, XL.1d, MASK.1d ++ eor T2.16b, T2.16b, XH.16b ++ eor XL.16b, XL.16b, T2.16b ++ ++ cbnz w0, 0b ++ ++ st1 {XL.16b}, [x1] ++ ret ++ENDPROC(pmull_ghash_update) +diff -Nur linux-3.14.36/arch/arm64/crypto/ghash-ce-glue.c linux-openelec/arch/arm64/crypto/ghash-ce-glue.c +--- linux-3.14.36/arch/arm64/crypto/ghash-ce-glue.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/ghash-ce-glue.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,156 @@ ++/* ++ * Accelerated GHASH implementation with ARMv8 PMULL instructions. ++ * ++ * Copyright (C) 2014 Linaro Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("GHASH secure hash using ARMv8 Crypto Extensions"); ++MODULE_AUTHOR("Ard Biesheuvel "); ++MODULE_LICENSE("GPL v2"); ++ ++#define GHASH_BLOCK_SIZE 16 ++#define GHASH_DIGEST_SIZE 16 ++ ++struct ghash_key { ++ u64 a; ++ u64 b; ++}; ++ ++struct ghash_desc_ctx { ++ u64 digest[GHASH_DIGEST_SIZE/sizeof(u64)]; ++ u8 buf[GHASH_BLOCK_SIZE]; ++ u32 count; ++}; ++ ++asmlinkage void pmull_ghash_update(int blocks, u64 dg[], const char *src, ++ struct ghash_key const *k, const char *head); ++ ++static int ghash_init(struct shash_desc *desc) ++{ ++ struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); ++ ++ *ctx = (struct ghash_desc_ctx){}; ++ return 0; ++} ++ ++static int ghash_update(struct shash_desc *desc, const u8 *src, ++ unsigned int len) ++{ ++ struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); ++ unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; ++ ++ ctx->count += len; ++ ++ if ((partial + len) >= GHASH_BLOCK_SIZE) { ++ struct ghash_key *key = crypto_shash_ctx(desc->tfm); ++ int blocks; ++ ++ if (partial) { ++ int p = GHASH_BLOCK_SIZE - partial; ++ ++ memcpy(ctx->buf + partial, src, p); ++ src += p; ++ len -= p; ++ } ++ ++ blocks = len / GHASH_BLOCK_SIZE; ++ len %= GHASH_BLOCK_SIZE; ++ ++ kernel_neon_begin_partial(8); ++ pmull_ghash_update(blocks, ctx->digest, src, key, ++ partial ? ctx->buf : NULL); ++ kernel_neon_end(); ++ src += blocks * GHASH_BLOCK_SIZE; ++ partial = 0; ++ } ++ if (len) ++ memcpy(ctx->buf + partial, src, len); ++ return 0; ++} ++ ++static int ghash_final(struct shash_desc *desc, u8 *dst) ++{ ++ struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); ++ unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; ++ ++ if (partial) { ++ struct ghash_key *key = crypto_shash_ctx(desc->tfm); ++ ++ memset(ctx->buf + partial, 0, GHASH_BLOCK_SIZE - partial); ++ ++ kernel_neon_begin_partial(8); ++ pmull_ghash_update(1, ctx->digest, ctx->buf, key, NULL); ++ kernel_neon_end(); ++ } ++ put_unaligned_be64(ctx->digest[1], dst); ++ put_unaligned_be64(ctx->digest[0], dst + 8); ++ ++ *ctx = (struct ghash_desc_ctx){}; ++ return 0; ++} ++ ++static int ghash_setkey(struct crypto_shash *tfm, ++ const u8 *inkey, unsigned int keylen) ++{ ++ struct ghash_key *key = crypto_shash_ctx(tfm); ++ u64 a, b; ++ ++ if (keylen != GHASH_BLOCK_SIZE) { ++ crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); ++ return -EINVAL; ++ } ++ ++ /* perform multiplication by 'x' in GF(2^128) */ ++ b = get_unaligned_be64(inkey); ++ a = get_unaligned_be64(inkey + 8); ++ ++ key->a = (a << 1) | (b >> 63); ++ key->b = (b << 1) | (a >> 63); ++ ++ if (b >> 63) ++ key->b ^= 0xc200000000000000UL; ++ ++ return 0; ++} ++ ++static struct shash_alg ghash_alg = { ++ .digestsize = GHASH_DIGEST_SIZE, ++ .init = ghash_init, ++ .update = ghash_update, ++ .final = ghash_final, ++ .setkey = ghash_setkey, ++ .descsize = sizeof(struct ghash_desc_ctx), ++ .base = { ++ .cra_name = "ghash", ++ .cra_driver_name = "ghash-ce", ++ .cra_priority = 200, ++ .cra_flags = CRYPTO_ALG_TYPE_SHASH, ++ .cra_blocksize = GHASH_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct ghash_key), ++ .cra_module = THIS_MODULE, ++ }, ++}; ++ ++static int __init ghash_ce_mod_init(void) ++{ ++ return crypto_register_shash(&ghash_alg); ++} ++ ++static void __exit ghash_ce_mod_exit(void) ++{ ++ crypto_unregister_shash(&ghash_alg); ++} ++ ++module_cpu_feature_match(PMULL, ghash_ce_mod_init); ++module_exit(ghash_ce_mod_exit); +diff -Nur linux-3.14.36/arch/arm64/crypto/Kconfig linux-openelec/arch/arm64/crypto/Kconfig +--- linux-3.14.36/arch/arm64/crypto/Kconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/Kconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,53 @@ ++ ++menuconfig ARM64_CRYPTO ++ bool "ARM64 Accelerated Cryptographic Algorithms" ++ depends on ARM64 ++ help ++ Say Y here to choose from a selection of cryptographic algorithms ++ implemented using ARM64 specific CPU features or instructions. ++ ++if ARM64_CRYPTO ++ ++config CRYPTO_SHA1_ARM64_CE ++ tristate "SHA-1 digest algorithm (ARMv8 Crypto Extensions)" ++ depends on ARM64 && KERNEL_MODE_NEON ++ select CRYPTO_HASH ++ ++config CRYPTO_SHA2_ARM64_CE ++ tristate "SHA-224/SHA-256 digest algorithm (ARMv8 Crypto Extensions)" ++ depends on ARM64 && KERNEL_MODE_NEON ++ select CRYPTO_HASH ++ ++config CRYPTO_GHASH_ARM64_CE ++ tristate "GHASH (for GCM chaining mode) using ARMv8 Crypto Extensions" ++ depends on ARM64 && KERNEL_MODE_NEON ++ select CRYPTO_HASH ++ ++config CRYPTO_AES_ARM64_CE ++ tristate "AES core cipher using ARMv8 Crypto Extensions" ++ depends on ARM64 && KERNEL_MODE_NEON ++ select CRYPTO_ALGAPI ++ select CRYPTO_AES ++ ++config CRYPTO_AES_ARM64_CE_CCM ++ tristate "AES in CCM mode using ARMv8 Crypto Extensions" ++ depends on ARM64 && KERNEL_MODE_NEON ++ select CRYPTO_ALGAPI ++ select CRYPTO_AES ++ select CRYPTO_AEAD ++ ++config CRYPTO_AES_ARM64_CE_BLK ++ tristate "AES in ECB/CBC/CTR/XTS modes using ARMv8 Crypto Extensions" ++ depends on ARM64 && KERNEL_MODE_NEON ++ select CRYPTO_BLKCIPHER ++ select CRYPTO_AES ++ select CRYPTO_ABLK_HELPER ++ ++config CRYPTO_AES_ARM64_NEON_BLK ++ tristate "AES in ECB/CBC/CTR/XTS modes using NEON instructions" ++ depends on ARM64 && KERNEL_MODE_NEON ++ select CRYPTO_BLKCIPHER ++ select CRYPTO_AES ++ select CRYPTO_ABLK_HELPER ++ ++endif +diff -Nur linux-3.14.36/arch/arm64/crypto/Makefile linux-openelec/arch/arm64/crypto/Makefile +--- linux-3.14.36/arch/arm64/crypto/Makefile 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,38 @@ ++# ++# linux/arch/arm64/crypto/Makefile ++# ++# Copyright (C) 2014 Linaro Ltd ++# ++# 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. ++# ++ ++obj-$(CONFIG_CRYPTO_SHA1_ARM64_CE) += sha1-ce.o ++sha1-ce-y := sha1-ce-glue.o sha1-ce-core.o ++ ++obj-$(CONFIG_CRYPTO_SHA2_ARM64_CE) += sha2-ce.o ++sha2-ce-y := sha2-ce-glue.o sha2-ce-core.o ++ ++obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o ++ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o ++ ++obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o ++CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto ++ ++obj-$(CONFIG_CRYPTO_AES_ARM64_CE_CCM) += aes-ce-ccm.o ++aes-ce-ccm-y := aes-ce-ccm-glue.o aes-ce-ccm-core.o ++ ++obj-$(CONFIG_CRYPTO_AES_ARM64_CE_BLK) += aes-ce-blk.o ++aes-ce-blk-y := aes-glue-ce.o aes-ce.o ++ ++obj-$(CONFIG_CRYPTO_AES_ARM64_NEON_BLK) += aes-neon-blk.o ++aes-neon-blk-y := aes-glue-neon.o aes-neon.o ++ ++AFLAGS_aes-ce.o := -DINTERLEAVE=2 -DINTERLEAVE_INLINE ++AFLAGS_aes-neon.o := -DINTERLEAVE=4 ++ ++CFLAGS_aes-glue-ce.o := -DUSE_V8_CRYPTO_EXTENSIONS ++ ++$(obj)/aes-glue-%.o: $(src)/aes-glue.c FORCE ++ $(call if_changed_dep,cc_o_c) +diff -Nur linux-3.14.36/arch/arm64/crypto/sha1-ce-core.S linux-openelec/arch/arm64/crypto/sha1-ce-core.S +--- linux-3.14.36/arch/arm64/crypto/sha1-ce-core.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/sha1-ce-core.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,153 @@ ++/* ++ * sha1-ce-core.S - SHA-1 secure hash using ARMv8 Crypto Extensions ++ * ++ * Copyright (C) 2014 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++ .text ++ .arch armv8-a+crypto ++ ++ k0 .req v0 ++ k1 .req v1 ++ k2 .req v2 ++ k3 .req v3 ++ ++ t0 .req v4 ++ t1 .req v5 ++ ++ dga .req q6 ++ dgav .req v6 ++ dgb .req s7 ++ dgbv .req v7 ++ ++ dg0q .req q12 ++ dg0s .req s12 ++ dg0v .req v12 ++ dg1s .req s13 ++ dg1v .req v13 ++ dg2s .req s14 ++ ++ .macro add_only, op, ev, rc, s0, dg1 ++ .ifc \ev, ev ++ add t1.4s, v\s0\().4s, \rc\().4s ++ sha1h dg2s, dg0s ++ .ifnb \dg1 ++ sha1\op dg0q, \dg1, t0.4s ++ .else ++ sha1\op dg0q, dg1s, t0.4s ++ .endif ++ .else ++ .ifnb \s0 ++ add t0.4s, v\s0\().4s, \rc\().4s ++ .endif ++ sha1h dg1s, dg0s ++ sha1\op dg0q, dg2s, t1.4s ++ .endif ++ .endm ++ ++ .macro add_update, op, ev, rc, s0, s1, s2, s3, dg1 ++ sha1su0 v\s0\().4s, v\s1\().4s, v\s2\().4s ++ add_only \op, \ev, \rc, \s1, \dg1 ++ sha1su1 v\s0\().4s, v\s3\().4s ++ .endm ++ ++ /* ++ * The SHA1 round constants ++ */ ++ .align 4 ++.Lsha1_rcon: ++ .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 ++ ++ /* ++ * void sha1_ce_transform(int blocks, u8 const *src, u32 *state, ++ * u8 *head, long bytes) ++ */ ++ENTRY(sha1_ce_transform) ++ /* load round constants */ ++ adr x6, .Lsha1_rcon ++ ld1r {k0.4s}, [x6], #4 ++ ld1r {k1.4s}, [x6], #4 ++ ld1r {k2.4s}, [x6], #4 ++ ld1r {k3.4s}, [x6] ++ ++ /* load state */ ++ ldr dga, [x2] ++ ldr dgb, [x2, #16] ++ ++ /* load partial state (if supplied) */ ++ cbz x3, 0f ++ ld1 {v8.4s-v11.4s}, [x3] ++ b 1f ++ ++ /* load input */ ++0: ld1 {v8.4s-v11.4s}, [x1], #64 ++ sub w0, w0, #1 ++ ++1: ++CPU_LE( rev32 v8.16b, v8.16b ) ++CPU_LE( rev32 v9.16b, v9.16b ) ++CPU_LE( rev32 v10.16b, v10.16b ) ++CPU_LE( rev32 v11.16b, v11.16b ) ++ ++2: add t0.4s, v8.4s, k0.4s ++ mov dg0v.16b, dgav.16b ++ ++ add_update c, ev, k0, 8, 9, 10, 11, dgb ++ add_update c, od, k0, 9, 10, 11, 8 ++ add_update c, ev, k0, 10, 11, 8, 9 ++ add_update c, od, k0, 11, 8, 9, 10 ++ add_update c, ev, k1, 8, 9, 10, 11 ++ ++ add_update p, od, k1, 9, 10, 11, 8 ++ add_update p, ev, k1, 10, 11, 8, 9 ++ add_update p, od, k1, 11, 8, 9, 10 ++ add_update p, ev, k1, 8, 9, 10, 11 ++ add_update p, od, k2, 9, 10, 11, 8 ++ ++ add_update m, ev, k2, 10, 11, 8, 9 ++ add_update m, od, k2, 11, 8, 9, 10 ++ add_update m, ev, k2, 8, 9, 10, 11 ++ add_update m, od, k2, 9, 10, 11, 8 ++ add_update m, ev, k3, 10, 11, 8, 9 ++ ++ add_update p, od, k3, 11, 8, 9, 10 ++ add_only p, ev, k3, 9 ++ add_only p, od, k3, 10 ++ add_only p, ev, k3, 11 ++ add_only p, od ++ ++ /* update state */ ++ add dgbv.2s, dgbv.2s, dg1v.2s ++ add dgav.4s, dgav.4s, dg0v.4s ++ ++ cbnz w0, 0b ++ ++ /* ++ * Final block: add padding and total bit count. ++ * Skip if we have no total byte count in x4. In that case, the input ++ * size was not a round multiple of the block size, and the padding is ++ * handled by the C code. ++ */ ++ cbz x4, 3f ++ movi v9.2d, #0 ++ mov x8, #0x80000000 ++ movi v10.2d, #0 ++ ror x7, x4, #29 // ror(lsl(x4, 3), 32) ++ fmov d8, x8 ++ mov x4, #0 ++ mov v11.d[0], xzr ++ mov v11.d[1], x7 ++ b 2b ++ ++ /* store new state */ ++3: str dga, [x2] ++ str dgb, [x2, #16] ++ ret ++ENDPROC(sha1_ce_transform) +diff -Nur linux-3.14.36/arch/arm64/crypto/sha1-ce-glue.c linux-openelec/arch/arm64/crypto/sha1-ce-glue.c +--- linux-3.14.36/arch/arm64/crypto/sha1-ce-glue.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/sha1-ce-glue.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,174 @@ ++/* ++ * sha1-ce-glue.c - SHA-1 secure hash using ARMv8 Crypto Extensions ++ * ++ * Copyright (C) 2014 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("SHA1 secure hash using ARMv8 Crypto Extensions"); ++MODULE_AUTHOR("Ard Biesheuvel "); ++MODULE_LICENSE("GPL v2"); ++ ++asmlinkage void sha1_ce_transform(int blocks, u8 const *src, u32 *state, ++ u8 *head, long bytes); ++ ++static int sha1_init(struct shash_desc *desc) ++{ ++ struct sha1_state *sctx = shash_desc_ctx(desc); ++ ++ *sctx = (struct sha1_state){ ++ .state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, ++ }; ++ return 0; ++} ++ ++static int sha1_update(struct shash_desc *desc, const u8 *data, ++ unsigned int len) ++{ ++ struct sha1_state *sctx = shash_desc_ctx(desc); ++ unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; ++ ++ sctx->count += len; ++ ++ if ((partial + len) >= SHA1_BLOCK_SIZE) { ++ int blocks; ++ ++ if (partial) { ++ int p = SHA1_BLOCK_SIZE - partial; ++ ++ memcpy(sctx->buffer + partial, data, p); ++ data += p; ++ len -= p; ++ } ++ ++ blocks = len / SHA1_BLOCK_SIZE; ++ len %= SHA1_BLOCK_SIZE; ++ ++ kernel_neon_begin_partial(16); ++ sha1_ce_transform(blocks, data, sctx->state, ++ partial ? sctx->buffer : NULL, 0); ++ kernel_neon_end(); ++ ++ data += blocks * SHA1_BLOCK_SIZE; ++ partial = 0; ++ } ++ if (len) ++ memcpy(sctx->buffer + partial, data, len); ++ return 0; ++} ++ ++static int sha1_final(struct shash_desc *desc, u8 *out) ++{ ++ static const u8 padding[SHA1_BLOCK_SIZE] = { 0x80, }; ++ ++ struct sha1_state *sctx = shash_desc_ctx(desc); ++ __be64 bits = cpu_to_be64(sctx->count << 3); ++ __be32 *dst = (__be32 *)out; ++ int i; ++ ++ u32 padlen = SHA1_BLOCK_SIZE ++ - ((sctx->count + sizeof(bits)) % SHA1_BLOCK_SIZE); ++ ++ sha1_update(desc, padding, padlen); ++ sha1_update(desc, (const u8 *)&bits, sizeof(bits)); ++ ++ for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) ++ put_unaligned_be32(sctx->state[i], dst++); ++ ++ *sctx = (struct sha1_state){}; ++ return 0; ++} ++ ++static int sha1_finup(struct shash_desc *desc, const u8 *data, ++ unsigned int len, u8 *out) ++{ ++ struct sha1_state *sctx = shash_desc_ctx(desc); ++ __be32 *dst = (__be32 *)out; ++ int blocks; ++ int i; ++ ++ if (sctx->count || !len || (len % SHA1_BLOCK_SIZE)) { ++ sha1_update(desc, data, len); ++ return sha1_final(desc, out); ++ } ++ ++ /* ++ * Use a fast path if the input is a multiple of 64 bytes. In ++ * this case, there is no need to copy data around, and we can ++ * perform the entire digest calculation in a single invocation ++ * of sha1_ce_transform() ++ */ ++ blocks = len / SHA1_BLOCK_SIZE; ++ ++ kernel_neon_begin_partial(16); ++ sha1_ce_transform(blocks, data, sctx->state, NULL, len); ++ kernel_neon_end(); ++ ++ for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) ++ put_unaligned_be32(sctx->state[i], dst++); ++ ++ *sctx = (struct sha1_state){}; ++ return 0; ++} ++ ++static int sha1_export(struct shash_desc *desc, void *out) ++{ ++ struct sha1_state *sctx = shash_desc_ctx(desc); ++ struct sha1_state *dst = out; ++ ++ *dst = *sctx; ++ return 0; ++} ++ ++static int sha1_import(struct shash_desc *desc, const void *in) ++{ ++ struct sha1_state *sctx = shash_desc_ctx(desc); ++ struct sha1_state const *src = in; ++ ++ *sctx = *src; ++ return 0; ++} ++ ++static struct shash_alg alg = { ++ .init = sha1_init, ++ .update = sha1_update, ++ .final = sha1_final, ++ .finup = sha1_finup, ++ .export = sha1_export, ++ .import = sha1_import, ++ .descsize = sizeof(struct sha1_state), ++ .digestsize = SHA1_DIGEST_SIZE, ++ .statesize = sizeof(struct sha1_state), ++ .base = { ++ .cra_name = "sha1", ++ .cra_driver_name = "sha1-ce", ++ .cra_priority = 200, ++ .cra_flags = CRYPTO_ALG_TYPE_SHASH, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_module = THIS_MODULE, ++ } ++}; ++ ++static int __init sha1_ce_mod_init(void) ++{ ++ return crypto_register_shash(&alg); ++} ++ ++static void __exit sha1_ce_mod_fini(void) ++{ ++ crypto_unregister_shash(&alg); ++} ++ ++module_cpu_feature_match(SHA1, sha1_ce_mod_init); ++module_exit(sha1_ce_mod_fini); +diff -Nur linux-3.14.36/arch/arm64/crypto/sha2-ce-core.S linux-openelec/arch/arm64/crypto/sha2-ce-core.S +--- linux-3.14.36/arch/arm64/crypto/sha2-ce-core.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/sha2-ce-core.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,156 @@ ++/* ++ * sha2-ce-core.S - core SHA-224/SHA-256 transform using v8 Crypto Extensions ++ * ++ * Copyright (C) 2014 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++ .text ++ .arch armv8-a+crypto ++ ++ dga .req q20 ++ dgav .req v20 ++ dgb .req q21 ++ dgbv .req v21 ++ ++ t0 .req v22 ++ t1 .req v23 ++ ++ dg0q .req q24 ++ dg0v .req v24 ++ dg1q .req q25 ++ dg1v .req v25 ++ dg2q .req q26 ++ dg2v .req v26 ++ ++ .macro add_only, ev, rc, s0 ++ mov dg2v.16b, dg0v.16b ++ .ifeq \ev ++ add t1.4s, v\s0\().4s, \rc\().4s ++ sha256h dg0q, dg1q, t0.4s ++ sha256h2 dg1q, dg2q, t0.4s ++ .else ++ .ifnb \s0 ++ add t0.4s, v\s0\().4s, \rc\().4s ++ .endif ++ sha256h dg0q, dg1q, t1.4s ++ sha256h2 dg1q, dg2q, t1.4s ++ .endif ++ .endm ++ ++ .macro add_update, ev, rc, s0, s1, s2, s3 ++ sha256su0 v\s0\().4s, v\s1\().4s ++ add_only \ev, \rc, \s1 ++ sha256su1 v\s0\().4s, v\s2\().4s, v\s3\().4s ++ .endm ++ ++ /* ++ * The SHA-256 round constants ++ */ ++ .align 4 ++.Lsha2_rcon: ++ .word 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5 ++ .word 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5 ++ .word 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3 ++ .word 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174 ++ .word 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc ++ .word 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da ++ .word 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7 ++ .word 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967 ++ .word 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13 ++ .word 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85 ++ .word 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3 ++ .word 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070 ++ .word 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5 ++ .word 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3 ++ .word 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208 ++ .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ++ ++ /* ++ * void sha2_ce_transform(int blocks, u8 const *src, u32 *state, ++ * u8 *head, long bytes) ++ */ ++ENTRY(sha2_ce_transform) ++ /* load round constants */ ++ adr x8, .Lsha2_rcon ++ ld1 { v0.4s- v3.4s}, [x8], #64 ++ ld1 { v4.4s- v7.4s}, [x8], #64 ++ ld1 { v8.4s-v11.4s}, [x8], #64 ++ ld1 {v12.4s-v15.4s}, [x8] ++ ++ /* load state */ ++ ldp dga, dgb, [x2] ++ ++ /* load partial input (if supplied) */ ++ cbz x3, 0f ++ ld1 {v16.4s-v19.4s}, [x3] ++ b 1f ++ ++ /* load input */ ++0: ld1 {v16.4s-v19.4s}, [x1], #64 ++ sub w0, w0, #1 ++ ++1: ++CPU_LE( rev32 v16.16b, v16.16b ) ++CPU_LE( rev32 v17.16b, v17.16b ) ++CPU_LE( rev32 v18.16b, v18.16b ) ++CPU_LE( rev32 v19.16b, v19.16b ) ++ ++2: add t0.4s, v16.4s, v0.4s ++ mov dg0v.16b, dgav.16b ++ mov dg1v.16b, dgbv.16b ++ ++ add_update 0, v1, 16, 17, 18, 19 ++ add_update 1, v2, 17, 18, 19, 16 ++ add_update 0, v3, 18, 19, 16, 17 ++ add_update 1, v4, 19, 16, 17, 18 ++ ++ add_update 0, v5, 16, 17, 18, 19 ++ add_update 1, v6, 17, 18, 19, 16 ++ add_update 0, v7, 18, 19, 16, 17 ++ add_update 1, v8, 19, 16, 17, 18 ++ ++ add_update 0, v9, 16, 17, 18, 19 ++ add_update 1, v10, 17, 18, 19, 16 ++ add_update 0, v11, 18, 19, 16, 17 ++ add_update 1, v12, 19, 16, 17, 18 ++ ++ add_only 0, v13, 17 ++ add_only 1, v14, 18 ++ add_only 0, v15, 19 ++ add_only 1 ++ ++ /* update state */ ++ add dgav.4s, dgav.4s, dg0v.4s ++ add dgbv.4s, dgbv.4s, dg1v.4s ++ ++ /* handled all input blocks? */ ++ cbnz w0, 0b ++ ++ /* ++ * Final block: add padding and total bit count. ++ * Skip if we have no total byte count in x4. In that case, the input ++ * size was not a round multiple of the block size, and the padding is ++ * handled by the C code. ++ */ ++ cbz x4, 3f ++ movi v17.2d, #0 ++ mov x8, #0x80000000 ++ movi v18.2d, #0 ++ ror x7, x4, #29 // ror(lsl(x4, 3), 32) ++ fmov d16, x8 ++ mov x4, #0 ++ mov v19.d[0], xzr ++ mov v19.d[1], x7 ++ b 2b ++ ++ /* store new state */ ++3: stp dga, dgb, [x2] ++ ret ++ENDPROC(sha2_ce_transform) +diff -Nur linux-3.14.36/arch/arm64/crypto/sha2-ce-glue.c linux-openelec/arch/arm64/crypto/sha2-ce-glue.c +--- linux-3.14.36/arch/arm64/crypto/sha2-ce-glue.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/crypto/sha2-ce-glue.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,255 @@ ++/* ++ * sha2-ce-glue.c - SHA-224/SHA-256 using ARMv8 Crypto Extensions ++ * ++ * Copyright (C) 2014 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("SHA-224/SHA-256 secure hash using ARMv8 Crypto Extensions"); ++MODULE_AUTHOR("Ard Biesheuvel "); ++MODULE_LICENSE("GPL v2"); ++ ++asmlinkage int sha2_ce_transform(int blocks, u8 const *src, u32 *state, ++ u8 *head, long bytes); ++ ++static int sha224_init(struct shash_desc *desc) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ ++ *sctx = (struct sha256_state){ ++ .state = { ++ SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3, ++ SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7, ++ } ++ }; ++ return 0; ++} ++ ++static int sha256_init(struct shash_desc *desc) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ ++ *sctx = (struct sha256_state){ ++ .state = { ++ SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, ++ SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7, ++ } ++ }; ++ return 0; ++} ++ ++static int sha2_update(struct shash_desc *desc, const u8 *data, ++ unsigned int len) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; ++ ++ sctx->count += len; ++ ++ if ((partial + len) >= SHA256_BLOCK_SIZE) { ++ int blocks; ++ ++ if (partial) { ++ int p = SHA256_BLOCK_SIZE - partial; ++ ++ memcpy(sctx->buf + partial, data, p); ++ data += p; ++ len -= p; ++ } ++ ++ blocks = len / SHA256_BLOCK_SIZE; ++ len %= SHA256_BLOCK_SIZE; ++ ++ kernel_neon_begin_partial(28); ++ sha2_ce_transform(blocks, data, sctx->state, ++ partial ? sctx->buf : NULL, 0); ++ kernel_neon_end(); ++ ++ data += blocks * SHA256_BLOCK_SIZE; ++ partial = 0; ++ } ++ if (len) ++ memcpy(sctx->buf + partial, data, len); ++ return 0; ++} ++ ++static void sha2_final(struct shash_desc *desc) ++{ ++ static const u8 padding[SHA256_BLOCK_SIZE] = { 0x80, }; ++ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ __be64 bits = cpu_to_be64(sctx->count << 3); ++ u32 padlen = SHA256_BLOCK_SIZE ++ - ((sctx->count + sizeof(bits)) % SHA256_BLOCK_SIZE); ++ ++ sha2_update(desc, padding, padlen); ++ sha2_update(desc, (const u8 *)&bits, sizeof(bits)); ++} ++ ++static int sha224_final(struct shash_desc *desc, u8 *out) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ __be32 *dst = (__be32 *)out; ++ int i; ++ ++ sha2_final(desc); ++ ++ for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(__be32); i++) ++ put_unaligned_be32(sctx->state[i], dst++); ++ ++ *sctx = (struct sha256_state){}; ++ return 0; ++} ++ ++static int sha256_final(struct shash_desc *desc, u8 *out) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ __be32 *dst = (__be32 *)out; ++ int i; ++ ++ sha2_final(desc); ++ ++ for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(__be32); i++) ++ put_unaligned_be32(sctx->state[i], dst++); ++ ++ *sctx = (struct sha256_state){}; ++ return 0; ++} ++ ++static void sha2_finup(struct shash_desc *desc, const u8 *data, ++ unsigned int len) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ int blocks; ++ ++ if (sctx->count || !len || (len % SHA256_BLOCK_SIZE)) { ++ sha2_update(desc, data, len); ++ sha2_final(desc); ++ return; ++ } ++ ++ /* ++ * Use a fast path if the input is a multiple of 64 bytes. In ++ * this case, there is no need to copy data around, and we can ++ * perform the entire digest calculation in a single invocation ++ * of sha2_ce_transform() ++ */ ++ blocks = len / SHA256_BLOCK_SIZE; ++ ++ kernel_neon_begin_partial(28); ++ sha2_ce_transform(blocks, data, sctx->state, NULL, len); ++ kernel_neon_end(); ++ data += blocks * SHA256_BLOCK_SIZE; ++} ++ ++static int sha224_finup(struct shash_desc *desc, const u8 *data, ++ unsigned int len, u8 *out) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ __be32 *dst = (__be32 *)out; ++ int i; ++ ++ sha2_finup(desc, data, len); ++ ++ for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(__be32); i++) ++ put_unaligned_be32(sctx->state[i], dst++); ++ ++ *sctx = (struct sha256_state){}; ++ return 0; ++} ++ ++static int sha256_finup(struct shash_desc *desc, const u8 *data, ++ unsigned int len, u8 *out) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ __be32 *dst = (__be32 *)out; ++ int i; ++ ++ sha2_finup(desc, data, len); ++ ++ for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(__be32); i++) ++ put_unaligned_be32(sctx->state[i], dst++); ++ ++ *sctx = (struct sha256_state){}; ++ return 0; ++} ++ ++static int sha2_export(struct shash_desc *desc, void *out) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ struct sha256_state *dst = out; ++ ++ *dst = *sctx; ++ return 0; ++} ++ ++static int sha2_import(struct shash_desc *desc, const void *in) ++{ ++ struct sha256_state *sctx = shash_desc_ctx(desc); ++ struct sha256_state const *src = in; ++ ++ *sctx = *src; ++ return 0; ++} ++ ++static struct shash_alg algs[] = { { ++ .init = sha224_init, ++ .update = sha2_update, ++ .final = sha224_final, ++ .finup = sha224_finup, ++ .export = sha2_export, ++ .import = sha2_import, ++ .descsize = sizeof(struct sha256_state), ++ .digestsize = SHA224_DIGEST_SIZE, ++ .statesize = sizeof(struct sha256_state), ++ .base = { ++ .cra_name = "sha224", ++ .cra_driver_name = "sha224-ce", ++ .cra_priority = 200, ++ .cra_flags = CRYPTO_ALG_TYPE_SHASH, ++ .cra_blocksize = SHA256_BLOCK_SIZE, ++ .cra_module = THIS_MODULE, ++ } ++}, { ++ .init = sha256_init, ++ .update = sha2_update, ++ .final = sha256_final, ++ .finup = sha256_finup, ++ .export = sha2_export, ++ .import = sha2_import, ++ .descsize = sizeof(struct sha256_state), ++ .digestsize = SHA256_DIGEST_SIZE, ++ .statesize = sizeof(struct sha256_state), ++ .base = { ++ .cra_name = "sha256", ++ .cra_driver_name = "sha256-ce", ++ .cra_priority = 200, ++ .cra_flags = CRYPTO_ALG_TYPE_SHASH, ++ .cra_blocksize = SHA256_BLOCK_SIZE, ++ .cra_module = THIS_MODULE, ++ } ++} }; ++ ++static int __init sha2_ce_mod_init(void) ++{ ++ return crypto_register_shashes(algs, ARRAY_SIZE(algs)); ++} ++ ++static void __exit sha2_ce_mod_fini(void) ++{ ++ crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); ++} ++ ++module_cpu_feature_match(SHA2, sha2_ce_mod_init); ++module_exit(sha2_ce_mod_fini); +diff -Nur linux-3.14.36/arch/arm64/include/asm/bL_switcher.h linux-openelec/arch/arm64/include/asm/bL_switcher.h +--- linux-3.14.36/arch/arm64/include/asm/bL_switcher.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/include/asm/bL_switcher.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,54 @@ ++/* ++ * Based on the stubs for the ARM implementation which is: ++ * ++ * Created by: Nicolas Pitre, April 2012 ++ * Copyright: (C) 2012-2013 Linaro Limited ++ * ++ * 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 ASM_BL_SWITCHER_H ++#define ASM_BL_SWITCHER_H ++ ++#include ++#include ++ ++typedef void (*bL_switch_completion_handler)(void *cookie); ++ ++static inline int bL_switch_request(unsigned int cpu, ++ unsigned int new_cluster_id) ++{ ++ return -ENOTSUPP; ++} ++ ++/* ++ * Register here to be notified about runtime enabling/disabling of ++ * the switcher. ++ * ++ * The notifier chain is called with the switcher activation lock held: ++ * the switcher will not be enabled or disabled during callbacks. ++ * Callbacks must not call bL_switcher_{get,put}_enabled(). ++ */ ++#define BL_NOTIFY_PRE_ENABLE 0 ++#define BL_NOTIFY_POST_ENABLE 1 ++#define BL_NOTIFY_PRE_DISABLE 2 ++#define BL_NOTIFY_POST_DISABLE 3 ++ ++static inline int bL_switcher_register_notifier(struct notifier_block *nb) ++{ ++ return 0; ++} ++ ++static inline int bL_switcher_unregister_notifier(struct notifier_block *nb) ++{ ++ return 0; ++} ++ ++static inline bool bL_switcher_get_enabled(void) { return false; } ++static inline void bL_switcher_put_enabled(void) { } ++static inline int bL_switcher_trace_trigger(void) { return 0; } ++static inline int bL_switcher_get_logical_index(u32 mpidr) { return -EUNATCH; } ++ ++#endif +diff -Nur linux-3.14.36/arch/arm64/include/asm/cacheflush.h linux-openelec/arch/arm64/include/asm/cacheflush.h +--- linux-3.14.36/arch/arm64/include/asm/cacheflush.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/cacheflush.h 2015-05-06 12:05:43.000000000 -0500 +@@ -85,6 +85,13 @@ + } + + /* ++ * Cache maintenance functions used by the DMA API. No to be used directly. ++ */ ++extern void __dma_map_area(const void *, size_t, int); ++extern void __dma_unmap_area(const void *, size_t, int); ++extern void __dma_flush_range(const void *, const void *); ++ ++/* + * Copy user data from/to a page which is mapped into a different + * processes address space. Really, we want to allow our "user + * space" model to handle this. +diff -Nur linux-3.14.36/arch/arm64/include/asm/compat.h linux-openelec/arch/arm64/include/asm/compat.h +--- linux-3.14.36/arch/arm64/include/asm/compat.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/compat.h 2015-07-24 18:03:28.688842002 -0500 +@@ -228,7 +228,7 @@ + return (u32)(unsigned long)uptr; + } + +-#define compat_user_stack_pointer() (current_pt_regs()->compat_sp) ++#define compat_user_stack_pointer() (user_stack_pointer(current_pt_regs())) + + static inline void __user *arch_compat_alloc_user_space(long len) + { +@@ -305,11 +305,6 @@ + + #else /* !CONFIG_COMPAT */ + +-static inline int is_compat_task(void) +-{ +- return 0; +-} +- + static inline int is_compat_thread(struct thread_info *thread) + { + return 0; +diff -Nur linux-3.14.36/arch/arm64/include/asm/cpufeature.h linux-openelec/arch/arm64/include/asm/cpufeature.h +--- linux-3.14.36/arch/arm64/include/asm/cpufeature.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/include/asm/cpufeature.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,29 @@ ++/* ++ * Copyright (C) 2014 Linaro Ltd. ++ * ++ * 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 __ASM_CPUFEATURE_H ++#define __ASM_CPUFEATURE_H ++ ++#include ++ ++/* ++ * In the arm64 world (as in the ARM world), elf_hwcap is used both internally ++ * in the kernel and for user space to keep track of which optional features ++ * are supported by the current system. So let's map feature 'x' to HWCAP_x. ++ * Note that HWCAP_x constants are bit fields so we need to take the log. ++ */ ++ ++#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap)) ++#define cpu_feature(x) ilog2(HWCAP_ ## x) ++ ++static inline bool cpu_have_feature(unsigned int num) ++{ ++ return elf_hwcap & (1UL << num); ++} ++ ++#endif +diff -Nur linux-3.14.36/arch/arm64/include/asm/debug-monitors.h linux-openelec/arch/arm64/include/asm/debug-monitors.h +--- linux-3.14.36/arch/arm64/include/asm/debug-monitors.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/debug-monitors.h 2015-05-06 12:05:43.000000000 -0500 +@@ -26,6 +26,53 @@ + #define DBG_ESR_EVT_HWWP 0x2 + #define DBG_ESR_EVT_BRK 0x6 + ++/* ++ * Break point instruction encoding ++ */ ++#define BREAK_INSTR_SIZE 4 ++ ++/* ++ * ESR values expected for dynamic and compile time BRK instruction ++ */ ++#define DBG_ESR_VAL_BRK(x) (0xf2000000 | ((x) & 0xfffff)) ++ ++/* ++ * #imm16 values used for BRK instruction generation ++ * Allowed values for kgbd are 0x400 - 0x7ff ++ * 0x400: for dynamic BRK instruction ++ * 0x401: for compile time BRK instruction ++ */ ++#define KGDB_DYN_DGB_BRK_IMM 0x400 ++#define KDBG_COMPILED_DBG_BRK_IMM 0x401 ++ ++/* ++ * BRK instruction encoding ++ * The #imm16 value should be placed at bits[20:5] within BRK ins ++ */ ++#define AARCH64_BREAK_MON 0xd4200000 ++ ++/* ++ * Extract byte from BRK instruction ++ */ ++#define KGDB_DYN_DGB_BRK_INS_BYTE(x) \ ++ ((((AARCH64_BREAK_MON) & 0xffe0001f) >> (x * 8)) & 0xff) ++ ++/* ++ * Extract byte from BRK #imm16 ++ */ ++#define KGBD_DYN_DGB_BRK_IMM_BYTE(x) \ ++ (((((KGDB_DYN_DGB_BRK_IMM) & 0xffff) << 5) >> (x * 8)) & 0xff) ++ ++#define KGDB_DYN_DGB_BRK_BYTE(x) \ ++ (KGDB_DYN_DGB_BRK_INS_BYTE(x) | KGBD_DYN_DGB_BRK_IMM_BYTE(x)) ++ ++#define KGDB_DYN_BRK_INS_BYTE0 KGDB_DYN_DGB_BRK_BYTE(0) ++#define KGDB_DYN_BRK_INS_BYTE1 KGDB_DYN_DGB_BRK_BYTE(1) ++#define KGDB_DYN_BRK_INS_BYTE2 KGDB_DYN_DGB_BRK_BYTE(2) ++#define KGDB_DYN_BRK_INS_BYTE3 KGDB_DYN_DGB_BRK_BYTE(3) ++ ++#define CACHE_FLUSH_IS_SAFE 1 ++ + enum debug_el { + DBG_ACTIVE_EL0 = 0, + DBG_ACTIVE_EL1, +@@ -43,23 +90,6 @@ + #ifndef __ASSEMBLY__ + struct task_struct; + +-#define local_dbg_save(flags) \ +- do { \ +- typecheck(unsigned long, flags); \ +- asm volatile( \ +- "mrs %0, daif // local_dbg_save\n" \ +- "msr daifset, #8" \ +- : "=r" (flags) : : "memory"); \ +- } while (0) +- +-#define local_dbg_restore(flags) \ +- do { \ +- typecheck(unsigned long, flags); \ +- asm volatile( \ +- "msr daif, %0 // local_dbg_restore\n" \ +- : : "r" (flags) : "memory"); \ +- } while (0) +- + #define DBG_ARCH_ID_RESERVED 0 /* In case of ptrace ABI updates. */ + + #define DBG_HOOK_HANDLED 0 +diff -Nur linux-3.14.36/arch/arm64/include/asm/dma-mapping.h linux-openelec/arch/arm64/include/asm/dma-mapping.h +--- linux-3.14.36/arch/arm64/include/asm/dma-mapping.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/dma-mapping.h 2015-05-06 12:05:43.000000000 -0500 +@@ -28,6 +28,8 @@ + + #define DMA_ERROR_CODE (~(dma_addr_t)0) + extern struct dma_map_ops *dma_ops; ++extern struct dma_map_ops coherent_swiotlb_dma_ops; ++extern struct dma_map_ops noncoherent_swiotlb_dma_ops; + + static inline struct dma_map_ops *__generic_dma_ops(struct device *dev) + { +@@ -45,6 +47,11 @@ + return __generic_dma_ops(dev); + } + ++static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) ++{ ++ dev->archdata.dma_ops = ops; ++} ++ + #include + + static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) +diff -Nur linux-3.14.36/arch/arm64/include/asm/ftrace.h linux-openelec/arch/arm64/include/asm/ftrace.h +--- linux-3.14.36/arch/arm64/include/asm/ftrace.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/include/asm/ftrace.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,59 @@ ++/* ++ * arch/arm64/include/asm/ftrace.h ++ * ++ * Copyright (C) 2013 Linaro Limited ++ * Author: AKASHI Takahiro ++ * ++ * 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 __ASM_FTRACE_H ++#define __ASM_FTRACE_H ++ ++#include ++ ++#define MCOUNT_ADDR ((unsigned long)_mcount) ++#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE ++ ++#ifndef __ASSEMBLY__ ++#include ++ ++extern void _mcount(unsigned long); ++extern void *return_address(unsigned int); ++ ++struct dyn_arch_ftrace { ++ /* No extra data needed for arm64 */ ++}; ++ ++extern unsigned long ftrace_graph_call; ++ ++static inline unsigned long ftrace_call_adjust(unsigned long addr) ++{ ++ /* ++ * addr is the address of the mcount call instruction. ++ * recordmcount does the necessary offset calculation. ++ */ ++ return addr; ++} ++ ++#define ftrace_return_address(n) return_address(n) ++ ++/* ++ * Because AArch32 mode does not share the same syscall table with AArch64, ++ * tracing compat syscalls may result in reporting bogus syscalls or even ++ * hang-up, so just do not trace them. ++ * See kernel/trace/trace_syscalls.c ++ * ++ * x86 code says: ++ * If the user realy wants these, then they should use the ++ * raw syscall tracepoints with filtering. ++ */ ++#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS ++static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) ++{ ++ return is_compat_task(); ++} ++#endif /* ifndef __ASSEMBLY__ */ ++ ++#endif /* __ASM_FTRACE_H */ +diff -Nur linux-3.14.36/arch/arm64/include/asm/hwcap.h linux-openelec/arch/arm64/include/asm/hwcap.h +--- linux-3.14.36/arch/arm64/include/asm/hwcap.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/hwcap.h 2015-07-24 18:03:29.256842002 -0500 +@@ -33,6 +33,12 @@ + #define COMPAT_HWCAP_LPAE (1 << 20) + #define COMPAT_HWCAP_EVTSTRM (1 << 21) + ++#define COMPAT_HWCAP2_AES (1 << 0) ++#define COMPAT_HWCAP2_PMULL (1 << 1) ++#define COMPAT_HWCAP2_SHA1 (1 << 2) ++#define COMPAT_HWCAP2_SHA2 (1 << 3) ++#define COMPAT_HWCAP2_CRC32 (1 << 4) ++ + #ifndef __ASSEMBLY__ + /* + * This yields a mask that user programs can use to figure out what +@@ -42,7 +48,8 @@ + + #ifdef CONFIG_COMPAT + #define COMPAT_ELF_HWCAP (compat_elf_hwcap) +-extern unsigned int compat_elf_hwcap; ++#define COMPAT_ELF_HWCAP2 (compat_elf_hwcap2) ++extern unsigned int compat_elf_hwcap, compat_elf_hwcap2; + #endif + + extern unsigned long elf_hwcap; +diff -Nur linux-3.14.36/arch/arm64/include/asm/hwcap.h.orig linux-openelec/arch/arm64/include/asm/hwcap.h.orig +--- linux-3.14.36/arch/arm64/include/asm/hwcap.h.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/include/asm/hwcap.h.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (C) 2012 ARM Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++#ifndef __ASM_HWCAP_H ++#define __ASM_HWCAP_H ++ ++#include ++ ++#define COMPAT_HWCAP_HALF (1 << 1) ++#define COMPAT_HWCAP_THUMB (1 << 2) ++#define COMPAT_HWCAP_FAST_MULT (1 << 4) ++#define COMPAT_HWCAP_VFP (1 << 6) ++#define COMPAT_HWCAP_EDSP (1 << 7) ++#define COMPAT_HWCAP_NEON (1 << 12) ++#define COMPAT_HWCAP_VFPv3 (1 << 13) ++#define COMPAT_HWCAP_TLS (1 << 15) ++#define COMPAT_HWCAP_VFPv4 (1 << 16) ++#define COMPAT_HWCAP_IDIVA (1 << 17) ++#define COMPAT_HWCAP_IDIVT (1 << 18) ++#define COMPAT_HWCAP_IDIV (COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT) ++#define COMPAT_HWCAP_EVTSTRM (1 << 21) ++ ++#define COMPAT_HWCAP2_AES (1 << 0) ++#define COMPAT_HWCAP2_PMULL (1 << 1) ++#define COMPAT_HWCAP2_SHA1 (1 << 2) ++#define COMPAT_HWCAP2_SHA2 (1 << 3) ++#define COMPAT_HWCAP2_CRC32 (1 << 4) ++ ++#ifndef __ASSEMBLY__ ++/* ++ * This yields a mask that user programs can use to figure out what ++ * instruction set this cpu supports. ++ */ ++#define ELF_HWCAP (elf_hwcap) ++ ++#ifdef CONFIG_COMPAT ++#define COMPAT_ELF_HWCAP (compat_elf_hwcap) ++#define COMPAT_ELF_HWCAP2 (compat_elf_hwcap2) ++extern unsigned int compat_elf_hwcap, compat_elf_hwcap2; ++#endif ++ ++extern unsigned long elf_hwcap; ++#endif ++#endif +diff -Nur linux-3.14.36/arch/arm64/include/asm/insn.h linux-openelec/arch/arm64/include/asm/insn.h +--- linux-3.14.36/arch/arm64/include/asm/insn.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/insn.h 2015-05-06 12:05:43.000000000 -0500 +@@ -16,11 +16,14 @@ + */ + #ifndef __ASM_INSN_H + #define __ASM_INSN_H ++ + #include + + /* A64 instructions are always 32 bits. */ + #define AARCH64_INSN_SIZE 4 + ++#ifndef __ASSEMBLY__ ++ + /* + * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a + * Section C3.1 "A64 instruction index by encoding": +@@ -105,4 +108,6 @@ + int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); + int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); + ++#endif /* __ASSEMBLY__ */ ++ + #endif /* __ASM_INSN_H */ +diff -Nur linux-3.14.36/arch/arm64/include/asm/irqflags.h linux-openelec/arch/arm64/include/asm/irqflags.h +--- linux-3.14.36/arch/arm64/include/asm/irqflags.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/irqflags.h 2015-05-06 12:05:43.000000000 -0500 +@@ -90,5 +90,28 @@ + return flags & PSR_I_BIT; + } + ++/* ++ * save and restore debug state ++ */ ++#define local_dbg_save(flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ asm volatile( \ ++ "mrs %0, daif // local_dbg_save\n" \ ++ "msr daifset, #8" \ ++ : "=r" (flags) : : "memory"); \ ++ } while (0) ++ ++#define local_dbg_restore(flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ asm volatile( \ ++ "msr daif, %0 // local_dbg_restore\n" \ ++ : : "r" (flags) : "memory"); \ ++ } while (0) ++ ++#define local_dbg_enable() asm("msr daifclr, #8" : : : "memory") ++#define local_dbg_disable() asm("msr daifset, #8" : : : "memory") ++ + #endif + #endif +diff -Nur linux-3.14.36/arch/arm64/include/asm/Kbuild linux-openelec/arch/arm64/include/asm/Kbuild +--- linux-3.14.36/arch/arm64/include/asm/Kbuild 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/Kbuild 2015-05-06 12:05:43.000000000 -0500 +@@ -35,6 +35,7 @@ + generic-y += sembuf.h + generic-y += serial.h + generic-y += shmbuf.h ++generic-y += simd.h + generic-y += sizes.h + generic-y += socket.h + generic-y += sockios.h +diff -Nur linux-3.14.36/arch/arm64/include/asm/kgdb.h linux-openelec/arch/arm64/include/asm/kgdb.h +--- linux-3.14.36/arch/arm64/include/asm/kgdb.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/include/asm/kgdb.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,84 @@ ++/* ++ * AArch64 KGDB support ++ * ++ * Based on arch/arm/include/kgdb.h ++ * ++ * Copyright (C) 2013 Cavium Inc. ++ * Author: Vijaya Kumar K ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#ifndef __ARM_KGDB_H ++#define __ARM_KGDB_H ++ ++#include ++#include ++ ++#ifndef __ASSEMBLY__ ++ ++static inline void arch_kgdb_breakpoint(void) ++{ ++ asm ("brk %0" : : "I" (KDBG_COMPILED_DBG_BRK_IMM)); ++} ++ ++extern void kgdb_handle_bus_error(void); ++extern int kgdb_fault_expected; ++ ++#endif /* !__ASSEMBLY__ */ ++ ++/* ++ * gdb is expecting the following registers layout. ++ * ++ * General purpose regs: ++ * r0-r30: 64 bit ++ * sp,pc : 64 bit ++ * pstate : 64 bit ++ * Total: 34 ++ * FPU regs: ++ * f0-f31: 128 bit ++ * Total: 32 ++ * Extra regs ++ * fpsr & fpcr: 32 bit ++ * Total: 2 ++ * ++ */ ++ ++#define _GP_REGS 34 ++#define _FP_REGS 32 ++#define _EXTRA_REGS 2 ++/* ++ * general purpose registers size in bytes. ++ * pstate is only 4 bytes. subtract 4 bytes ++ */ ++#define GP_REG_BYTES (_GP_REGS * 8) ++#define DBG_MAX_REG_NUM (_GP_REGS + _FP_REGS + _EXTRA_REGS) ++ ++/* ++ * Size of I/O buffer for gdb packet. ++ * considering to hold all register contents, size is set ++ */ ++ ++#define BUFMAX 2048 ++ ++/* ++ * Number of bytes required for gdb_regs buffer. ++ * _GP_REGS: 8 bytes, _FP_REGS: 16 bytes and _EXTRA_REGS: 4 bytes each ++ * GDB fails to connect for size beyond this with error ++ * "'g' packet reply is too long" ++ */ ++ ++#define NUMREGBYTES ((_GP_REGS * 8) + (_FP_REGS * 16) + \ ++ (_EXTRA_REGS * 4)) ++ ++#endif /* __ASM_KGDB_H */ +diff -Nur linux-3.14.36/arch/arm64/include/asm/page.h linux-openelec/arch/arm64/include/asm/page.h +--- linux-3.14.36/arch/arm64/include/asm/page.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/page.h 2015-05-06 12:05:43.000000000 -0500 +@@ -31,6 +31,15 @@ + /* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */ + #define __HAVE_ARCH_GATE_AREA 1 + ++/* ++ * The idmap and swapper page tables need some space reserved in the kernel ++ * image. The idmap only requires a pgd and a next level table to (section) map ++ * the kernel, while the swapper also maps the FDT and requires an additional ++ * table to map an early UART. See __create_page_tables for more information. ++ */ ++#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE) ++#define IDMAP_DIR_SIZE (2 * PAGE_SIZE) ++ + #ifndef __ASSEMBLY__ + + #ifdef CONFIG_ARM64_64K_PAGES +diff -Nur linux-3.14.36/arch/arm64/include/asm/pgtable.h linux-openelec/arch/arm64/include/asm/pgtable.h +--- linux-3.14.36/arch/arm64/include/asm/pgtable.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/pgtable.h 2015-05-06 12:05:43.000000000 -0500 +@@ -227,36 +227,36 @@ + + #define __HAVE_ARCH_PTE_SPECIAL + +-/* +- * Software PMD bits for THP +- */ ++static inline pte_t pmd_pte(pmd_t pmd) ++{ ++ return __pte(pmd_val(pmd)); ++} + +-#define PMD_SECT_DIRTY (_AT(pmdval_t, 1) << 55) +-#define PMD_SECT_SPLITTING (_AT(pmdval_t, 1) << 57) ++static inline pmd_t pte_pmd(pte_t pte) ++{ ++ return __pmd(pte_val(pte)); ++} + + /* + * THP definitions. + */ +-#define pmd_young(pmd) (pmd_val(pmd) & PMD_SECT_AF) +- +-#define __HAVE_ARCH_PMD_WRITE +-#define pmd_write(pmd) (!(pmd_val(pmd) & PMD_SECT_RDONLY)) + + #ifdef CONFIG_TRANSPARENT_HUGEPAGE + #define pmd_trans_huge(pmd) (pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT)) +-#define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING) ++#define pmd_trans_splitting(pmd) pte_special(pmd_pte(pmd)) + #endif + +-#define PMD_BIT_FUNC(fn,op) \ +-static inline pmd_t pmd_##fn(pmd_t pmd) { pmd_val(pmd) op; return pmd; } ++#define pmd_young(pmd) pte_young(pmd_pte(pmd)) ++#define pmd_wrprotect(pmd) pte_pmd(pte_wrprotect(pmd_pte(pmd))) ++#define pmd_mksplitting(pmd) pte_pmd(pte_mkspecial(pmd_pte(pmd))) ++#define pmd_mkold(pmd) pte_pmd(pte_mkold(pmd_pte(pmd))) ++#define pmd_mkwrite(pmd) pte_pmd(pte_mkwrite(pmd_pte(pmd))) ++#define pmd_mkdirty(pmd) pte_pmd(pte_mkdirty(pmd_pte(pmd))) ++#define pmd_mkyoung(pmd) pte_pmd(pte_mkyoung(pmd_pte(pmd))) ++#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) &= ~PMD_TYPE_MASK)) + +-PMD_BIT_FUNC(wrprotect, |= PMD_SECT_RDONLY); +-PMD_BIT_FUNC(mkold, &= ~PMD_SECT_AF); +-PMD_BIT_FUNC(mksplitting, |= PMD_SECT_SPLITTING); +-PMD_BIT_FUNC(mkwrite, &= ~PMD_SECT_RDONLY); +-PMD_BIT_FUNC(mkdirty, |= PMD_SECT_DIRTY); +-PMD_BIT_FUNC(mkyoung, |= PMD_SECT_AF); +-PMD_BIT_FUNC(mknotpresent, &= ~PMD_TYPE_MASK); ++#define __HAVE_ARCH_PMD_WRITE ++#define pmd_write(pmd) pte_write(pmd_pte(pmd)) + + #define pmd_mkhuge(pmd) (__pmd(pmd_val(pmd) & ~PMD_TABLE_BIT)) + +@@ -266,16 +266,7 @@ + + #define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK)) + +-static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) +-{ +- const pmdval_t mask = PMD_SECT_USER | PMD_SECT_PXN | PMD_SECT_UXN | +- PMD_SECT_RDONLY | PMD_SECT_PROT_NONE | +- PMD_SECT_VALID; +- pmd_val(pmd) = (pmd_val(pmd) & ~mask) | (pgprot_val(newprot) & mask); +- return pmd; +-} +- +-#define set_pmd_at(mm, addr, pmdp, pmd) set_pmd(pmdp, pmd) ++#define set_pmd_at(mm, addr, pmdp, pmd) set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd)) + + static inline int has_transparent_hugepage(void) + { +@@ -383,12 +374,14 @@ + return pte; + } + ++static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) ++{ ++ return pte_pmd(pte_modify(pmd_pte(pmd), newprot)); ++} ++ + extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; + extern pgd_t idmap_pg_dir[PTRS_PER_PGD]; + +-#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE) +-#define IDMAP_DIR_SIZE (2 * PAGE_SIZE) +- + /* + * Encode and decode a swap entry: + * bits 0-1: present (must be zero) +diff -Nur linux-3.14.36/arch/arm64/include/asm/ptrace.h linux-openelec/arch/arm64/include/asm/ptrace.h +--- linux-3.14.36/arch/arm64/include/asm/ptrace.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/ptrace.h 2015-05-06 12:05:43.000000000 -0500 +@@ -68,6 +68,7 @@ + + /* Architecturally defined mapping between AArch32 and AArch64 registers */ + #define compat_usr(x) regs[(x)] ++#define compat_fp regs[11] + #define compat_sp regs[13] + #define compat_lr regs[14] + #define compat_sp_hyp regs[15] +@@ -132,7 +133,12 @@ + (!((regs)->pstate & PSR_F_BIT)) + + #define user_stack_pointer(regs) \ +- ((regs)->sp) ++ (!compat_user_mode(regs)) ? ((regs)->sp) : ((regs)->compat_sp) ++ ++static inline unsigned long regs_return_value(struct pt_regs *regs) ++{ ++ return regs->regs[0]; ++} + + /* + * Are the current registers suitable for user mode? (used to maintain +@@ -164,7 +170,7 @@ + return 0; + } + +-#define instruction_pointer(regs) (regs)->pc ++#define instruction_pointer(regs) ((unsigned long)(regs)->pc) + + #ifdef CONFIG_SMP + extern unsigned long profile_pc(struct pt_regs *regs); +diff -Nur linux-3.14.36/arch/arm64/include/asm/syscall.h linux-openelec/arch/arm64/include/asm/syscall.h +--- linux-3.14.36/arch/arm64/include/asm/syscall.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/syscall.h 2015-05-06 12:05:43.000000000 -0500 +@@ -18,6 +18,7 @@ + + #include + ++extern const void *sys_call_table[]; + + static inline int syscall_get_nr(struct task_struct *task, + struct pt_regs *regs) +diff -Nur linux-3.14.36/arch/arm64/include/asm/thread_info.h linux-openelec/arch/arm64/include/asm/thread_info.h +--- linux-3.14.36/arch/arm64/include/asm/thread_info.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/thread_info.h 2015-05-06 12:05:43.000000000 -0500 +@@ -91,6 +91,9 @@ + /* + * thread information flags: + * TIF_SYSCALL_TRACE - syscall trace active ++ * TIF_SYSCALL_TRACEPOINT - syscall tracepoint for ftrace ++ * TIF_SYSCALL_AUDIT - syscall auditing ++ * TIF_SECOMP - syscall secure computing + * TIF_SIGPENDING - signal pending + * TIF_NEED_RESCHED - rescheduling necessary + * TIF_NOTIFY_RESUME - callback before returning to user +@@ -101,6 +104,9 @@ + #define TIF_NEED_RESCHED 1 + #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ + #define TIF_SYSCALL_TRACE 8 ++#define TIF_SYSCALL_AUDIT 9 ++#define TIF_SYSCALL_TRACEPOINT 10 ++#define TIF_SECCOMP 11 + #define TIF_POLLING_NRFLAG 16 + #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ + #define TIF_FREEZE 19 +@@ -112,10 +118,17 @@ + #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) + #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) + #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) ++#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) ++#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) ++#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) ++#define _TIF_SECCOMP (1 << TIF_SECCOMP) + #define _TIF_32BIT (1 << TIF_32BIT) + + #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ + _TIF_NOTIFY_RESUME) + ++#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ ++ _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP) ++ + #endif /* __KERNEL__ */ + #endif /* __ASM_THREAD_INFO_H */ +diff -Nur linux-3.14.36/arch/arm64/include/asm/topology.h linux-openelec/arch/arm64/include/asm/topology.h +--- linux-3.14.36/arch/arm64/include/asm/topology.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/include/asm/topology.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,70 @@ ++#ifndef __ASM_TOPOLOGY_H ++#define __ASM_TOPOLOGY_H ++ ++#ifdef CONFIG_SMP ++ ++#include ++ ++struct cpu_topology { ++ int thread_id; ++ int core_id; ++ int cluster_id; ++ cpumask_t thread_sibling; ++ cpumask_t core_sibling; ++}; ++ ++extern struct cpu_topology cpu_topology[NR_CPUS]; ++ ++#define topology_physical_package_id(cpu) (cpu_topology[cpu].cluster_id) ++#define topology_core_id(cpu) (cpu_topology[cpu].core_id) ++#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling) ++#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling) ++ ++#define mc_capable() (cpu_topology[0].cluster_id != -1) ++#define smt_capable() (cpu_topology[0].thread_id != -1) ++ ++void init_cpu_topology(void); ++void store_cpu_topology(unsigned int cpuid); ++const struct cpumask *cpu_coregroup_mask(int cpu); ++ ++#ifdef CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE ++/* Common values for CPUs */ ++#ifndef SD_CPU_INIT ++#define SD_CPU_INIT (struct sched_domain) { \ ++ .min_interval = 1, \ ++ .max_interval = 4, \ ++ .busy_factor = 64, \ ++ .imbalance_pct = 125, \ ++ .cache_nice_tries = 1, \ ++ .busy_idx = 2, \ ++ .idle_idx = 1, \ ++ .newidle_idx = 0, \ ++ .wake_idx = 0, \ ++ .forkexec_idx = 0, \ ++ \ ++ .flags = 0*SD_LOAD_BALANCE \ ++ | 1*SD_BALANCE_NEWIDLE \ ++ | 1*SD_BALANCE_EXEC \ ++ | 1*SD_BALANCE_FORK \ ++ | 0*SD_BALANCE_WAKE \ ++ | 1*SD_WAKE_AFFINE \ ++ | 0*SD_SHARE_CPUPOWER \ ++ | 0*SD_SHARE_PKG_RESOURCES \ ++ | 0*SD_SERIALIZE \ ++ , \ ++ .last_balance = jiffies, \ ++ .balance_interval = 1, \ ++} ++#endif ++#endif /* CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE */ ++ ++#else ++ ++static inline void init_cpu_topology(void) { } ++static inline void store_cpu_topology(unsigned int cpuid) { } ++ ++#endif ++ ++#include ++ ++#endif /* _ASM_ARM_TOPOLOGY_H */ +diff -Nur linux-3.14.36/arch/arm64/include/asm/unistd.h linux-openelec/arch/arm64/include/asm/unistd.h +--- linux-3.14.36/arch/arm64/include/asm/unistd.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/asm/unistd.h 2015-05-06 12:05:43.000000000 -0500 +@@ -28,3 +28,5 @@ + #endif + #define __ARCH_WANT_SYS_CLONE + #include ++ ++#define NR_syscalls (__NR_syscalls) +diff -Nur linux-3.14.36/arch/arm64/include/uapi/asm/Kbuild linux-openelec/arch/arm64/include/uapi/asm/Kbuild +--- linux-3.14.36/arch/arm64/include/uapi/asm/Kbuild 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/include/uapi/asm/Kbuild 2015-05-06 12:05:43.000000000 -0500 +@@ -9,6 +9,7 @@ + header-y += fcntl.h + header-y += hwcap.h + header-y += kvm_para.h ++header-y += perf_regs.h + header-y += param.h + header-y += ptrace.h + header-y += setup.h +diff -Nur linux-3.14.36/arch/arm64/include/uapi/asm/perf_regs.h linux-openelec/arch/arm64/include/uapi/asm/perf_regs.h +--- linux-3.14.36/arch/arm64/include/uapi/asm/perf_regs.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/include/uapi/asm/perf_regs.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,40 @@ ++#ifndef _ASM_ARM64_PERF_REGS_H ++#define _ASM_ARM64_PERF_REGS_H ++ ++enum perf_event_arm_regs { ++ PERF_REG_ARM64_X0, ++ PERF_REG_ARM64_X1, ++ PERF_REG_ARM64_X2, ++ PERF_REG_ARM64_X3, ++ PERF_REG_ARM64_X4, ++ PERF_REG_ARM64_X5, ++ PERF_REG_ARM64_X6, ++ PERF_REG_ARM64_X7, ++ PERF_REG_ARM64_X8, ++ PERF_REG_ARM64_X9, ++ PERF_REG_ARM64_X10, ++ PERF_REG_ARM64_X11, ++ PERF_REG_ARM64_X12, ++ PERF_REG_ARM64_X13, ++ PERF_REG_ARM64_X14, ++ PERF_REG_ARM64_X15, ++ PERF_REG_ARM64_X16, ++ PERF_REG_ARM64_X17, ++ PERF_REG_ARM64_X18, ++ PERF_REG_ARM64_X19, ++ PERF_REG_ARM64_X20, ++ PERF_REG_ARM64_X21, ++ PERF_REG_ARM64_X22, ++ PERF_REG_ARM64_X23, ++ PERF_REG_ARM64_X24, ++ PERF_REG_ARM64_X25, ++ PERF_REG_ARM64_X26, ++ PERF_REG_ARM64_X27, ++ PERF_REG_ARM64_X28, ++ PERF_REG_ARM64_X29, ++ PERF_REG_ARM64_LR, ++ PERF_REG_ARM64_SP, ++ PERF_REG_ARM64_PC, ++ PERF_REG_ARM64_MAX, ++}; ++#endif /* _ASM_ARM64_PERF_REGS_H */ +diff -Nur linux-3.14.36/arch/arm64/Kconfig linux-openelec/arch/arm64/Kconfig +--- linux-3.14.36/arch/arm64/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/Kconfig 2015-05-06 12:05:43.000000000 -0500 +@@ -4,6 +4,7 @@ + select ARCH_USE_CMPXCHG_LOCKREF + select ARCH_SUPPORTS_ATOMIC_RMW + select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST ++ select ARCH_HAS_OPP + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_WANT_COMPAT_IPC_PARSE_VERSION + select ARCH_WANT_FRAME_POINTERS +@@ -17,6 +18,7 @@ + select DCACHE_WORD_ACCESS + select GENERIC_CLOCKEVENTS + select GENERIC_CLOCKEVENTS_BROADCAST if SMP ++ select GENERIC_CPU_AUTOPROBE + select GENERIC_IOMAP + select GENERIC_IRQ_PROBE + select GENERIC_IRQ_SHOW +@@ -27,18 +29,27 @@ + select GENERIC_TIME_VSYSCALL + select HARDIRQS_SW_RESEND + select HAVE_ARCH_JUMP_LABEL ++ select HAVE_ARCH_KGDB + select HAVE_ARCH_TRACEHOOK ++ select HAVE_C_RECORDMCOUNT + select HAVE_DEBUG_BUGVERBOSE + select HAVE_DEBUG_KMEMLEAK + select HAVE_DMA_API_DEBUG + select HAVE_DMA_ATTRS + select HAVE_DMA_CONTIGUOUS + select HAVE_EFFICIENT_UNALIGNED_ACCESS ++ select HAVE_DYNAMIC_FTRACE ++ select HAVE_FTRACE_MCOUNT_RECORD ++ select HAVE_FUNCTION_TRACER ++ select HAVE_FUNCTION_GRAPH_TRACER + select HAVE_GENERIC_DMA_COHERENT + select HAVE_HW_BREAKPOINT if PERF_EVENTS + select HAVE_MEMBLOCK + select HAVE_PATA_PLATFORM + select HAVE_PERF_EVENTS ++ select HAVE_PERF_REGS ++ select HAVE_PERF_USER_STACK_DUMP ++ select HAVE_SYSCALL_TRACEPOINTS + select IRQ_DOMAIN + select MODULES_USE_ELF_RELA + select NO_BOOTMEM +@@ -86,7 +97,7 @@ + config GENERIC_CALIBRATE_DELAY + def_bool y + +-config ZONE_DMA32 ++config ZONE_DMA + def_bool y + + config ARCH_DMA_ADDR_T_64BIT +@@ -165,6 +176,134 @@ + + If you don't know what to do here, say N. + ++config SCHED_MC ++ bool "Multi-core scheduler support" ++ depends on SMP ++ help ++ Multi-core scheduler support improves the CPU scheduler's decision ++ making when dealing with multi-core CPU chips at a cost of slightly ++ increased overhead in some places. If unsure say N here. ++ ++config SCHED_SMT ++ bool "SMT scheduler support" ++ depends on SMP ++ help ++ Improves the CPU scheduler's decision making when dealing with ++ MultiThreading at a cost of slightly increased overhead in some ++ places. If unsure say N here. ++ ++config SCHED_MC ++ bool "Multi-core scheduler support" ++ depends on ARM_CPU_TOPOLOGY ++ help ++ Multi-core scheduler support improves the CPU scheduler's decision ++ making when dealing with multi-core CPU chips at a cost of slightly ++ increased overhead in some places. If unsure say N here. ++ ++config SCHED_SMT ++ bool "SMT scheduler support" ++ depends on ARM_CPU_TOPOLOGY ++ help ++ Improves the CPU scheduler's decision making when dealing with ++ MultiThreading at a cost of slightly increased overhead in some ++ places. If unsure say N here. ++ ++config DISABLE_CPU_SCHED_DOMAIN_BALANCE ++ bool "(EXPERIMENTAL) Disable CPU level scheduler load-balancing" ++ help ++ Disables scheduler load-balancing at CPU sched domain level. ++ ++config SCHED_HMP ++ bool "(EXPERIMENTAL) Heterogenous multiprocessor scheduling" ++ depends on DISABLE_CPU_SCHED_DOMAIN_BALANCE && SCHED_MC && FAIR_GROUP_SCHED && !SCHED_AUTOGROUP ++ help ++ Experimental scheduler optimizations for heterogeneous platforms. ++ Attempts to introspectively select task affinity to optimize power ++ and performance. Basic support for multiple (>2) cpu types is in place, ++ but it has only been tested with two types of cpus. ++ There is currently no support for migration of task groups, hence ++ !SCHED_AUTOGROUP. Furthermore, normal load-balancing must be disabled ++ between cpus of different type (DISABLE_CPU_SCHED_DOMAIN_BALANCE). ++ ++config SCHED_HMP_PRIO_FILTER ++ bool "(EXPERIMENTAL) Filter HMP migrations by task priority" ++ depends on SCHED_HMP ++ help ++ Enables task priority based HMP migration filter. Any task with ++ a NICE value above the threshold will always be on low-power cpus ++ with less compute capacity. ++ ++config SCHED_HMP_PRIO_FILTER_VAL ++ int "NICE priority threshold" ++ default 5 ++ depends on SCHED_HMP_PRIO_FILTER ++ ++config HMP_FAST_CPU_MASK ++ string "HMP scheduler fast CPU mask" ++ depends on SCHED_HMP ++ help ++ Leave empty to use device tree information. ++ Specify the cpuids of the fast CPUs in the system as a list string, ++ e.g. cpuid 0+1 should be specified as 0-1. ++ ++config HMP_SLOW_CPU_MASK ++ string "HMP scheduler slow CPU mask" ++ depends on SCHED_HMP ++ help ++ Leave empty to use device tree information. ++ Specify the cpuids of the slow CPUs in the system as a list string, ++ e.g. cpuid 0+1 should be specified as 0-1. ++ ++config HMP_VARIABLE_SCALE ++ bool "Allows changing the load tracking scale through sysfs" ++ depends on SCHED_HMP ++ help ++ When turned on, this option exports the thresholds and load average ++ period value for the load tracking patches through sysfs. ++ The values can be modified to change the rate of load accumulation ++ and the thresholds used for HMP migration. ++ The load_avg_period_ms is the time in ms to reach a load average of ++ 0.5 for an idle task of 0 load average ratio that start a busy loop. ++ The up_threshold and down_threshold is the value to go to a faster ++ CPU or to go back to a slower cpu. ++ The {up,down}_threshold are devided by 1024 before being compared ++ to the load average. ++ For examples, with load_avg_period_ms = 128 and up_threshold = 512, ++ a running task with a load of 0 will be migrated to a bigger CPU after ++ 128ms, because after 128ms its load_avg_ratio is 0.5 and the real ++ up_threshold is 0.5. ++ This patch has the same behavior as changing the Y of the load ++ average computation to ++ (1002/1024)^(LOAD_AVG_PERIOD/load_avg_period_ms) ++ but it remove intermadiate overflows in computation. ++ ++config HMP_FREQUENCY_INVARIANT_SCALE ++ bool "(EXPERIMENTAL) Frequency-Invariant Tracked Load for HMP" ++ depends on HMP_VARIABLE_SCALE && CPU_FREQ ++ help ++ Scales the current load contribution in line with the frequency ++ of the CPU that the task was executed on. ++ In this version, we use a simple linear scale derived from the ++ maximum frequency reported by CPUFreq. ++ Restricting tracked load to be scaled by the CPU's frequency ++ represents the consumption of possible compute capacity ++ (rather than consumption of actual instantaneous capacity as ++ normal) and allows the HMP migration's simple threshold ++ migration strategy to interact more predictably with CPUFreq's ++ asynchronous compute capacity changes. ++ ++config SCHED_HMP_LITTLE_PACKING ++ bool "Small task packing for HMP" ++ depends on SCHED_HMP ++ default n ++ help ++ Allows the HMP Scheduler to pack small tasks into CPUs in the ++ smallest HMP domain. ++ Controlled by two sysfs files in sys/kernel/hmp. ++ packing_enable: 1 to enable, 0 to disable packing. Default 1. ++ packing_limit: runqueue load ratio where a RQ is considered ++ to be full. Default is NICE_0_LOAD * 9/8. ++ + config NR_CPUS + int "Maximum number of CPUs (2-32)" + range 2 32 +@@ -317,5 +456,8 @@ + source "security/Kconfig" + + source "crypto/Kconfig" ++if CRYPTO ++source "arch/arm64/crypto/Kconfig" ++endif + + source "lib/Kconfig" +diff -Nur linux-3.14.36/arch/arm64/kernel/arm64ksyms.c linux-openelec/arch/arm64/kernel/arm64ksyms.c +--- linux-3.14.36/arch/arm64/kernel/arm64ksyms.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/arm64ksyms.c 2015-05-06 12:05:43.000000000 -0500 +@@ -56,3 +56,7 @@ + EXPORT_SYMBOL(test_and_clear_bit); + EXPORT_SYMBOL(change_bit); + EXPORT_SYMBOL(test_and_change_bit); ++ ++#ifdef CONFIG_FUNCTION_TRACER ++EXPORT_SYMBOL(_mcount); ++#endif +diff -Nur linux-3.14.36/arch/arm64/kernel/debug-monitors.c linux-openelec/arch/arm64/kernel/debug-monitors.c +--- linux-3.14.36/arch/arm64/kernel/debug-monitors.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/debug-monitors.c 2015-05-06 12:05:43.000000000 -0500 +@@ -138,6 +138,7 @@ + { + asm volatile("msr oslar_el1, %0" : : "r" (0)); + isb(); ++ local_dbg_enable(); + } + + static int os_lock_notify(struct notifier_block *self, +@@ -314,9 +315,6 @@ + if (call_break_hook(regs, esr) == DBG_HOOK_HANDLED) + return 0; + +- pr_warn("unexpected brk exception at %lx, esr=0x%x\n", +- (long)instruction_pointer(regs), esr); +- + if (!user_mode(regs)) + return -EFAULT; + +diff -Nur linux-3.14.36/arch/arm64/kernel/entry-ftrace.S linux-openelec/arch/arm64/kernel/entry-ftrace.S +--- linux-3.14.36/arch/arm64/kernel/entry-ftrace.S 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/entry-ftrace.S 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,218 @@ ++/* ++ * arch/arm64/kernel/entry-ftrace.S ++ * ++ * Copyright (C) 2013 Linaro Limited ++ * Author: AKASHI Takahiro ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++ ++/* ++ * Gcc with -pg will put the following code in the beginning of each function: ++ * mov x0, x30 ++ * bl _mcount ++ * [function's body ...] ++ * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic ++ * ftrace is enabled. ++ * ++ * Please note that x0 as an argument will not be used here because we can ++ * get lr(x30) of instrumented function at any time by winding up call stack ++ * as long as the kernel is compiled without -fomit-frame-pointer. ++ * (or CONFIG_FRAME_POINTER, this is forced on arm64) ++ * ++ * stack layout after mcount_enter in _mcount(): ++ * ++ * current sp/fp => 0:+-----+ ++ * in _mcount() | x29 | -> instrumented function's fp ++ * +-----+ ++ * | x30 | -> _mcount()'s lr (= instrumented function's pc) ++ * old sp => +16:+-----+ ++ * when instrumented | | ++ * function calls | ... | ++ * _mcount() | | ++ * | | ++ * instrumented => +xx:+-----+ ++ * function's fp | x29 | -> parent's fp ++ * +-----+ ++ * | x30 | -> instrumented function's lr (= parent's pc) ++ * +-----+ ++ * | ... | ++ */ ++ ++ .macro mcount_enter ++ stp x29, x30, [sp, #-16]! ++ mov x29, sp ++ .endm ++ ++ .macro mcount_exit ++ ldp x29, x30, [sp], #16 ++ ret ++ .endm ++ ++ .macro mcount_adjust_addr rd, rn ++ sub \rd, \rn, #AARCH64_INSN_SIZE ++ .endm ++ ++ /* for instrumented function's parent */ ++ .macro mcount_get_parent_fp reg ++ ldr \reg, [x29] ++ ldr \reg, [\reg] ++ .endm ++ ++ /* for instrumented function */ ++ .macro mcount_get_pc0 reg ++ mcount_adjust_addr \reg, x30 ++ .endm ++ ++ .macro mcount_get_pc reg ++ ldr \reg, [x29, #8] ++ mcount_adjust_addr \reg, \reg ++ .endm ++ ++ .macro mcount_get_lr reg ++ ldr \reg, [x29] ++ ldr \reg, [\reg, #8] ++ mcount_adjust_addr \reg, \reg ++ .endm ++ ++ .macro mcount_get_lr_addr reg ++ ldr \reg, [x29] ++ add \reg, \reg, #8 ++ .endm ++ ++#ifndef CONFIG_DYNAMIC_FTRACE ++/* ++ * void _mcount(unsigned long return_address) ++ * @return_address: return address to instrumented function ++ * ++ * This function makes calls, if enabled, to: ++ * - tracer function to probe instrumented function's entry, ++ * - ftrace_graph_caller to set up an exit hook ++ */ ++ENTRY(_mcount) ++#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST ++ ldr x0, =ftrace_trace_stop ++ ldr x0, [x0] // if ftrace_trace_stop ++ ret // return; ++#endif ++ mcount_enter ++ ++ ldr x0, =ftrace_trace_function ++ ldr x2, [x0] ++ adr x0, ftrace_stub ++ cmp x0, x2 // if (ftrace_trace_function ++ b.eq skip_ftrace_call // != ftrace_stub) { ++ ++ mcount_get_pc x0 // function's pc ++ mcount_get_lr x1 // function's lr (= parent's pc) ++ blr x2 // (*ftrace_trace_function)(pc, lr); ++ ++#ifndef CONFIG_FUNCTION_GRAPH_TRACER ++skip_ftrace_call: // return; ++ mcount_exit // } ++#else ++ mcount_exit // return; ++ // } ++skip_ftrace_call: ++ ldr x1, =ftrace_graph_return ++ ldr x2, [x1] // if ((ftrace_graph_return ++ cmp x0, x2 // != ftrace_stub) ++ b.ne ftrace_graph_caller ++ ++ ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry ++ ldr x2, [x1] // != ftrace_graph_entry_stub)) ++ ldr x0, =ftrace_graph_entry_stub ++ cmp x0, x2 ++ b.ne ftrace_graph_caller // ftrace_graph_caller(); ++ ++ mcount_exit ++#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ++ENDPROC(_mcount) ++ ++#else /* CONFIG_DYNAMIC_FTRACE */ ++/* ++ * _mcount() is used to build the kernel with -pg option, but all the branch ++ * instructions to _mcount() are replaced to NOP initially at kernel start up, ++ * and later on, NOP to branch to ftrace_caller() when enabled or branch to ++ * NOP when disabled per-function base. ++ */ ++ENTRY(_mcount) ++ ret ++ENDPROC(_mcount) ++ ++/* ++ * void ftrace_caller(unsigned long return_address) ++ * @return_address: return address to instrumented function ++ * ++ * This function is a counterpart of _mcount() in 'static' ftrace, and ++ * makes calls to: ++ * - tracer function to probe instrumented function's entry, ++ * - ftrace_graph_caller to set up an exit hook ++ */ ++ENTRY(ftrace_caller) ++ mcount_enter ++ ++ mcount_get_pc0 x0 // function's pc ++ mcount_get_lr x1 // function's lr ++ ++ .global ftrace_call ++ftrace_call: // tracer(pc, lr); ++ nop // This will be replaced with "bl xxx" ++ // where xxx can be any kind of tracer. ++ ++#ifdef CONFIG_FUNCTION_GRAPH_TRACER ++ .global ftrace_graph_call ++ftrace_graph_call: // ftrace_graph_caller(); ++ nop // If enabled, this will be replaced ++ // "b ftrace_graph_caller" ++#endif ++ ++ mcount_exit ++ENDPROC(ftrace_caller) ++#endif /* CONFIG_DYNAMIC_FTRACE */ ++ ++ENTRY(ftrace_stub) ++ ret ++ENDPROC(ftrace_stub) ++ ++#ifdef CONFIG_FUNCTION_GRAPH_TRACER ++/* ++ * void ftrace_graph_caller(void) ++ * ++ * Called from _mcount() or ftrace_caller() when function_graph tracer is ++ * selected. ++ * This function w/ prepare_ftrace_return() fakes link register's value on ++ * the call stack in order to intercept instrumented function's return path ++ * and run return_to_handler() later on its exit. ++ */ ++ENTRY(ftrace_graph_caller) ++ mcount_get_lr_addr x0 // pointer to function's saved lr ++ mcount_get_pc x1 // function's pc ++ mcount_get_parent_fp x2 // parent's fp ++ bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) ++ ++ mcount_exit ++ENDPROC(ftrace_graph_caller) ++ ++/* ++ * void return_to_handler(void) ++ * ++ * Run ftrace_return_to_handler() before going back to parent. ++ * @fp is checked against the value passed by ftrace_graph_caller() ++ * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. ++ */ ++ENTRY(return_to_handler) ++ str x0, [sp, #-16]! ++ mov x0, x29 // parent's fp ++ bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); ++ mov x30, x0 // restore the original return address ++ ldr x0, [sp], #16 ++ ret ++END(return_to_handler) ++#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +diff -Nur linux-3.14.36/arch/arm64/kernel/entry.S linux-openelec/arch/arm64/kernel/entry.S +--- linux-3.14.36/arch/arm64/kernel/entry.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/entry.S 2015-05-06 12:05:43.000000000 -0500 +@@ -630,8 +630,9 @@ + enable_irq + + get_thread_info tsk +- ldr x16, [tsk, #TI_FLAGS] // check for syscall tracing +- tbnz x16, #TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls? ++ ldr x16, [tsk, #TI_FLAGS] // check for syscall hooks ++ tst x16, #_TIF_SYSCALL_WORK ++ b.ne __sys_trace + adr lr, ret_fast_syscall // return address + cmp scno, sc_nr // check upper syscall limit + b.hs ni_sys +@@ -647,9 +648,8 @@ + * switches, and waiting for our parent to respond. + */ + __sys_trace: +- mov x1, sp +- mov w0, #0 // trace entry +- bl syscall_trace ++ mov x0, sp ++ bl syscall_trace_enter + adr lr, __sys_trace_return // return address + uxtw scno, w0 // syscall number (possibly new) + mov x1, sp // pointer to regs +@@ -664,9 +664,8 @@ + + __sys_trace_return: + str x0, [sp] // save returned x0 +- mov x1, sp +- mov w0, #1 // trace exit +- bl syscall_trace ++ mov x0, sp ++ bl syscall_trace_exit + b ret_to_user + + /* +diff -Nur linux-3.14.36/arch/arm64/kernel/ftrace.c linux-openelec/arch/arm64/kernel/ftrace.c +--- linux-3.14.36/arch/arm64/kernel/ftrace.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/ftrace.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,177 @@ ++/* ++ * arch/arm64/kernel/ftrace.c ++ * ++ * Copyright (C) 2013 Linaro Limited ++ * Author: AKASHI Takahiro ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#ifdef CONFIG_DYNAMIC_FTRACE ++/* ++ * Replace a single instruction, which may be a branch or NOP. ++ * If @validate == true, a replaced instruction is checked against 'old'. ++ */ ++static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, ++ bool validate) ++{ ++ u32 replaced; ++ ++ /* ++ * Note: ++ * Due to modules and __init, code can disappear and change, ++ * we need to protect against faulting as well as code changing. ++ * We do this by aarch64_insn_*() which use the probe_kernel_*(). ++ * ++ * No lock is held here because all the modifications are run ++ * through stop_machine(). ++ */ ++ if (validate) { ++ if (aarch64_insn_read((void *)pc, &replaced)) ++ return -EFAULT; ++ ++ if (replaced != old) ++ return -EINVAL; ++ } ++ if (aarch64_insn_patch_text_nosync((void *)pc, new)) ++ return -EPERM; ++ ++ return 0; ++} ++ ++/* ++ * Replace tracer function in ftrace_caller() ++ */ ++int ftrace_update_ftrace_func(ftrace_func_t func) ++{ ++ unsigned long pc; ++ u32 new; ++ ++ pc = (unsigned long)&ftrace_call; ++ new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); ++ ++ return ftrace_modify_code(pc, 0, new, false); ++} ++ ++/* ++ * Turn on the call to ftrace_caller() in instrumented function ++ */ ++int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ++{ ++ unsigned long pc = rec->ip; ++ u32 old, new; ++ ++ old = aarch64_insn_gen_nop(); ++ new = aarch64_insn_gen_branch_imm(pc, addr, true); ++ ++ return ftrace_modify_code(pc, old, new, true); ++} ++ ++/* ++ * Turn off the call to ftrace_caller() in instrumented function ++ */ ++int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, ++ unsigned long addr) ++{ ++ unsigned long pc = rec->ip; ++ u32 old, new; ++ ++ old = aarch64_insn_gen_branch_imm(pc, addr, true); ++ new = aarch64_insn_gen_nop(); ++ ++ return ftrace_modify_code(pc, old, new, true); ++} ++ ++int __init ftrace_dyn_arch_init(void *data) ++{ ++ *(unsigned long *)data = 0; ++ return 0; ++} ++#endif /* CONFIG_DYNAMIC_FTRACE */ ++ ++#ifdef CONFIG_FUNCTION_GRAPH_TRACER ++/* ++ * function_graph tracer expects ftrace_return_to_handler() to be called ++ * on the way back to parent. For this purpose, this function is called ++ * in _mcount() or ftrace_caller() to replace return address (*parent) on ++ * the call stack to return_to_handler. ++ * ++ * Note that @frame_pointer is used only for sanity check later. ++ */ ++void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, ++ unsigned long frame_pointer) ++{ ++ unsigned long return_hooker = (unsigned long)&return_to_handler; ++ unsigned long old; ++ struct ftrace_graph_ent trace; ++ int err; ++ ++ if (unlikely(atomic_read(¤t->tracing_graph_pause))) ++ return; ++ ++ /* ++ * Note: ++ * No protection against faulting at *parent, which may be seen ++ * on other archs. It's unlikely on AArch64. ++ */ ++ old = *parent; ++ *parent = return_hooker; ++ ++ trace.func = self_addr; ++ trace.depth = current->curr_ret_stack + 1; ++ ++ /* Only trace if the calling function expects to */ ++ if (!ftrace_graph_entry(&trace)) { ++ *parent = old; ++ return; ++ } ++ ++ err = ftrace_push_return_trace(old, self_addr, &trace.depth, ++ frame_pointer); ++ if (err == -EBUSY) { ++ *parent = old; ++ return; ++ } ++} ++ ++#ifdef CONFIG_DYNAMIC_FTRACE ++/* ++ * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() ++ * depending on @enable. ++ */ ++static int ftrace_modify_graph_caller(bool enable) ++{ ++ unsigned long pc = (unsigned long)&ftrace_graph_call; ++ u32 branch, nop; ++ ++ branch = aarch64_insn_gen_branch_imm(pc, ++ (unsigned long)ftrace_graph_caller, false); ++ nop = aarch64_insn_gen_nop(); ++ ++ if (enable) ++ return ftrace_modify_code(pc, nop, branch, true); ++ else ++ return ftrace_modify_code(pc, branch, nop, true); ++} ++ ++int ftrace_enable_ftrace_graph_caller(void) ++{ ++ return ftrace_modify_graph_caller(true); ++} ++ ++int ftrace_disable_ftrace_graph_caller(void) ++{ ++ return ftrace_modify_graph_caller(false); ++} ++#endif /* CONFIG_DYNAMIC_FTRACE */ ++#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +diff -Nur linux-3.14.36/arch/arm64/kernel/head.S linux-openelec/arch/arm64/kernel/head.S +--- linux-3.14.36/arch/arm64/kernel/head.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/head.S 2015-05-06 12:05:43.000000000 -0500 +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -34,29 +35,17 @@ + #include + #include + +-/* +- * swapper_pg_dir is the virtual address of the initial page table. We place +- * the page tables 3 * PAGE_SIZE below KERNEL_RAM_VADDR. The idmap_pg_dir has +- * 2 pages and is placed below swapper_pg_dir. +- */ + #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) + + #if (KERNEL_RAM_VADDR & 0xfffff) != 0x80000 + #error KERNEL_RAM_VADDR must start at 0xXXX80000 + #endif + +-#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE) +-#define IDMAP_DIR_SIZE (2 * PAGE_SIZE) +- +- .globl swapper_pg_dir +- .equ swapper_pg_dir, KERNEL_RAM_VADDR - SWAPPER_DIR_SIZE +- +- .globl idmap_pg_dir +- .equ idmap_pg_dir, swapper_pg_dir - IDMAP_DIR_SIZE +- +- .macro pgtbl, ttb0, ttb1, phys +- add \ttb1, \phys, #TEXT_OFFSET - SWAPPER_DIR_SIZE +- sub \ttb0, \ttb1, #IDMAP_DIR_SIZE ++ .macro pgtbl, ttb0, ttb1, virt_to_phys ++ ldr \ttb1, =swapper_pg_dir ++ ldr \ttb0, =idmap_pg_dir ++ add \ttb1, \ttb1, \virt_to_phys ++ add \ttb0, \ttb0, \virt_to_phys + .endm + + #ifdef CONFIG_ARM64_64K_PAGES +@@ -229,7 +218,11 @@ + cmp w20, #BOOT_CPU_MODE_EL2 + b.ne 1f + add x1, x1, #4 +-1: str w20, [x1] // This CPU has booted in EL1 ++1: dc cvac, x1 // Clean potentially dirty cache line ++ dsb sy ++ str w20, [x1] // This CPU has booted in EL1 ++ dc civac, x1 // Clean&invalidate potentially stale cache line ++ dsb sy + ret + ENDPROC(set_cpu_boot_mode_flag) + +@@ -240,8 +233,9 @@ + * This is not in .bss, because we set it sufficiently early that the boot-time + * zeroing of .bss would clobber it. + */ +- .pushsection .data ++ .pushsection .data..cacheline_aligned + ENTRY(__boot_cpu_mode) ++ .align L1_CACHE_SHIFT + .long BOOT_CPU_MODE_EL2 + .long 0 + .popsection +@@ -298,7 +292,7 @@ + mov x23, x0 // x23=current cpu_table + cbz x23, __error_p // invalid processor (x23=0)? + +- pgtbl x25, x26, x24 // x25=TTBR0, x26=TTBR1 ++ pgtbl x25, x26, x28 // x25=TTBR0, x26=TTBR1 + ldr x12, [x23, #CPU_INFO_SETUP] + add x12, x12, x28 // __virt_to_phys + blr x12 // initialise processor +@@ -340,8 +334,13 @@ + * x27 = *virtual* address to jump to upon completion + * + * other registers depend on the function called upon completion ++ * ++ * We align the entire function to the smallest power of two larger than it to ++ * ensure it fits within a single block map entry. Otherwise were PHYS_OFFSET ++ * close to the end of a 512MB or 1GB block we might require an additional ++ * table to map the entire function. + */ +- .align 6 ++ .align 4 + __turn_mmu_on: + msr sctlr_el1, x0 + isb +@@ -384,26 +383,18 @@ + * Preserves: tbl, flags + * Corrupts: phys, start, end, pstate + */ +- .macro create_block_map, tbl, flags, phys, start, end, idmap=0 ++ .macro create_block_map, tbl, flags, phys, start, end + lsr \phys, \phys, #BLOCK_SHIFT +- .if \idmap +- and \start, \phys, #PTRS_PER_PTE - 1 // table index +- .else + lsr \start, \start, #BLOCK_SHIFT + and \start, \start, #PTRS_PER_PTE - 1 // table index +- .endif + orr \phys, \flags, \phys, lsl #BLOCK_SHIFT // table entry +- .ifnc \start,\end + lsr \end, \end, #BLOCK_SHIFT + and \end, \end, #PTRS_PER_PTE - 1 // table end index +- .endif + 9999: str \phys, [\tbl, \start, lsl #3] // store the entry +- .ifnc \start,\end + add \start, \start, #1 // next entry + add \phys, \phys, #BLOCK_SIZE // next block + cmp \start, \end + b.ls 9999b +- .endif + .endm + + /* +@@ -415,7 +406,16 @@ + * - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1) + */ + __create_page_tables: +- pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses ++ pgtbl x25, x26, x28 // idmap_pg_dir and swapper_pg_dir addresses ++ mov x27, lr ++ ++ /* ++ * Invalidate the idmap and swapper page tables to avoid potential ++ * dirty cache lines being evicted. ++ */ ++ mov x0, x25 ++ add x1, x26, #SWAPPER_DIR_SIZE ++ bl __inval_cache_range + + /* + * Clear the idmap and swapper page tables. +@@ -435,9 +435,13 @@ + * Create the identity mapping. + */ + add x0, x25, #PAGE_SIZE // section table address +- adr x3, __turn_mmu_on // virtual/physical address ++ ldr x3, =KERNEL_START ++ add x3, x3, x28 // __pa(KERNEL_START) + create_pgd_entry x25, x0, x3, x5, x6 +- create_block_map x0, x7, x3, x5, x5, idmap=1 ++ ldr x6, =KERNEL_END ++ mov x5, x3 // __pa(KERNEL_START) ++ add x6, x6, x28 // __pa(KERNEL_END) ++ create_block_map x0, x7, x3, x5, x6 + + /* + * Map the kernel image (starting with PHYS_OFFSET). +@@ -445,7 +449,7 @@ + add x0, x26, #PAGE_SIZE // section table address + mov x5, #PAGE_OFFSET + create_pgd_entry x26, x0, x5, x3, x6 +- ldr x6, =KERNEL_END - 1 ++ ldr x6, =KERNEL_END + mov x3, x24 // phys offset + create_block_map x0, x7, x3, x5, x6 + +@@ -474,6 +478,17 @@ + add x0, x26, #2 * PAGE_SIZE // section table address + create_pgd_entry x26, x0, x5, x6, x7 + #endif ++ ++ /* ++ * Since the page tables have been populated with non-cacheable ++ * accesses (MMU disabled), invalidate the idmap and swapper page ++ * tables again to remove any speculatively loaded cache lines. ++ */ ++ mov x0, x25 ++ add x1, x26, #SWAPPER_DIR_SIZE ++ bl __inval_cache_range ++ ++ mov lr, x27 + ret + ENDPROC(__create_page_tables) + .ltorg +@@ -483,7 +498,7 @@ + __switch_data: + .quad __mmap_switched + .quad __bss_start // x6 +- .quad _end // x7 ++ .quad __bss_stop // x7 + .quad processor_id // x4 + .quad __fdt_pointer // x5 + .quad memstart_addr // x6 +diff -Nur linux-3.14.36/arch/arm64/kernel/hw_breakpoint.c linux-openelec/arch/arm64/kernel/hw_breakpoint.c +--- linux-3.14.36/arch/arm64/kernel/hw_breakpoint.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/hw_breakpoint.c 2015-05-06 12:05:43.000000000 -0500 +@@ -20,6 +20,7 @@ + + #define pr_fmt(fmt) "hw-breakpoint: " fmt + ++#include + #include + #include + #include +@@ -27,7 +28,6 @@ + #include + #include + +-#include + #include + #include + #include +diff -Nur linux-3.14.36/arch/arm64/kernel/kgdb.c linux-openelec/arch/arm64/kernel/kgdb.c +--- linux-3.14.36/arch/arm64/kernel/kgdb.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/kgdb.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,336 @@ ++/* ++ * AArch64 KGDB support ++ * ++ * Based on arch/arm/kernel/kgdb.c ++ * ++ * Copyright (C) 2013 Cavium Inc. ++ * Author: Vijaya Kumar K ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++ ++struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { ++ { "x0", 8, offsetof(struct pt_regs, regs[0])}, ++ { "x1", 8, offsetof(struct pt_regs, regs[1])}, ++ { "x2", 8, offsetof(struct pt_regs, regs[2])}, ++ { "x3", 8, offsetof(struct pt_regs, regs[3])}, ++ { "x4", 8, offsetof(struct pt_regs, regs[4])}, ++ { "x5", 8, offsetof(struct pt_regs, regs[5])}, ++ { "x6", 8, offsetof(struct pt_regs, regs[6])}, ++ { "x7", 8, offsetof(struct pt_regs, regs[7])}, ++ { "x8", 8, offsetof(struct pt_regs, regs[8])}, ++ { "x9", 8, offsetof(struct pt_regs, regs[9])}, ++ { "x10", 8, offsetof(struct pt_regs, regs[10])}, ++ { "x11", 8, offsetof(struct pt_regs, regs[11])}, ++ { "x12", 8, offsetof(struct pt_regs, regs[12])}, ++ { "x13", 8, offsetof(struct pt_regs, regs[13])}, ++ { "x14", 8, offsetof(struct pt_regs, regs[14])}, ++ { "x15", 8, offsetof(struct pt_regs, regs[15])}, ++ { "x16", 8, offsetof(struct pt_regs, regs[16])}, ++ { "x17", 8, offsetof(struct pt_regs, regs[17])}, ++ { "x18", 8, offsetof(struct pt_regs, regs[18])}, ++ { "x19", 8, offsetof(struct pt_regs, regs[19])}, ++ { "x20", 8, offsetof(struct pt_regs, regs[20])}, ++ { "x21", 8, offsetof(struct pt_regs, regs[21])}, ++ { "x22", 8, offsetof(struct pt_regs, regs[22])}, ++ { "x23", 8, offsetof(struct pt_regs, regs[23])}, ++ { "x24", 8, offsetof(struct pt_regs, regs[24])}, ++ { "x25", 8, offsetof(struct pt_regs, regs[25])}, ++ { "x26", 8, offsetof(struct pt_regs, regs[26])}, ++ { "x27", 8, offsetof(struct pt_regs, regs[27])}, ++ { "x28", 8, offsetof(struct pt_regs, regs[28])}, ++ { "x29", 8, offsetof(struct pt_regs, regs[29])}, ++ { "x30", 8, offsetof(struct pt_regs, regs[30])}, ++ { "sp", 8, offsetof(struct pt_regs, sp)}, ++ { "pc", 8, offsetof(struct pt_regs, pc)}, ++ { "pstate", 8, offsetof(struct pt_regs, pstate)}, ++ { "v0", 16, -1 }, ++ { "v1", 16, -1 }, ++ { "v2", 16, -1 }, ++ { "v3", 16, -1 }, ++ { "v4", 16, -1 }, ++ { "v5", 16, -1 }, ++ { "v6", 16, -1 }, ++ { "v7", 16, -1 }, ++ { "v8", 16, -1 }, ++ { "v9", 16, -1 }, ++ { "v10", 16, -1 }, ++ { "v11", 16, -1 }, ++ { "v12", 16, -1 }, ++ { "v13", 16, -1 }, ++ { "v14", 16, -1 }, ++ { "v15", 16, -1 }, ++ { "v16", 16, -1 }, ++ { "v17", 16, -1 }, ++ { "v18", 16, -1 }, ++ { "v19", 16, -1 }, ++ { "v20", 16, -1 }, ++ { "v21", 16, -1 }, ++ { "v22", 16, -1 }, ++ { "v23", 16, -1 }, ++ { "v24", 16, -1 }, ++ { "v25", 16, -1 }, ++ { "v26", 16, -1 }, ++ { "v27", 16, -1 }, ++ { "v28", 16, -1 }, ++ { "v29", 16, -1 }, ++ { "v30", 16, -1 }, ++ { "v31", 16, -1 }, ++ { "fpsr", 4, -1 }, ++ { "fpcr", 4, -1 }, ++}; ++ ++char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) ++{ ++ if (regno >= DBG_MAX_REG_NUM || regno < 0) ++ return NULL; ++ ++ if (dbg_reg_def[regno].offset != -1) ++ memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, ++ dbg_reg_def[regno].size); ++ else ++ memset(mem, 0, dbg_reg_def[regno].size); ++ return dbg_reg_def[regno].name; ++} ++ ++int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) ++{ ++ if (regno >= DBG_MAX_REG_NUM || regno < 0) ++ return -EINVAL; ++ ++ if (dbg_reg_def[regno].offset != -1) ++ memcpy((void *)regs + dbg_reg_def[regno].offset, mem, ++ dbg_reg_def[regno].size); ++ return 0; ++} ++ ++void ++sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task) ++{ ++ struct pt_regs *thread_regs; ++ ++ /* Initialize to zero */ ++ memset((char *)gdb_regs, 0, NUMREGBYTES); ++ thread_regs = task_pt_regs(task); ++ memcpy((void *)gdb_regs, (void *)thread_regs->regs, GP_REG_BYTES); ++} ++ ++void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) ++{ ++ regs->pc = pc; ++} ++ ++static int compiled_break; ++ ++static void kgdb_arch_update_addr(struct pt_regs *regs, ++ char *remcom_in_buffer) ++{ ++ unsigned long addr; ++ char *ptr; ++ ++ ptr = &remcom_in_buffer[1]; ++ if (kgdb_hex2long(&ptr, &addr)) ++ kgdb_arch_set_pc(regs, addr); ++ else if (compiled_break == 1) ++ kgdb_arch_set_pc(regs, regs->pc + 4); ++ ++ compiled_break = 0; ++} ++ ++int kgdb_arch_handle_exception(int exception_vector, int signo, ++ int err_code, char *remcom_in_buffer, ++ char *remcom_out_buffer, ++ struct pt_regs *linux_regs) ++{ ++ int err; ++ ++ switch (remcom_in_buffer[0]) { ++ case 'D': ++ case 'k': ++ /* ++ * Packet D (Detach), k (kill). No special handling ++ * is required here. Handle same as c packet. ++ */ ++ case 'c': ++ /* ++ * Packet c (Continue) to continue executing. ++ * Set pc to required address. ++ * Try to read optional parameter and set pc. ++ * If this was a compiled breakpoint, we need to move ++ * to the next instruction else we will just breakpoint ++ * over and over again. ++ */ ++ kgdb_arch_update_addr(linux_regs, remcom_in_buffer); ++ atomic_set(&kgdb_cpu_doing_single_step, -1); ++ kgdb_single_step = 0; ++ ++ /* ++ * Received continue command, disable single step ++ */ ++ if (kernel_active_single_step()) ++ kernel_disable_single_step(); ++ ++ err = 0; ++ break; ++ case 's': ++ /* ++ * Update step address value with address passed ++ * with step packet. ++ * On debug exception return PC is copied to ELR ++ * So just update PC. ++ * If no step address is passed, resume from the address ++ * pointed by PC. Do not update PC ++ */ ++ kgdb_arch_update_addr(linux_regs, remcom_in_buffer); ++ atomic_set(&kgdb_cpu_doing_single_step, raw_smp_processor_id()); ++ kgdb_single_step = 1; ++ ++ /* ++ * Enable single step handling ++ */ ++ if (!kernel_active_single_step()) ++ kernel_enable_single_step(linux_regs); ++ err = 0; ++ break; ++ default: ++ err = -1; ++ } ++ return err; ++} ++ ++static int kgdb_brk_fn(struct pt_regs *regs, unsigned int esr) ++{ ++ kgdb_handle_exception(1, SIGTRAP, 0, regs); ++ return 0; ++} ++ ++static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr) ++{ ++ compiled_break = 1; ++ kgdb_handle_exception(1, SIGTRAP, 0, regs); ++ ++ return 0; ++} ++ ++static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr) ++{ ++ kgdb_handle_exception(1, SIGTRAP, 0, regs); ++ return 0; ++} ++ ++static struct break_hook kgdb_brkpt_hook = { ++ .esr_mask = 0xffffffff, ++ .esr_val = DBG_ESR_VAL_BRK(KGDB_DYN_DGB_BRK_IMM), ++ .fn = kgdb_brk_fn ++}; ++ ++static struct break_hook kgdb_compiled_brkpt_hook = { ++ .esr_mask = 0xffffffff, ++ .esr_val = DBG_ESR_VAL_BRK(KDBG_COMPILED_DBG_BRK_IMM), ++ .fn = kgdb_compiled_brk_fn ++}; ++ ++static struct step_hook kgdb_step_hook = { ++ .fn = kgdb_step_brk_fn ++}; ++ ++static void kgdb_call_nmi_hook(void *ignored) ++{ ++ kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); ++} ++ ++void kgdb_roundup_cpus(unsigned long flags) ++{ ++ local_irq_enable(); ++ smp_call_function(kgdb_call_nmi_hook, NULL, 0); ++ local_irq_disable(); ++} ++ ++static int __kgdb_notify(struct die_args *args, unsigned long cmd) ++{ ++ struct pt_regs *regs = args->regs; ++ ++ if (kgdb_handle_exception(1, args->signr, cmd, regs)) ++ return NOTIFY_DONE; ++ return NOTIFY_STOP; ++} ++ ++static int ++kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) ++{ ++ unsigned long flags; ++ int ret; ++ ++ local_irq_save(flags); ++ ret = __kgdb_notify(ptr, cmd); ++ local_irq_restore(flags); ++ ++ return ret; ++} ++ ++static struct notifier_block kgdb_notifier = { ++ .notifier_call = kgdb_notify, ++ /* ++ * Want to be lowest priority ++ */ ++ .priority = -INT_MAX, ++}; ++ ++/* ++ * kgdb_arch_init - Perform any architecture specific initalization. ++ * This function will handle the initalization of any architecture ++ * specific callbacks. ++ */ ++int kgdb_arch_init(void) ++{ ++ int ret = register_die_notifier(&kgdb_notifier); ++ ++ if (ret != 0) ++ return ret; ++ ++ register_break_hook(&kgdb_brkpt_hook); ++ register_break_hook(&kgdb_compiled_brkpt_hook); ++ register_step_hook(&kgdb_step_hook); ++ return 0; ++} ++ ++/* ++ * kgdb_arch_exit - Perform any architecture specific uninitalization. ++ * This function will handle the uninitalization of any architecture ++ * specific callbacks, for dynamic registration and unregistration. ++ */ ++void kgdb_arch_exit(void) ++{ ++ unregister_break_hook(&kgdb_brkpt_hook); ++ unregister_break_hook(&kgdb_compiled_brkpt_hook); ++ unregister_step_hook(&kgdb_step_hook); ++ unregister_die_notifier(&kgdb_notifier); ++} ++ ++/* ++ * ARM instructions are always in LE. ++ * Break instruction is encoded in LE format ++ */ ++struct kgdb_arch arch_kgdb_ops = { ++ .gdb_bpt_instr = { ++ KGDB_DYN_BRK_INS_BYTE0, ++ KGDB_DYN_BRK_INS_BYTE1, ++ KGDB_DYN_BRK_INS_BYTE2, ++ KGDB_DYN_BRK_INS_BYTE3, ++ } ++}; +diff -Nur linux-3.14.36/arch/arm64/kernel/Makefile linux-openelec/arch/arm64/kernel/Makefile +--- linux-3.14.36/arch/arm64/kernel/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -5,21 +5,29 @@ + CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) + AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) + ++CFLAGS_REMOVE_ftrace.o = -pg ++CFLAGS_REMOVE_insn.o = -pg ++CFLAGS_REMOVE_return_address.o = -pg ++ + # Object file lists. + arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ + entry-fpsimd.o process.o ptrace.o setup.o signal.o \ + sys.o stacktrace.o time.o traps.o io.o vdso.o \ +- hyp-stub.o psci.o cpu_ops.o insn.o ++ hyp-stub.o psci.o cpu_ops.o insn.o return_address.o + + arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ + sys_compat.o ++arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o + arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o ++arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o + arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o ++arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o + arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o +-arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o ++arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o + arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o + arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o ++arm64-obj-$(CONFIG_KGDB) += kgdb.o + + obj-y += $(arm64-obj-y) vdso/ + obj-m += $(arm64-obj-m) +diff -Nur linux-3.14.36/arch/arm64/kernel/perf_event.c linux-openelec/arch/arm64/kernel/perf_event.c +--- linux-3.14.36/arch/arm64/kernel/perf_event.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/perf_event.c 2015-05-06 12:05:43.000000000 -0500 +@@ -1348,8 +1348,8 @@ + * Callchain handling code. + */ + struct frame_tail { +- struct frame_tail __user *fp; +- unsigned long lr; ++ struct frame_tail __user *fp; ++ unsigned long lr; + } __attribute__((packed)); + + /* +@@ -1386,22 +1386,84 @@ + return buftail.fp; + } + ++#ifdef CONFIG_COMPAT ++/* ++ * The registers we're interested in are at the end of the variable ++ * length saved register structure. The fp points at the end of this ++ * structure so the address of this struct is: ++ * (struct compat_frame_tail *)(xxx->fp)-1 ++ * ++ * This code has been adapted from the ARM OProfile support. ++ */ ++struct compat_frame_tail { ++ compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */ ++ u32 sp; ++ u32 lr; ++} __attribute__((packed)); ++ ++static struct compat_frame_tail __user * ++compat_user_backtrace(struct compat_frame_tail __user *tail, ++ struct perf_callchain_entry *entry) ++{ ++ struct compat_frame_tail buftail; ++ unsigned long err; ++ ++ /* Also check accessibility of one struct frame_tail beyond */ ++ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) ++ return NULL; ++ ++ pagefault_disable(); ++ err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); ++ pagefault_enable(); ++ ++ if (err) ++ return NULL; ++ ++ perf_callchain_store(entry, buftail.lr); ++ ++ /* ++ * Frame pointers should strictly progress back up the stack ++ * (towards higher addresses). ++ */ ++ if (tail + 1 >= (struct compat_frame_tail __user *) ++ compat_ptr(buftail.fp)) ++ return NULL; ++ ++ return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; ++} ++#endif /* CONFIG_COMPAT */ ++ + void perf_callchain_user(struct perf_callchain_entry *entry, + struct pt_regs *regs) + { +- struct frame_tail __user *tail; +- + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { + /* We don't support guest os callchain now */ + return; + } + + perf_callchain_store(entry, regs->pc); +- tail = (struct frame_tail __user *)regs->regs[29]; + +- while (entry->nr < PERF_MAX_STACK_DEPTH && +- tail && !((unsigned long)tail & 0xf)) +- tail = user_backtrace(tail, entry); ++ if (!compat_user_mode(regs)) { ++ /* AARCH64 mode */ ++ struct frame_tail __user *tail; ++ ++ tail = (struct frame_tail __user *)regs->regs[29]; ++ ++ while (entry->nr < PERF_MAX_STACK_DEPTH && ++ tail && !((unsigned long)tail & 0xf)) ++ tail = user_backtrace(tail, entry); ++ } else { ++#ifdef CONFIG_COMPAT ++ /* AARCH32 compat mode */ ++ struct compat_frame_tail __user *tail; ++ ++ tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; ++ ++ while ((entry->nr < PERF_MAX_STACK_DEPTH) && ++ tail && !((unsigned long)tail & 0x3)) ++ tail = compat_user_backtrace(tail, entry); ++#endif ++ } + } + + /* +@@ -1429,6 +1491,7 @@ + frame.fp = regs->regs[29]; + frame.sp = regs->sp; + frame.pc = regs->pc; ++ + walk_stackframe(&frame, callchain_trace, entry); + } + +diff -Nur linux-3.14.36/arch/arm64/kernel/perf_regs.c linux-openelec/arch/arm64/kernel/perf_regs.c +--- linux-3.14.36/arch/arm64/kernel/perf_regs.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/perf_regs.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,46 @@ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++u64 perf_reg_value(struct pt_regs *regs, int idx) ++{ ++ if (WARN_ON_ONCE((u32)idx >= PERF_REG_ARM64_MAX)) ++ return 0; ++ ++ /* ++ * Compat (i.e. 32 bit) mode: ++ * - PC has been set in the pt_regs struct in kernel_entry, ++ * - Handle SP and LR here. ++ */ ++ if (compat_user_mode(regs)) { ++ if ((u32)idx == PERF_REG_ARM64_SP) ++ return regs->compat_sp; ++ if ((u32)idx == PERF_REG_ARM64_LR) ++ return regs->compat_lr; ++ } ++ ++ return regs->regs[idx]; ++} ++ ++#define REG_RESERVED (~((1ULL << PERF_REG_ARM64_MAX) - 1)) ++ ++int perf_reg_validate(u64 mask) ++{ ++ if (!mask || mask & REG_RESERVED) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++u64 perf_reg_abi(struct task_struct *task) ++{ ++ if (is_compat_thread(task_thread_info(task))) ++ return PERF_SAMPLE_REGS_ABI_32; ++ else ++ return PERF_SAMPLE_REGS_ABI_64; ++} +diff -Nur linux-3.14.36/arch/arm64/kernel/process.c linux-openelec/arch/arm64/kernel/process.c +--- linux-3.14.36/arch/arm64/kernel/process.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/process.c 2015-07-24 18:03:28.448842002 -0500 +@@ -20,6 +20,7 @@ + + #include + ++#include + #include + #include + #include +diff -Nur linux-3.14.36/arch/arm64/kernel/process.c.orig linux-openelec/arch/arm64/kernel/process.c.orig +--- linux-3.14.36/arch/arm64/kernel/process.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/process.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,350 @@ ++/* ++ * Based on arch/arm/kernel/process.c ++ * ++ * Original Copyright (C) 1995 Linus Torvalds ++ * Copyright (C) 1996-2000 Russell King - Converted to ARM. ++ * Copyright (C) 2012 ARM Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void setup_restart(void) ++{ ++ /* ++ * Tell the mm system that we are going to reboot - ++ * we may need it to insert some 1:1 mappings so that ++ * soft boot works. ++ */ ++ setup_mm_for_reboot(); ++ ++ /* Clean and invalidate caches */ ++ flush_cache_all(); ++ ++ /* Turn D-cache off */ ++ cpu_cache_off(); ++ ++ /* Push out any further dirty data, and ensure cache is empty */ ++ flush_cache_all(); ++} ++ ++void soft_restart(unsigned long addr) ++{ ++ setup_restart(); ++ cpu_reset(addr); ++} ++ ++/* ++ * Function pointers to optional machine specific functions ++ */ ++void (*pm_power_off)(void); ++EXPORT_SYMBOL_GPL(pm_power_off); ++ ++void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); ++EXPORT_SYMBOL_GPL(arm_pm_restart); ++ ++/* ++ * This is our default idle handler. ++ */ ++void arch_cpu_idle(void) ++{ ++ /* ++ * This should do all the clock switching and wait for interrupt ++ * tricks ++ */ ++ if (cpuidle_idle_call()) { ++ cpu_do_idle(); ++ local_irq_enable(); ++ } ++} ++ ++#ifdef CONFIG_HOTPLUG_CPU ++void arch_cpu_idle_dead(void) ++{ ++ cpu_die(); ++} ++#endif ++ ++void machine_shutdown(void) ++{ ++#ifdef CONFIG_SMP ++ smp_send_stop(); ++#endif ++} ++ ++void machine_halt(void) ++{ ++ machine_shutdown(); ++ while (1); ++} ++ ++void machine_power_off(void) ++{ ++ machine_shutdown(); ++ if (pm_power_off) ++ pm_power_off(); ++} ++ ++void machine_restart(char *cmd) ++{ ++ machine_shutdown(); ++ ++ /* Disable interrupts first */ ++ local_irq_disable(); ++ ++ /* Now call the architecture specific reboot code. */ ++ if (arm_pm_restart) ++ arm_pm_restart(reboot_mode, cmd); ++ ++ /* ++ * Whoops - the architecture was unable to reboot. ++ */ ++ printk("Reboot failed -- System halted\n"); ++ while (1); ++} ++ ++void __show_regs(struct pt_regs *regs) ++{ ++ int i, top_reg; ++ u64 lr, sp; ++ ++ if (compat_user_mode(regs)) { ++ lr = regs->compat_lr; ++ sp = regs->compat_sp; ++ top_reg = 12; ++ } else { ++ lr = regs->regs[30]; ++ sp = regs->sp; ++ top_reg = 29; ++ } ++ ++ show_regs_print_info(KERN_DEFAULT); ++ print_symbol("PC is at %s\n", instruction_pointer(regs)); ++ print_symbol("LR is at %s\n", lr); ++ printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n", ++ regs->pc, lr, regs->pstate); ++ printk("sp : %016llx\n", sp); ++ for (i = top_reg; i >= 0; i--) { ++ printk("x%-2d: %016llx ", i, regs->regs[i]); ++ if (i % 2 == 0) ++ printk("\n"); ++ } ++ printk("\n"); ++} ++ ++void show_regs(struct pt_regs * regs) ++{ ++ printk("\n"); ++ __show_regs(regs); ++} ++ ++/* ++ * Free current thread data structures etc.. ++ */ ++void exit_thread(void) ++{ ++} ++ ++void flush_thread(void) ++{ ++ fpsimd_flush_thread(); ++ flush_ptrace_hw_breakpoint(current); ++} ++ ++void release_thread(struct task_struct *dead_task) ++{ ++} ++ ++int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) ++{ ++ fpsimd_save_state(¤t->thread.fpsimd_state); ++ *dst = *src; ++ return 0; ++} ++ ++asmlinkage void ret_from_fork(void) asm("ret_from_fork"); ++ ++int copy_thread(unsigned long clone_flags, unsigned long stack_start, ++ unsigned long stk_sz, struct task_struct *p) ++{ ++ struct pt_regs *childregs = task_pt_regs(p); ++ unsigned long tls = p->thread.tp_value; ++ ++ memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); ++ ++ if (likely(!(p->flags & PF_KTHREAD))) { ++ *childregs = *current_pt_regs(); ++ childregs->regs[0] = 0; ++ if (is_compat_thread(task_thread_info(p))) { ++ if (stack_start) ++ childregs->compat_sp = stack_start; ++ } else { ++ /* ++ * Read the current TLS pointer from tpidr_el0 as it may be ++ * out-of-sync with the saved value. ++ */ ++ asm("mrs %0, tpidr_el0" : "=r" (tls)); ++ if (stack_start) { ++ /* 16-byte aligned stack mandatory on AArch64 */ ++ if (stack_start & 15) ++ return -EINVAL; ++ childregs->sp = stack_start; ++ } ++ } ++ /* ++ * If a TLS pointer was passed to clone (4th argument), use it ++ * for the new thread. ++ */ ++ if (clone_flags & CLONE_SETTLS) ++ tls = childregs->regs[3]; ++ } else { ++ memset(childregs, 0, sizeof(struct pt_regs)); ++ childregs->pstate = PSR_MODE_EL1h; ++ p->thread.cpu_context.x19 = stack_start; ++ p->thread.cpu_context.x20 = stk_sz; ++ } ++ p->thread.cpu_context.pc = (unsigned long)ret_from_fork; ++ p->thread.cpu_context.sp = (unsigned long)childregs; ++ p->thread.tp_value = tls; ++ ++ ptrace_hw_copy_thread(p); ++ ++ return 0; ++} ++ ++static void tls_thread_switch(struct task_struct *next) ++{ ++ unsigned long tpidr, tpidrro; ++ ++ if (!is_compat_task()) { ++ asm("mrs %0, tpidr_el0" : "=r" (tpidr)); ++ current->thread.tp_value = tpidr; ++ } ++ ++ if (is_compat_thread(task_thread_info(next))) { ++ tpidr = 0; ++ tpidrro = next->thread.tp_value; ++ } else { ++ tpidr = next->thread.tp_value; ++ tpidrro = 0; ++ } ++ ++ asm( ++ " msr tpidr_el0, %0\n" ++ " msr tpidrro_el0, %1" ++ : : "r" (tpidr), "r" (tpidrro)); ++} ++ ++/* ++ * Thread switching. ++ */ ++struct task_struct *__switch_to(struct task_struct *prev, ++ struct task_struct *next) ++{ ++ struct task_struct *last; ++ ++ fpsimd_thread_switch(next); ++ tls_thread_switch(next); ++ hw_breakpoint_thread_switch(next); ++ contextidr_thread_switch(next); ++ ++ /* ++ * Complete any pending TLB or cache maintenance on this CPU in case ++ * the thread migrates to a different CPU. ++ */ ++ dsb(); ++ ++ /* the actual thread switch */ ++ last = cpu_switch_to(prev, next); ++ ++ return last; ++} ++ ++unsigned long get_wchan(struct task_struct *p) ++{ ++ struct stackframe frame; ++ unsigned long stack_page; ++ int count = 0; ++ if (!p || p == current || p->state == TASK_RUNNING) ++ return 0; ++ ++ frame.fp = thread_saved_fp(p); ++ frame.sp = thread_saved_sp(p); ++ frame.pc = thread_saved_pc(p); ++ stack_page = (unsigned long)task_stack_page(p); ++ do { ++ if (frame.sp < stack_page || ++ frame.sp >= stack_page + THREAD_SIZE || ++ unwind_frame(&frame)) ++ return 0; ++ if (!in_sched_functions(frame.pc)) ++ return frame.pc; ++ } while (count ++ < 16); ++ return 0; ++} ++ ++unsigned long arch_align_stack(unsigned long sp) ++{ ++ if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) ++ sp -= get_random_int() & ~PAGE_MASK; ++ return sp & ~0xf; ++} ++ ++static unsigned long randomize_base(unsigned long base) ++{ ++ unsigned long range_end = base + (STACK_RND_MASK << PAGE_SHIFT) + 1; ++ return randomize_range(base, range_end, 0) ? : base; ++} ++ ++unsigned long arch_randomize_brk(struct mm_struct *mm) ++{ ++ return randomize_base(mm->brk); ++} ++ ++unsigned long randomize_et_dyn(unsigned long base) ++{ ++ return randomize_base(base); ++} +diff -Nur linux-3.14.36/arch/arm64/kernel/ptrace.c linux-openelec/arch/arm64/kernel/ptrace.c +--- linux-3.14.36/arch/arm64/kernel/ptrace.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/ptrace.c 2015-07-24 18:03:28.448842002 -0500 +@@ -19,6 +19,7 @@ + * along with this program. If not, see . + */ + ++#include + #include + #include + #include +@@ -41,6 +42,9 @@ + #include + #include + ++#define CREATE_TRACE_POINTS ++#include ++ + /* + * TODO: does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. +@@ -1073,35 +1077,49 @@ + return ptrace_request(child, request, addr, data); + } + +-asmlinkage int syscall_trace(int dir, struct pt_regs *regs) ++enum ptrace_syscall_dir { ++ PTRACE_SYSCALL_ENTER = 0, ++ PTRACE_SYSCALL_EXIT, ++}; ++ ++static void tracehook_report_syscall(struct pt_regs *regs, ++ enum ptrace_syscall_dir dir) + { ++ int regno; + unsigned long saved_reg; + +- if (!test_thread_flag(TIF_SYSCALL_TRACE)) +- return regs->syscallno; +- +- if (is_compat_task()) { +- /* AArch32 uses ip (r12) for scratch */ +- saved_reg = regs->regs[12]; +- regs->regs[12] = dir; +- } else { +- /* +- * Save X7. X7 is used to denote syscall entry/exit: +- * X7 = 0 -> entry, = 1 -> exit +- */ +- saved_reg = regs->regs[7]; +- regs->regs[7] = dir; +- } ++ /* ++ * A scratch register (ip(r12) on AArch32, x7 on AArch64) is ++ * used to denote syscall entry/exit: ++ */ ++ regno = (is_compat_task() ? 12 : 7); ++ saved_reg = regs->regs[regno]; ++ regs->regs[regno] = dir; + +- if (dir) ++ if (dir == PTRACE_SYSCALL_EXIT) + tracehook_report_syscall_exit(regs, 0); + else if (tracehook_report_syscall_entry(regs)) + regs->syscallno = ~0UL; + +- if (is_compat_task()) +- regs->regs[12] = saved_reg; +- else +- regs->regs[7] = saved_reg; ++ regs->regs[regno] = saved_reg; ++} ++ ++asmlinkage int syscall_trace_enter(struct pt_regs *regs) ++{ ++ if (test_thread_flag(TIF_SYSCALL_TRACE)) ++ tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER); ++ ++ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) ++ trace_sys_enter(regs, regs->syscallno); + + return regs->syscallno; + } ++ ++asmlinkage void syscall_trace_exit(struct pt_regs *regs) ++{ ++ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) ++ trace_sys_exit(regs, regs_return_value(regs)); ++ ++ if (test_thread_flag(TIF_SYSCALL_TRACE)) ++ tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT); ++} +diff -Nur linux-3.14.36/arch/arm64/kernel/ptrace.c.orig linux-openelec/arch/arm64/kernel/ptrace.c.orig +--- linux-3.14.36/arch/arm64/kernel/ptrace.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/ptrace.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,1124 @@ ++/* ++ * Based on arch/arm/kernel/ptrace.c ++ * ++ * By Ross Biro 1/23/92 ++ * edited by Linus Torvalds ++ * ARM modifications Copyright (C) 2000 Russell King ++ * Copyright (C) 2012 ARM Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++/* ++ * TODO: does not yet catch signals sent when the child dies. ++ * in exit.c or in signal.c. ++ */ ++ ++/* ++ * Called by kernel/ptrace.c when detaching.. ++ */ ++void ptrace_disable(struct task_struct *child) ++{ ++} ++ ++#ifdef CONFIG_HAVE_HW_BREAKPOINT ++/* ++ * Handle hitting a HW-breakpoint. ++ */ ++static void ptrace_hbptriggered(struct perf_event *bp, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); ++ siginfo_t info = { ++ .si_signo = SIGTRAP, ++ .si_errno = 0, ++ .si_code = TRAP_HWBKPT, ++ .si_addr = (void __user *)(bkpt->trigger), ++ }; ++ ++#ifdef CONFIG_COMPAT ++ int i; ++ ++ if (!is_compat_task()) ++ goto send_sig; ++ ++ for (i = 0; i < ARM_MAX_BRP; ++i) { ++ if (current->thread.debug.hbp_break[i] == bp) { ++ info.si_errno = (i << 1) + 1; ++ break; ++ } ++ } ++ for (i = ARM_MAX_BRP; i < ARM_MAX_HBP_SLOTS && !bp; ++i) { ++ if (current->thread.debug.hbp_watch[i] == bp) { ++ info.si_errno = -((i << 1) + 1); ++ break; ++ } ++ } ++ ++send_sig: ++#endif ++ force_sig_info(SIGTRAP, &info, current); ++} ++ ++/* ++ * Unregister breakpoints from this task and reset the pointers in ++ * the thread_struct. ++ */ ++void flush_ptrace_hw_breakpoint(struct task_struct *tsk) ++{ ++ int i; ++ struct thread_struct *t = &tsk->thread; ++ ++ for (i = 0; i < ARM_MAX_BRP; i++) { ++ if (t->debug.hbp_break[i]) { ++ unregister_hw_breakpoint(t->debug.hbp_break[i]); ++ t->debug.hbp_break[i] = NULL; ++ } ++ } ++ ++ for (i = 0; i < ARM_MAX_WRP; i++) { ++ if (t->debug.hbp_watch[i]) { ++ unregister_hw_breakpoint(t->debug.hbp_watch[i]); ++ t->debug.hbp_watch[i] = NULL; ++ } ++ } ++} ++ ++void ptrace_hw_copy_thread(struct task_struct *tsk) ++{ ++ memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); ++} ++ ++static struct perf_event *ptrace_hbp_get_event(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx) ++{ ++ struct perf_event *bp = ERR_PTR(-EINVAL); ++ ++ switch (note_type) { ++ case NT_ARM_HW_BREAK: ++ if (idx < ARM_MAX_BRP) ++ bp = tsk->thread.debug.hbp_break[idx]; ++ break; ++ case NT_ARM_HW_WATCH: ++ if (idx < ARM_MAX_WRP) ++ bp = tsk->thread.debug.hbp_watch[idx]; ++ break; ++ } ++ ++ return bp; ++} ++ ++static int ptrace_hbp_set_event(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx, ++ struct perf_event *bp) ++{ ++ int err = -EINVAL; ++ ++ switch (note_type) { ++ case NT_ARM_HW_BREAK: ++ if (idx < ARM_MAX_BRP) { ++ tsk->thread.debug.hbp_break[idx] = bp; ++ err = 0; ++ } ++ break; ++ case NT_ARM_HW_WATCH: ++ if (idx < ARM_MAX_WRP) { ++ tsk->thread.debug.hbp_watch[idx] = bp; ++ err = 0; ++ } ++ break; ++ } ++ ++ return err; ++} ++ ++static struct perf_event *ptrace_hbp_create(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx) ++{ ++ struct perf_event *bp; ++ struct perf_event_attr attr; ++ int err, type; ++ ++ switch (note_type) { ++ case NT_ARM_HW_BREAK: ++ type = HW_BREAKPOINT_X; ++ break; ++ case NT_ARM_HW_WATCH: ++ type = HW_BREAKPOINT_RW; ++ break; ++ default: ++ return ERR_PTR(-EINVAL); ++ } ++ ++ ptrace_breakpoint_init(&attr); ++ ++ /* ++ * Initialise fields to sane defaults ++ * (i.e. values that will pass validation). ++ */ ++ attr.bp_addr = 0; ++ attr.bp_len = HW_BREAKPOINT_LEN_4; ++ attr.bp_type = type; ++ attr.disabled = 1; ++ ++ bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk); ++ if (IS_ERR(bp)) ++ return bp; ++ ++ err = ptrace_hbp_set_event(note_type, tsk, idx, bp); ++ if (err) ++ return ERR_PTR(err); ++ ++ return bp; ++} ++ ++static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, ++ struct arch_hw_breakpoint_ctrl ctrl, ++ struct perf_event_attr *attr) ++{ ++ int err, len, type, disabled = !ctrl.enabled; ++ ++ attr->disabled = disabled; ++ if (disabled) ++ return 0; ++ ++ err = arch_bp_generic_fields(ctrl, &len, &type); ++ if (err) ++ return err; ++ ++ switch (note_type) { ++ case NT_ARM_HW_BREAK: ++ if ((type & HW_BREAKPOINT_X) != type) ++ return -EINVAL; ++ break; ++ case NT_ARM_HW_WATCH: ++ if ((type & HW_BREAKPOINT_RW) != type) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ attr->bp_len = len; ++ attr->bp_type = type; ++ ++ return 0; ++} ++ ++static int ptrace_hbp_get_resource_info(unsigned int note_type, u32 *info) ++{ ++ u8 num; ++ u32 reg = 0; ++ ++ switch (note_type) { ++ case NT_ARM_HW_BREAK: ++ num = hw_breakpoint_slots(TYPE_INST); ++ break; ++ case NT_ARM_HW_WATCH: ++ num = hw_breakpoint_slots(TYPE_DATA); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ reg |= debug_monitors_arch(); ++ reg <<= 8; ++ reg |= num; ++ ++ *info = reg; ++ return 0; ++} ++ ++static int ptrace_hbp_get_ctrl(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx, ++ u32 *ctrl) ++{ ++ struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx); ++ ++ if (IS_ERR(bp)) ++ return PTR_ERR(bp); ++ ++ *ctrl = bp ? encode_ctrl_reg(counter_arch_bp(bp)->ctrl) : 0; ++ return 0; ++} ++ ++static int ptrace_hbp_get_addr(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx, ++ u64 *addr) ++{ ++ struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx); ++ ++ if (IS_ERR(bp)) ++ return PTR_ERR(bp); ++ ++ *addr = bp ? bp->attr.bp_addr : 0; ++ return 0; ++} ++ ++static struct perf_event *ptrace_hbp_get_initialised_bp(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx) ++{ ++ struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx); ++ ++ if (!bp) ++ bp = ptrace_hbp_create(note_type, tsk, idx); ++ ++ return bp; ++} ++ ++static int ptrace_hbp_set_ctrl(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx, ++ u32 uctrl) ++{ ++ int err; ++ struct perf_event *bp; ++ struct perf_event_attr attr; ++ struct arch_hw_breakpoint_ctrl ctrl; ++ ++ bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx); ++ if (IS_ERR(bp)) { ++ err = PTR_ERR(bp); ++ return err; ++ } ++ ++ attr = bp->attr; ++ decode_ctrl_reg(uctrl, &ctrl); ++ err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr); ++ if (err) ++ return err; ++ ++ return modify_user_hw_breakpoint(bp, &attr); ++} ++ ++static int ptrace_hbp_set_addr(unsigned int note_type, ++ struct task_struct *tsk, ++ unsigned long idx, ++ u64 addr) ++{ ++ int err; ++ struct perf_event *bp; ++ struct perf_event_attr attr; ++ ++ bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx); ++ if (IS_ERR(bp)) { ++ err = PTR_ERR(bp); ++ return err; ++ } ++ ++ attr = bp->attr; ++ attr.bp_addr = addr; ++ err = modify_user_hw_breakpoint(bp, &attr); ++ return err; ++} ++ ++#define PTRACE_HBP_ADDR_SZ sizeof(u64) ++#define PTRACE_HBP_CTRL_SZ sizeof(u32) ++#define PTRACE_HBP_PAD_SZ sizeof(u32) ++ ++static int hw_break_get(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ void *kbuf, void __user *ubuf) ++{ ++ unsigned int note_type = regset->core_note_type; ++ int ret, idx = 0, offset, limit; ++ u32 info, ctrl; ++ u64 addr; ++ ++ /* Resource info */ ++ ret = ptrace_hbp_get_resource_info(note_type, &info); ++ if (ret) ++ return ret; ++ ++ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &info, 0, ++ sizeof(info)); ++ if (ret) ++ return ret; ++ ++ /* Pad */ ++ offset = offsetof(struct user_hwdebug_state, pad); ++ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, offset, ++ offset + PTRACE_HBP_PAD_SZ); ++ if (ret) ++ return ret; ++ ++ /* (address, ctrl) registers */ ++ offset = offsetof(struct user_hwdebug_state, dbg_regs); ++ limit = regset->n * regset->size; ++ while (count && offset < limit) { ++ ret = ptrace_hbp_get_addr(note_type, target, idx, &addr); ++ if (ret) ++ return ret; ++ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &addr, ++ offset, offset + PTRACE_HBP_ADDR_SZ); ++ if (ret) ++ return ret; ++ offset += PTRACE_HBP_ADDR_SZ; ++ ++ ret = ptrace_hbp_get_ctrl(note_type, target, idx, &ctrl); ++ if (ret) ++ return ret; ++ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &ctrl, ++ offset, offset + PTRACE_HBP_CTRL_SZ); ++ if (ret) ++ return ret; ++ offset += PTRACE_HBP_CTRL_SZ; ++ ++ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, ++ offset, ++ offset + PTRACE_HBP_PAD_SZ); ++ if (ret) ++ return ret; ++ offset += PTRACE_HBP_PAD_SZ; ++ idx++; ++ } ++ ++ return 0; ++} ++ ++static int hw_break_set(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ const void *kbuf, const void __user *ubuf) ++{ ++ unsigned int note_type = regset->core_note_type; ++ int ret, idx = 0, offset, limit; ++ u32 ctrl; ++ u64 addr; ++ ++ /* Resource info and pad */ ++ offset = offsetof(struct user_hwdebug_state, dbg_regs); ++ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, offset); ++ if (ret) ++ return ret; ++ ++ /* (address, ctrl) registers */ ++ limit = regset->n * regset->size; ++ while (count && offset < limit) { ++ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr, ++ offset, offset + PTRACE_HBP_ADDR_SZ); ++ if (ret) ++ return ret; ++ ret = ptrace_hbp_set_addr(note_type, target, idx, addr); ++ if (ret) ++ return ret; ++ offset += PTRACE_HBP_ADDR_SZ; ++ ++ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, ++ offset, offset + PTRACE_HBP_CTRL_SZ); ++ if (ret) ++ return ret; ++ ret = ptrace_hbp_set_ctrl(note_type, target, idx, ctrl); ++ if (ret) ++ return ret; ++ offset += PTRACE_HBP_CTRL_SZ; ++ ++ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, ++ offset, ++ offset + PTRACE_HBP_PAD_SZ); ++ if (ret) ++ return ret; ++ offset += PTRACE_HBP_PAD_SZ; ++ idx++; ++ } ++ ++ return 0; ++} ++#endif /* CONFIG_HAVE_HW_BREAKPOINT */ ++ ++static int gpr_get(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ void *kbuf, void __user *ubuf) ++{ ++ struct user_pt_regs *uregs = &task_pt_regs(target)->user_regs; ++ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); ++} ++ ++static int gpr_set(struct task_struct *target, const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ const void *kbuf, const void __user *ubuf) ++{ ++ int ret; ++ struct user_pt_regs newregs; ++ ++ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1); ++ if (ret) ++ return ret; ++ ++ if (!valid_user_regs(&newregs)) ++ return -EINVAL; ++ ++ task_pt_regs(target)->user_regs = newregs; ++ return 0; ++} ++ ++/* ++ * TODO: update fp accessors for lazy context switching (sync/flush hwstate) ++ */ ++static int fpr_get(struct task_struct *target, const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ void *kbuf, void __user *ubuf) ++{ ++ struct user_fpsimd_state *uregs; ++ uregs = &target->thread.fpsimd_state.user_fpsimd; ++ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); ++} ++ ++static int fpr_set(struct task_struct *target, const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ const void *kbuf, const void __user *ubuf) ++{ ++ int ret; ++ struct user_fpsimd_state newstate; ++ ++ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1); ++ if (ret) ++ return ret; ++ ++ target->thread.fpsimd_state.user_fpsimd = newstate; ++ return ret; ++} ++ ++static int tls_get(struct task_struct *target, const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ void *kbuf, void __user *ubuf) ++{ ++ unsigned long *tls = &target->thread.tp_value; ++ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1); ++} ++ ++static int tls_set(struct task_struct *target, const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ const void *kbuf, const void __user *ubuf) ++{ ++ int ret; ++ unsigned long tls; ++ ++ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); ++ if (ret) ++ return ret; ++ ++ target->thread.tp_value = tls; ++ return ret; ++} ++ ++enum aarch64_regset { ++ REGSET_GPR, ++ REGSET_FPR, ++ REGSET_TLS, ++#ifdef CONFIG_HAVE_HW_BREAKPOINT ++ REGSET_HW_BREAK, ++ REGSET_HW_WATCH, ++#endif ++}; ++ ++static const struct user_regset aarch64_regsets[] = { ++ [REGSET_GPR] = { ++ .core_note_type = NT_PRSTATUS, ++ .n = sizeof(struct user_pt_regs) / sizeof(u64), ++ .size = sizeof(u64), ++ .align = sizeof(u64), ++ .get = gpr_get, ++ .set = gpr_set ++ }, ++ [REGSET_FPR] = { ++ .core_note_type = NT_PRFPREG, ++ .n = sizeof(struct user_fpsimd_state) / sizeof(u32), ++ /* ++ * We pretend we have 32-bit registers because the fpsr and ++ * fpcr are 32-bits wide. ++ */ ++ .size = sizeof(u32), ++ .align = sizeof(u32), ++ .get = fpr_get, ++ .set = fpr_set ++ }, ++ [REGSET_TLS] = { ++ .core_note_type = NT_ARM_TLS, ++ .n = 1, ++ .size = sizeof(void *), ++ .align = sizeof(void *), ++ .get = tls_get, ++ .set = tls_set, ++ }, ++#ifdef CONFIG_HAVE_HW_BREAKPOINT ++ [REGSET_HW_BREAK] = { ++ .core_note_type = NT_ARM_HW_BREAK, ++ .n = sizeof(struct user_hwdebug_state) / sizeof(u32), ++ .size = sizeof(u32), ++ .align = sizeof(u32), ++ .get = hw_break_get, ++ .set = hw_break_set, ++ }, ++ [REGSET_HW_WATCH] = { ++ .core_note_type = NT_ARM_HW_WATCH, ++ .n = sizeof(struct user_hwdebug_state) / sizeof(u32), ++ .size = sizeof(u32), ++ .align = sizeof(u32), ++ .get = hw_break_get, ++ .set = hw_break_set, ++ }, ++#endif ++}; ++ ++static const struct user_regset_view user_aarch64_view = { ++ .name = "aarch64", .e_machine = EM_AARCH64, ++ .regsets = aarch64_regsets, .n = ARRAY_SIZE(aarch64_regsets) ++}; ++ ++#ifdef CONFIG_COMPAT ++#include ++ ++enum compat_regset { ++ REGSET_COMPAT_GPR, ++ REGSET_COMPAT_VFP, ++}; ++ ++static int compat_gpr_get(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ void *kbuf, void __user *ubuf) ++{ ++ int ret = 0; ++ unsigned int i, start, num_regs; ++ ++ /* Calculate the number of AArch32 registers contained in count */ ++ num_regs = count / regset->size; ++ ++ /* Convert pos into an register number */ ++ start = pos / regset->size; ++ ++ if (start + num_regs > regset->n) ++ return -EIO; ++ ++ for (i = 0; i < num_regs; ++i) { ++ unsigned int idx = start + i; ++ compat_ulong_t reg; ++ ++ switch (idx) { ++ case 15: ++ reg = task_pt_regs(target)->pc; ++ break; ++ case 16: ++ reg = task_pt_regs(target)->pstate; ++ break; ++ case 17: ++ reg = task_pt_regs(target)->orig_x0; ++ break; ++ default: ++ reg = task_pt_regs(target)->regs[idx]; ++ } ++ ++ if (kbuf) { ++ memcpy(kbuf, ®, sizeof(reg)); ++ kbuf += sizeof(reg); ++ } else { ++ ret = copy_to_user(ubuf, ®, sizeof(reg)); ++ if (ret) ++ break; ++ ++ ubuf += sizeof(reg); ++ } ++ } ++ ++ return ret; ++} ++ ++static int compat_gpr_set(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ const void *kbuf, const void __user *ubuf) ++{ ++ struct pt_regs newregs; ++ int ret = 0; ++ unsigned int i, start, num_regs; ++ ++ /* Calculate the number of AArch32 registers contained in count */ ++ num_regs = count / regset->size; ++ ++ /* Convert pos into an register number */ ++ start = pos / regset->size; ++ ++ if (start + num_regs > regset->n) ++ return -EIO; ++ ++ newregs = *task_pt_regs(target); ++ ++ for (i = 0; i < num_regs; ++i) { ++ unsigned int idx = start + i; ++ compat_ulong_t reg; ++ ++ if (kbuf) { ++ memcpy(®, kbuf, sizeof(reg)); ++ kbuf += sizeof(reg); ++ } else { ++ ret = copy_from_user(®, ubuf, sizeof(reg)); ++ if (ret) ++ return ret; ++ ++ ubuf += sizeof(reg); ++ } ++ ++ switch (idx) { ++ case 15: ++ newregs.pc = reg; ++ break; ++ case 16: ++ newregs.pstate = reg; ++ break; ++ case 17: ++ newregs.orig_x0 = reg; ++ break; ++ default: ++ newregs.regs[idx] = reg; ++ } ++ ++ } ++ ++ if (valid_user_regs(&newregs.user_regs)) ++ *task_pt_regs(target) = newregs; ++ else ++ ret = -EINVAL; ++ ++ return ret; ++} ++ ++static int compat_vfp_get(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ void *kbuf, void __user *ubuf) ++{ ++ struct user_fpsimd_state *uregs; ++ compat_ulong_t fpscr; ++ int ret; ++ ++ uregs = &target->thread.fpsimd_state.user_fpsimd; ++ ++ /* ++ * The VFP registers are packed into the fpsimd_state, so they all sit ++ * nicely together for us. We just need to create the fpscr separately. ++ */ ++ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, ++ VFP_STATE_SIZE - sizeof(compat_ulong_t)); ++ ++ if (count && !ret) { ++ fpscr = (uregs->fpsr & VFP_FPSCR_STAT_MASK) | ++ (uregs->fpcr & VFP_FPSCR_CTRL_MASK); ++ ret = put_user(fpscr, (compat_ulong_t *)ubuf); ++ } ++ ++ return ret; ++} ++ ++static int compat_vfp_set(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ const void *kbuf, const void __user *ubuf) ++{ ++ struct user_fpsimd_state *uregs; ++ compat_ulong_t fpscr; ++ int ret; ++ ++ if (pos + count > VFP_STATE_SIZE) ++ return -EIO; ++ ++ uregs = &target->thread.fpsimd_state.user_fpsimd; ++ ++ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0, ++ VFP_STATE_SIZE - sizeof(compat_ulong_t)); ++ ++ if (count && !ret) { ++ ret = get_user(fpscr, (compat_ulong_t *)ubuf); ++ uregs->fpsr = fpscr & VFP_FPSCR_STAT_MASK; ++ uregs->fpcr = fpscr & VFP_FPSCR_CTRL_MASK; ++ } ++ ++ return ret; ++} ++ ++static const struct user_regset aarch32_regsets[] = { ++ [REGSET_COMPAT_GPR] = { ++ .core_note_type = NT_PRSTATUS, ++ .n = COMPAT_ELF_NGREG, ++ .size = sizeof(compat_elf_greg_t), ++ .align = sizeof(compat_elf_greg_t), ++ .get = compat_gpr_get, ++ .set = compat_gpr_set ++ }, ++ [REGSET_COMPAT_VFP] = { ++ .core_note_type = NT_ARM_VFP, ++ .n = VFP_STATE_SIZE / sizeof(compat_ulong_t), ++ .size = sizeof(compat_ulong_t), ++ .align = sizeof(compat_ulong_t), ++ .get = compat_vfp_get, ++ .set = compat_vfp_set ++ }, ++}; ++ ++static const struct user_regset_view user_aarch32_view = { ++ .name = "aarch32", .e_machine = EM_ARM, ++ .regsets = aarch32_regsets, .n = ARRAY_SIZE(aarch32_regsets) ++}; ++ ++static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off, ++ compat_ulong_t __user *ret) ++{ ++ compat_ulong_t tmp; ++ ++ if (off & 3) ++ return -EIO; ++ ++ if (off == COMPAT_PT_TEXT_ADDR) ++ tmp = tsk->mm->start_code; ++ else if (off == COMPAT_PT_DATA_ADDR) ++ tmp = tsk->mm->start_data; ++ else if (off == COMPAT_PT_TEXT_END_ADDR) ++ tmp = tsk->mm->end_code; ++ else if (off < sizeof(compat_elf_gregset_t)) ++ return copy_regset_to_user(tsk, &user_aarch32_view, ++ REGSET_COMPAT_GPR, off, ++ sizeof(compat_ulong_t), ret); ++ else if (off >= COMPAT_USER_SZ) ++ return -EIO; ++ else ++ tmp = 0; ++ ++ return put_user(tmp, ret); ++} ++ ++static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off, ++ compat_ulong_t val) ++{ ++ int ret; ++ mm_segment_t old_fs = get_fs(); ++ ++ if (off & 3 || off >= COMPAT_USER_SZ) ++ return -EIO; ++ ++ if (off >= sizeof(compat_elf_gregset_t)) ++ return 0; ++ ++ set_fs(KERNEL_DS); ++ ret = copy_regset_from_user(tsk, &user_aarch32_view, ++ REGSET_COMPAT_GPR, off, ++ sizeof(compat_ulong_t), ++ &val); ++ set_fs(old_fs); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_HAVE_HW_BREAKPOINT ++ ++/* ++ * Convert a virtual register number into an index for a thread_info ++ * breakpoint array. Breakpoints are identified using positive numbers ++ * whilst watchpoints are negative. The registers are laid out as pairs ++ * of (address, control), each pair mapping to a unique hw_breakpoint struct. ++ * Register 0 is reserved for describing resource information. ++ */ ++static int compat_ptrace_hbp_num_to_idx(compat_long_t num) ++{ ++ return (abs(num) - 1) >> 1; ++} ++ ++static int compat_ptrace_hbp_get_resource_info(u32 *kdata) ++{ ++ u8 num_brps, num_wrps, debug_arch, wp_len; ++ u32 reg = 0; ++ ++ num_brps = hw_breakpoint_slots(TYPE_INST); ++ num_wrps = hw_breakpoint_slots(TYPE_DATA); ++ ++ debug_arch = debug_monitors_arch(); ++ wp_len = 8; ++ reg |= debug_arch; ++ reg <<= 8; ++ reg |= wp_len; ++ reg <<= 8; ++ reg |= num_wrps; ++ reg <<= 8; ++ reg |= num_brps; ++ ++ *kdata = reg; ++ return 0; ++} ++ ++static int compat_ptrace_hbp_get(unsigned int note_type, ++ struct task_struct *tsk, ++ compat_long_t num, ++ u32 *kdata) ++{ ++ u64 addr = 0; ++ u32 ctrl = 0; ++ ++ int err, idx = compat_ptrace_hbp_num_to_idx(num);; ++ ++ if (num & 1) { ++ err = ptrace_hbp_get_addr(note_type, tsk, idx, &addr); ++ *kdata = (u32)addr; ++ } else { ++ err = ptrace_hbp_get_ctrl(note_type, tsk, idx, &ctrl); ++ *kdata = ctrl; ++ } ++ ++ return err; ++} ++ ++static int compat_ptrace_hbp_set(unsigned int note_type, ++ struct task_struct *tsk, ++ compat_long_t num, ++ u32 *kdata) ++{ ++ u64 addr; ++ u32 ctrl; ++ ++ int err, idx = compat_ptrace_hbp_num_to_idx(num); ++ ++ if (num & 1) { ++ addr = *kdata; ++ err = ptrace_hbp_set_addr(note_type, tsk, idx, addr); ++ } else { ++ ctrl = *kdata; ++ err = ptrace_hbp_set_ctrl(note_type, tsk, idx, ctrl); ++ } ++ ++ return err; ++} ++ ++static int compat_ptrace_gethbpregs(struct task_struct *tsk, compat_long_t num, ++ compat_ulong_t __user *data) ++{ ++ int ret; ++ u32 kdata; ++ mm_segment_t old_fs = get_fs(); ++ ++ set_fs(KERNEL_DS); ++ /* Watchpoint */ ++ if (num < 0) { ++ ret = compat_ptrace_hbp_get(NT_ARM_HW_WATCH, tsk, num, &kdata); ++ /* Resource info */ ++ } else if (num == 0) { ++ ret = compat_ptrace_hbp_get_resource_info(&kdata); ++ /* Breakpoint */ ++ } else { ++ ret = compat_ptrace_hbp_get(NT_ARM_HW_BREAK, tsk, num, &kdata); ++ } ++ set_fs(old_fs); ++ ++ if (!ret) ++ ret = put_user(kdata, data); ++ ++ return ret; ++} ++ ++static int compat_ptrace_sethbpregs(struct task_struct *tsk, compat_long_t num, ++ compat_ulong_t __user *data) ++{ ++ int ret; ++ u32 kdata = 0; ++ mm_segment_t old_fs = get_fs(); ++ ++ if (num == 0) ++ return 0; ++ ++ ret = get_user(kdata, data); ++ if (ret) ++ return ret; ++ ++ set_fs(KERNEL_DS); ++ if (num < 0) ++ ret = compat_ptrace_hbp_set(NT_ARM_HW_WATCH, tsk, num, &kdata); ++ else ++ ret = compat_ptrace_hbp_set(NT_ARM_HW_BREAK, tsk, num, &kdata); ++ set_fs(old_fs); ++ ++ return ret; ++} ++#endif /* CONFIG_HAVE_HW_BREAKPOINT */ ++ ++long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ++ compat_ulong_t caddr, compat_ulong_t cdata) ++{ ++ unsigned long addr = caddr; ++ unsigned long data = cdata; ++ void __user *datap = compat_ptr(data); ++ int ret; ++ ++ switch (request) { ++ case PTRACE_PEEKUSR: ++ ret = compat_ptrace_read_user(child, addr, datap); ++ break; ++ ++ case PTRACE_POKEUSR: ++ ret = compat_ptrace_write_user(child, addr, data); ++ break; ++ ++ case COMPAT_PTRACE_GETREGS: ++ ret = copy_regset_to_user(child, ++ &user_aarch32_view, ++ REGSET_COMPAT_GPR, ++ 0, sizeof(compat_elf_gregset_t), ++ datap); ++ break; ++ ++ case COMPAT_PTRACE_SETREGS: ++ ret = copy_regset_from_user(child, ++ &user_aarch32_view, ++ REGSET_COMPAT_GPR, ++ 0, sizeof(compat_elf_gregset_t), ++ datap); ++ break; ++ ++ case COMPAT_PTRACE_GET_THREAD_AREA: ++ ret = put_user((compat_ulong_t)child->thread.tp_value, ++ (compat_ulong_t __user *)datap); ++ break; ++ ++ case COMPAT_PTRACE_SET_SYSCALL: ++ task_pt_regs(child)->syscallno = data; ++ ret = 0; ++ break; ++ ++ case COMPAT_PTRACE_GETVFPREGS: ++ ret = copy_regset_to_user(child, ++ &user_aarch32_view, ++ REGSET_COMPAT_VFP, ++ 0, VFP_STATE_SIZE, ++ datap); ++ break; ++ ++ case COMPAT_PTRACE_SETVFPREGS: ++ ret = copy_regset_from_user(child, ++ &user_aarch32_view, ++ REGSET_COMPAT_VFP, ++ 0, VFP_STATE_SIZE, ++ datap); ++ break; ++ ++#ifdef CONFIG_HAVE_HW_BREAKPOINT ++ case COMPAT_PTRACE_GETHBPREGS: ++ ret = compat_ptrace_gethbpregs(child, addr, datap); ++ break; ++ ++ case COMPAT_PTRACE_SETHBPREGS: ++ ret = compat_ptrace_sethbpregs(child, addr, datap); ++ break; ++#endif ++ ++ default: ++ ret = compat_ptrace_request(child, request, addr, ++ data); ++ break; ++ } ++ ++ return ret; ++} ++#endif /* CONFIG_COMPAT */ ++ ++const struct user_regset_view *task_user_regset_view(struct task_struct *task) ++{ ++#ifdef CONFIG_COMPAT ++ if (is_compat_thread(task_thread_info(task))) ++ return &user_aarch32_view; ++#endif ++ return &user_aarch64_view; ++} ++ ++long arch_ptrace(struct task_struct *child, long request, ++ unsigned long addr, unsigned long data) ++{ ++ return ptrace_request(child, request, addr, data); ++} ++ ++enum ptrace_syscall_dir { ++ PTRACE_SYSCALL_ENTER = 0, ++ PTRACE_SYSCALL_EXIT, ++}; ++ ++static void tracehook_report_syscall(struct pt_regs *regs, ++ enum ptrace_syscall_dir dir) ++{ ++ int regno; ++ unsigned long saved_reg; ++ ++ /* ++ * A scratch register (ip(r12) on AArch32, x7 on AArch64) is ++ * used to denote syscall entry/exit: ++ */ ++ regno = (is_compat_task() ? 12 : 7); ++ saved_reg = regs->regs[regno]; ++ regs->regs[regno] = dir; ++ ++ if (dir == PTRACE_SYSCALL_EXIT) ++ tracehook_report_syscall_exit(regs, 0); ++ else if (tracehook_report_syscall_entry(regs)) ++ regs->syscallno = ~0UL; ++ ++ regs->regs[regno] = saved_reg; ++} ++ ++asmlinkage int syscall_trace_enter(struct pt_regs *regs) ++{ ++ if (test_thread_flag(TIF_SYSCALL_TRACE)) ++ tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER); ++ ++ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) ++ trace_sys_enter(regs, regs->syscallno); ++ ++ return regs->syscallno; ++} ++ ++asmlinkage void syscall_trace_exit(struct pt_regs *regs) ++{ ++ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) ++ trace_sys_exit(regs, regs_return_value(regs)); ++ ++ if (test_thread_flag(TIF_SYSCALL_TRACE)) ++ tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT); ++} +diff -Nur linux-3.14.36/arch/arm64/kernel/return_address.c linux-openelec/arch/arm64/kernel/return_address.c +--- linux-3.14.36/arch/arm64/kernel/return_address.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/return_address.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,55 @@ ++/* ++ * arch/arm64/kernel/return_address.c ++ * ++ * Copyright (C) 2013 Linaro Limited ++ * Author: AKASHI Takahiro ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++#include ++ ++struct return_address_data { ++ unsigned int level; ++ void *addr; ++}; ++ ++static int save_return_addr(struct stackframe *frame, void *d) ++{ ++ struct return_address_data *data = d; ++ ++ if (!data->level) { ++ data->addr = (void *)frame->pc; ++ return 1; ++ } else { ++ --data->level; ++ return 0; ++ } ++} ++ ++void *return_address(unsigned int level) ++{ ++ struct return_address_data data; ++ struct stackframe frame; ++ register unsigned long current_sp asm ("sp"); ++ ++ data.level = level + 2; ++ data.addr = NULL; ++ ++ frame.fp = (unsigned long)__builtin_frame_address(0); ++ frame.sp = current_sp; ++ frame.pc = (unsigned long)return_address; /* dummy */ ++ ++ walk_stackframe(&frame, save_return_addr, &data); ++ ++ if (!data.level) ++ return data.addr; ++ else ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(return_address); +diff -Nur linux-3.14.36/arch/arm64/kernel/setup.c linux-openelec/arch/arm64/kernel/setup.c +--- linux-3.14.36/arch/arm64/kernel/setup.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/setup.c 2015-07-24 18:03:29.644842002 -0500 +@@ -71,6 +71,7 @@ + COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV|\ + COMPAT_HWCAP_LPAE) + unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT; ++unsigned int compat_elf_hwcap2 __read_mostly; + #endif + + static const char *cpu_name; +@@ -258,6 +259,38 @@ + block = (features >> 16) & 0xf; + if (block && !(block & 0x8)) + elf_hwcap |= HWCAP_CRC32; ++ ++#ifdef CONFIG_COMPAT ++ /* ++ * ID_ISAR5_EL1 carries similar information as above, but pertaining to ++ * the Aarch32 32-bit execution state. ++ */ ++ features = read_cpuid(ID_ISAR5_EL1); ++ block = (features >> 4) & 0xf; ++ if (!(block & 0x8)) { ++ switch (block) { ++ default: ++ case 2: ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_PMULL; ++ case 1: ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_AES; ++ case 0: ++ break; ++ } ++ } ++ ++ block = (features >> 8) & 0xf; ++ if (block && !(block & 0x8)) ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA1; ++ ++ block = (features >> 12) & 0xf; ++ if (block && !(block & 0x8)) ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA2; ++ ++ block = (features >> 16) & 0xf; ++ if (block && !(block & 0x8)) ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_CRC32; ++#endif + } + + static void __init setup_machine_fdt(phys_addr_t dt_phys) +@@ -374,7 +407,7 @@ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + return 0; + } +-arch_initcall(arm64_device_init); ++arch_initcall_sync(arm64_device_init); + + static int __init topology_init(void) + { +diff -Nur linux-3.14.36/arch/arm64/kernel/setup.c.orig linux-openelec/arch/arm64/kernel/setup.c.orig +--- linux-3.14.36/arch/arm64/kernel/setup.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/setup.c.orig 2015-07-24 18:03:29.256842002 -0500 +@@ -0,0 +1,485 @@ ++/* ++ * Based on arch/arm/kernel/setup.c ++ * ++ * Copyright (C) 1995-2001 Russell King ++ * Copyright (C) 2012 ARM Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++unsigned int processor_id; ++EXPORT_SYMBOL(processor_id); ++ ++unsigned long elf_hwcap __read_mostly; ++EXPORT_SYMBOL_GPL(elf_hwcap); ++ ++#ifdef CONFIG_COMPAT ++#define COMPAT_ELF_HWCAP_DEFAULT \ ++ (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\ ++ COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\ ++ COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\ ++ COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\ ++ COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV|\ ++ COMPAT_HWCAP_LPAE) ++unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT; ++unsigned int compat_elf_hwcap2 __read_mostly; ++#endif ++ ++static const char *cpu_name; ++static const char *machine_name; ++phys_addr_t __fdt_pointer __initdata; ++ ++/* ++ * Standard memory resources ++ */ ++static struct resource mem_res[] = { ++ { ++ .name = "Kernel code", ++ .start = 0, ++ .end = 0, ++ .flags = IORESOURCE_MEM ++ }, ++ { ++ .name = "Kernel data", ++ .start = 0, ++ .end = 0, ++ .flags = IORESOURCE_MEM ++ } ++}; ++ ++#define kernel_code mem_res[0] ++#define kernel_data mem_res[1] ++ ++void __init early_print(const char *str, ...) ++{ ++ char buf[256]; ++ va_list ap; ++ ++ va_start(ap, str); ++ vsnprintf(buf, sizeof(buf), str, ap); ++ va_end(ap); ++ ++ printk("%s", buf); ++} ++ ++void __init smp_setup_processor_id(void) ++{ ++ /* ++ * clear __my_cpu_offset on boot CPU to avoid hang caused by ++ * using percpu variable early, for example, lockdep will ++ * access percpu variable inside lock_release ++ */ ++ set_my_cpu_offset(0); ++} ++ ++bool arch_match_cpu_phys_id(int cpu, u64 phys_id) ++{ ++ return phys_id == cpu_logical_map(cpu); ++} ++ ++struct mpidr_hash mpidr_hash; ++#ifdef CONFIG_SMP ++/** ++ * smp_build_mpidr_hash - Pre-compute shifts required at each affinity ++ * level in order to build a linear index from an ++ * MPIDR value. Resulting algorithm is a collision ++ * free hash carried out through shifting and ORing ++ */ ++static void __init smp_build_mpidr_hash(void) ++{ ++ u32 i, affinity, fs[4], bits[4], ls; ++ u64 mask = 0; ++ /* ++ * Pre-scan the list of MPIDRS and filter out bits that do ++ * not contribute to affinity levels, ie they never toggle. ++ */ ++ for_each_possible_cpu(i) ++ mask |= (cpu_logical_map(i) ^ cpu_logical_map(0)); ++ pr_debug("mask of set bits %#llx\n", mask); ++ /* ++ * Find and stash the last and first bit set at all affinity levels to ++ * check how many bits are required to represent them. ++ */ ++ for (i = 0; i < 4; i++) { ++ affinity = MPIDR_AFFINITY_LEVEL(mask, i); ++ /* ++ * Find the MSB bit and LSB bits position ++ * to determine how many bits are required ++ * to express the affinity level. ++ */ ++ ls = fls(affinity); ++ fs[i] = affinity ? ffs(affinity) - 1 : 0; ++ bits[i] = ls - fs[i]; ++ } ++ /* ++ * An index can be created from the MPIDR_EL1 by isolating the ++ * significant bits at each affinity level and by shifting ++ * them in order to compress the 32 bits values space to a ++ * compressed set of values. This is equivalent to hashing ++ * the MPIDR_EL1 through shifting and ORing. It is a collision free ++ * hash though not minimal since some levels might contain a number ++ * of CPUs that is not an exact power of 2 and their bit ++ * representation might contain holes, eg MPIDR_EL1[7:0] = {0x2, 0x80}. ++ */ ++ mpidr_hash.shift_aff[0] = MPIDR_LEVEL_SHIFT(0) + fs[0]; ++ mpidr_hash.shift_aff[1] = MPIDR_LEVEL_SHIFT(1) + fs[1] - bits[0]; ++ mpidr_hash.shift_aff[2] = MPIDR_LEVEL_SHIFT(2) + fs[2] - ++ (bits[1] + bits[0]); ++ mpidr_hash.shift_aff[3] = MPIDR_LEVEL_SHIFT(3) + ++ fs[3] - (bits[2] + bits[1] + bits[0]); ++ mpidr_hash.mask = mask; ++ mpidr_hash.bits = bits[3] + bits[2] + bits[1] + bits[0]; ++ pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] aff3[%u] mask[%#llx] bits[%u]\n", ++ mpidr_hash.shift_aff[0], ++ mpidr_hash.shift_aff[1], ++ mpidr_hash.shift_aff[2], ++ mpidr_hash.shift_aff[3], ++ mpidr_hash.mask, ++ mpidr_hash.bits); ++ /* ++ * 4x is an arbitrary value used to warn on a hash table much bigger ++ * than expected on most systems. ++ */ ++ if (mpidr_hash_size() > 4 * num_possible_cpus()) ++ pr_warn("Large number of MPIDR hash buckets detected\n"); ++ __flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash)); ++} ++#endif ++ ++static void __init setup_processor(void) ++{ ++ struct cpu_info *cpu_info; ++ u64 features, block; ++ ++ cpu_info = lookup_processor_type(read_cpuid_id()); ++ if (!cpu_info) { ++ printk("CPU configuration botched (ID %08x), unable to continue.\n", ++ read_cpuid_id()); ++ while (1); ++ } ++ ++ cpu_name = cpu_info->cpu_name; ++ ++ printk("CPU: %s [%08x] revision %d\n", ++ cpu_name, read_cpuid_id(), read_cpuid_id() & 15); ++ ++ sprintf(init_utsname()->machine, ELF_PLATFORM); ++ elf_hwcap = 0; ++ ++ /* ++ * ID_AA64ISAR0_EL1 contains 4-bit wide signed feature blocks. ++ * The blocks we test below represent incremental functionality ++ * for non-negative values. Negative values are reserved. ++ */ ++ features = read_cpuid(ID_AA64ISAR0_EL1); ++ block = (features >> 4) & 0xf; ++ if (!(block & 0x8)) { ++ switch (block) { ++ default: ++ case 2: ++ elf_hwcap |= HWCAP_PMULL; ++ case 1: ++ elf_hwcap |= HWCAP_AES; ++ case 0: ++ break; ++ } ++ } ++ ++ block = (features >> 8) & 0xf; ++ if (block && !(block & 0x8)) ++ elf_hwcap |= HWCAP_SHA1; ++ ++ block = (features >> 12) & 0xf; ++ if (block && !(block & 0x8)) ++ elf_hwcap |= HWCAP_SHA2; ++ ++ block = (features >> 16) & 0xf; ++ if (block && !(block & 0x8)) ++ elf_hwcap |= HWCAP_CRC32; ++ ++#ifdef CONFIG_COMPAT ++ /* ++ * ID_ISAR5_EL1 carries similar information as above, but pertaining to ++ * the Aarch32 32-bit execution state. ++ */ ++ features = read_cpuid(ID_ISAR5_EL1); ++ block = (features >> 4) & 0xf; ++ if (!(block & 0x8)) { ++ switch (block) { ++ default: ++ case 2: ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_PMULL; ++ case 1: ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_AES; ++ case 0: ++ break; ++ } ++ } ++ ++ block = (features >> 8) & 0xf; ++ if (block && !(block & 0x8)) ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA1; ++ ++ block = (features >> 12) & 0xf; ++ if (block && !(block & 0x8)) ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA2; ++ ++ block = (features >> 16) & 0xf; ++ if (block && !(block & 0x8)) ++ compat_elf_hwcap2 |= COMPAT_HWCAP2_CRC32; ++#endif ++} ++ ++static void __init setup_machine_fdt(phys_addr_t dt_phys) ++{ ++ if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) { ++ early_print("\n" ++ "Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n" ++ "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n" ++ "\nPlease check your bootloader.\n", ++ dt_phys, phys_to_virt(dt_phys)); ++ ++ while (true) ++ cpu_relax(); ++ } ++ ++ machine_name = of_flat_dt_get_machine_name(); ++} ++ ++/* ++ * Limit the memory size that was specified via FDT. ++ */ ++static int __init early_mem(char *p) ++{ ++ phys_addr_t limit; ++ ++ if (!p) ++ return 1; ++ ++ limit = memparse(p, &p) & PAGE_MASK; ++ pr_notice("Memory limited to %lldMB\n", limit >> 20); ++ ++ memblock_enforce_memory_limit(limit); ++ ++ return 0; ++} ++early_param("mem", early_mem); ++ ++static void __init request_standard_resources(void) ++{ ++ struct memblock_region *region; ++ struct resource *res; ++ ++ kernel_code.start = virt_to_phys(_text); ++ kernel_code.end = virt_to_phys(_etext - 1); ++ kernel_data.start = virt_to_phys(_sdata); ++ kernel_data.end = virt_to_phys(_end - 1); ++ ++ for_each_memblock(memory, region) { ++ res = alloc_bootmem_low(sizeof(*res)); ++ res->name = "System RAM"; ++ res->start = __pfn_to_phys(memblock_region_memory_base_pfn(region)); ++ res->end = __pfn_to_phys(memblock_region_memory_end_pfn(region)) - 1; ++ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ ++ request_resource(&iomem_resource, res); ++ ++ if (kernel_code.start >= res->start && ++ kernel_code.end <= res->end) ++ request_resource(res, &kernel_code); ++ if (kernel_data.start >= res->start && ++ kernel_data.end <= res->end) ++ request_resource(res, &kernel_data); ++ } ++} ++ ++u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID }; ++ ++void __init setup_arch(char **cmdline_p) ++{ ++ /* ++ * Unmask asynchronous aborts early to catch possible system errors. ++ */ ++ local_async_enable(); ++ ++ setup_processor(); ++ ++ setup_machine_fdt(__fdt_pointer); ++ ++ init_mm.start_code = (unsigned long) _text; ++ init_mm.end_code = (unsigned long) _etext; ++ init_mm.end_data = (unsigned long) _edata; ++ init_mm.brk = (unsigned long) _end; ++ ++ *cmdline_p = boot_command_line; ++ ++ parse_early_param(); ++ ++ arm64_memblock_init(); ++ ++ paging_init(); ++ request_standard_resources(); ++ ++ unflatten_device_tree(); ++ ++ psci_init(); ++ ++ cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; ++ cpu_read_bootcpu_ops(); ++#ifdef CONFIG_SMP ++ smp_init_cpus(); ++ smp_build_mpidr_hash(); ++#endif ++ ++#ifdef CONFIG_VT ++#if defined(CONFIG_VGA_CONSOLE) ++ conswitchp = &vga_con; ++#elif defined(CONFIG_DUMMY_CONSOLE) ++ conswitchp = &dummy_con; ++#endif ++#endif ++} ++ ++static int __init arm64_device_init(void) ++{ ++ of_clk_init(NULL); ++ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); ++ return 0; ++} ++arch_initcall_sync(arm64_device_init); ++ ++static DEFINE_PER_CPU(struct cpu, cpu_data); ++ ++static int __init topology_init(void) ++{ ++ int i; ++ ++ for_each_possible_cpu(i) { ++ struct cpu *cpu = &per_cpu(cpu_data, i); ++ cpu->hotpluggable = 1; ++ register_cpu(cpu, i); ++ } ++ ++ return 0; ++} ++subsys_initcall(topology_init); ++ ++static const char *hwcap_str[] = { ++ "fp", ++ "asimd", ++ "evtstrm", ++ "aes", ++ "pmull", ++ "sha1", ++ "sha2", ++ "crc32", ++ NULL ++}; ++ ++static int c_show(struct seq_file *m, void *v) ++{ ++ int i; ++ ++ seq_printf(m, "Processor\t: %s rev %d (%s)\n", ++ cpu_name, read_cpuid_id() & 15, ELF_PLATFORM); ++ ++ for_each_online_cpu(i) { ++ /* ++ * glibc reads /proc/cpuinfo to determine the number of ++ * online processors, looking for lines beginning with ++ * "processor". Give glibc what it expects. ++ */ ++#ifdef CONFIG_SMP ++ seq_printf(m, "processor\t: %d\n", i); ++#endif ++ } ++ ++ /* dump out the processor features */ ++ seq_puts(m, "Features\t: "); ++ ++ for (i = 0; hwcap_str[i]; i++) ++ if (elf_hwcap & (1 << i)) ++ seq_printf(m, "%s ", hwcap_str[i]); ++ ++ seq_printf(m, "\nCPU implementer\t: 0x%02x\n", read_cpuid_id() >> 24); ++ seq_printf(m, "CPU architecture: AArch64\n"); ++ seq_printf(m, "CPU variant\t: 0x%x\n", (read_cpuid_id() >> 20) & 15); ++ seq_printf(m, "CPU part\t: 0x%03x\n", (read_cpuid_id() >> 4) & 0xfff); ++ seq_printf(m, "CPU revision\t: %d\n", read_cpuid_id() & 15); ++ ++ seq_puts(m, "\n"); ++ ++ seq_printf(m, "Hardware\t: %s\n", machine_name); ++ ++ return 0; ++} ++ ++static void *c_start(struct seq_file *m, loff_t *pos) ++{ ++ return *pos < 1 ? (void *)1 : NULL; ++} ++ ++static void *c_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ ++*pos; ++ return NULL; ++} ++ ++static void c_stop(struct seq_file *m, void *v) ++{ ++} ++ ++const struct seq_operations cpuinfo_op = { ++ .start = c_start, ++ .next = c_next, ++ .stop = c_stop, ++ .show = c_show ++}; +diff -Nur linux-3.14.36/arch/arm64/kernel/signal.c linux-openelec/arch/arm64/kernel/signal.c +--- linux-3.14.36/arch/arm64/kernel/signal.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/signal.c 2015-05-06 12:05:43.000000000 -0500 +@@ -17,6 +17,7 @@ + * along with this program. If not, see . + */ + ++#include + #include + #include + #include +@@ -25,7 +26,6 @@ + #include + #include + +-#include + #include + #include + #include +diff -Nur linux-3.14.36/arch/arm64/kernel/smp.c linux-openelec/arch/arm64/kernel/smp.c +--- linux-3.14.36/arch/arm64/kernel/smp.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/smp.c 2015-07-24 18:03:29.644842002 -0500 +@@ -114,6 +114,11 @@ + return ret; + } + ++static void smp_store_cpu_info(unsigned int cpuid) ++{ ++ store_cpu_topology(cpuid); ++} ++ + /* + * This is the secondary CPU boot entry. We're using this CPUs + * idle thread stack, but a set of temporary page tables. +@@ -157,6 +162,8 @@ + */ + notify_cpu_starting(cpu); + ++ smp_store_cpu_info(cpu); ++ + /* + * OK, now it's safe to let the boot CPU continue. Wait for + * the CPU migration code to notice that the CPU is online +@@ -395,6 +402,10 @@ + int err; + unsigned int cpu, ncores = num_possible_cpus(); + ++ init_cpu_topology(); ++ ++ smp_store_cpu_info(smp_processor_id()); ++ + /* + * are we trying to boot more cores than exist? + */ +diff -Nur linux-3.14.36/arch/arm64/kernel/smp.c.orig linux-openelec/arch/arm64/kernel/smp.c.orig +--- linux-3.14.36/arch/arm64/kernel/smp.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/smp.c.orig 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,603 @@ ++/* ++ * SMP initialisation and IPI support ++ * Based on arch/arm/kernel/smp.c ++ * ++ * Copyright (C) 2012 ARM Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * as from 2.5, kernels no longer have an init_tasks structure ++ * so we need some other way of telling a new secondary core ++ * where to place its SVC stack ++ */ ++struct secondary_data secondary_data; ++ ++enum ipi_msg_type { ++ IPI_RESCHEDULE, ++ IPI_CALL_FUNC, ++ IPI_CALL_FUNC_SINGLE, ++ IPI_CPU_STOP, ++ IPI_TIMER, ++}; ++ ++/* ++ * Boot a secondary CPU, and assign it the specified idle task. ++ * This also gives us the initial stack to use for this CPU. ++ */ ++static int boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ if (cpu_ops[cpu]->cpu_boot) ++ return cpu_ops[cpu]->cpu_boot(cpu); ++ ++ return -EOPNOTSUPP; ++} ++ ++static DECLARE_COMPLETION(cpu_running); ++ ++int __cpu_up(unsigned int cpu, struct task_struct *idle) ++{ ++ int ret; ++ ++ /* ++ * We need to tell the secondary core where to find its stack and the ++ * page tables. ++ */ ++ secondary_data.stack = task_stack_page(idle) + THREAD_START_SP; ++ __flush_dcache_area(&secondary_data, sizeof(secondary_data)); ++ ++ /* ++ * Now bring the CPU into our world. ++ */ ++ ret = boot_secondary(cpu, idle); ++ if (ret == 0) { ++ /* ++ * CPU was successfully started, wait for it to come online or ++ * time out. ++ */ ++ wait_for_completion_timeout(&cpu_running, ++ msecs_to_jiffies(1000)); ++ ++ if (!cpu_online(cpu)) { ++ pr_crit("CPU%u: failed to come online\n", cpu); ++ ret = -EIO; ++ } ++ } else { ++ pr_err("CPU%u: failed to boot: %d\n", cpu, ret); ++ } ++ ++ secondary_data.stack = NULL; ++ ++ return ret; ++} ++ ++static void smp_store_cpu_info(unsigned int cpuid) ++{ ++ store_cpu_topology(cpuid); ++} ++ ++/* ++ * This is the secondary CPU boot entry. We're using this CPUs ++ * idle thread stack, but a set of temporary page tables. ++ */ ++asmlinkage void secondary_start_kernel(void) ++{ ++ struct mm_struct *mm = &init_mm; ++ unsigned int cpu = smp_processor_id(); ++ ++ /* ++ * All kernel threads share the same mm context; grab a ++ * reference and switch to it. ++ */ ++ atomic_inc(&mm->mm_count); ++ current->active_mm = mm; ++ cpumask_set_cpu(cpu, mm_cpumask(mm)); ++ ++ set_my_cpu_offset(per_cpu_offset(smp_processor_id())); ++ printk("CPU%u: Booted secondary processor\n", cpu); ++ ++ /* ++ * TTBR0 is only used for the identity mapping at this stage. Make it ++ * point to zero page to avoid speculatively fetching new entries. ++ */ ++ cpu_set_reserved_ttbr0(); ++ flush_tlb_all(); ++ ++ preempt_disable(); ++ trace_hardirqs_off(); ++ ++ if (cpu_ops[cpu]->cpu_postboot) ++ cpu_ops[cpu]->cpu_postboot(); ++ ++ /* ++ * Enable GIC and timers. ++ */ ++ notify_cpu_starting(cpu); ++ ++ smp_store_cpu_info(cpu); ++ ++ /* ++ * OK, now it's safe to let the boot CPU continue. Wait for ++ * the CPU migration code to notice that the CPU is online ++ * before we continue. ++ */ ++ set_cpu_online(cpu, true); ++ complete(&cpu_running); ++ ++ local_irq_enable(); ++ local_async_enable(); ++ ++ /* ++ * OK, it's off to the idle thread for us ++ */ ++ cpu_startup_entry(CPUHP_ONLINE); ++} ++ ++#ifdef CONFIG_HOTPLUG_CPU ++static int op_cpu_disable(unsigned int cpu) ++{ ++ /* ++ * If we don't have a cpu_die method, abort before we reach the point ++ * of no return. CPU0 may not have an cpu_ops, so test for it. ++ */ ++ if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_die) ++ return -EOPNOTSUPP; ++ ++ /* ++ * We may need to abort a hot unplug for some other mechanism-specific ++ * reason. ++ */ ++ if (cpu_ops[cpu]->cpu_disable) ++ return cpu_ops[cpu]->cpu_disable(cpu); ++ ++ return 0; ++} ++ ++/* ++ * __cpu_disable runs on the processor to be shutdown. ++ */ ++int __cpu_disable(void) ++{ ++ unsigned int cpu = smp_processor_id(); ++ int ret; ++ ++ ret = op_cpu_disable(cpu); ++ if (ret) ++ return ret; ++ ++ /* ++ * Take this CPU offline. Once we clear this, we can't return, ++ * and we must not schedule until we're ready to give up the cpu. ++ */ ++ set_cpu_online(cpu, false); ++ ++ /* ++ * OK - migrate IRQs away from this CPU ++ */ ++ migrate_irqs(); ++ ++ /* ++ * Remove this CPU from the vm mask set of all processes. ++ */ ++ clear_tasks_mm_cpumask(cpu); ++ ++ return 0; ++} ++ ++static DECLARE_COMPLETION(cpu_died); ++ ++/* ++ * called on the thread which is asking for a CPU to be shutdown - ++ * waits until shutdown has completed, or it is timed out. ++ */ ++void __cpu_die(unsigned int cpu) ++{ ++ if (!wait_for_completion_timeout(&cpu_died, msecs_to_jiffies(5000))) { ++ pr_crit("CPU%u: cpu didn't die\n", cpu); ++ return; ++ } ++ pr_notice("CPU%u: shutdown\n", cpu); ++} ++ ++/* ++ * Called from the idle thread for the CPU which has been shutdown. ++ * ++ * Note that we disable IRQs here, but do not re-enable them ++ * before returning to the caller. This is also the behaviour ++ * of the other hotplug-cpu capable cores, so presumably coming ++ * out of idle fixes this. ++ */ ++void cpu_die(void) ++{ ++ unsigned int cpu = smp_processor_id(); ++ ++ idle_task_exit(); ++ ++ local_irq_disable(); ++ ++ /* Tell __cpu_die() that this CPU is now safe to dispose of */ ++ complete(&cpu_died); ++ ++ /* ++ * Actually shutdown the CPU. This must never fail. The specific hotplug ++ * mechanism must perform all required cache maintenance to ensure that ++ * no dirty lines are lost in the process of shutting down the CPU. ++ */ ++ cpu_ops[cpu]->cpu_die(cpu); ++ ++ BUG(); ++} ++#endif ++ ++void __init smp_cpus_done(unsigned int max_cpus) ++{ ++ pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); ++} ++ ++void __init smp_prepare_boot_cpu(void) ++{ ++ set_my_cpu_offset(per_cpu_offset(smp_processor_id())); ++} ++ ++static void (*smp_cross_call)(const struct cpumask *, unsigned int); ++ ++/* ++ * Enumerate the possible CPU set from the device tree and build the ++ * cpu logical map array containing MPIDR values related to logical ++ * cpus. Assumes that cpu_logical_map(0) has already been initialized. ++ */ ++void __init smp_init_cpus(void) ++{ ++ struct device_node *dn = NULL; ++ unsigned int i, cpu = 1; ++ bool bootcpu_valid = false; ++ ++ while ((dn = of_find_node_by_type(dn, "cpu"))) { ++ const u32 *cell; ++ u64 hwid; ++ ++ /* ++ * A cpu node with missing "reg" property is ++ * considered invalid to build a cpu_logical_map ++ * entry. ++ */ ++ cell = of_get_property(dn, "reg", NULL); ++ if (!cell) { ++ pr_err("%s: missing reg property\n", dn->full_name); ++ goto next; ++ } ++ hwid = of_read_number(cell, of_n_addr_cells(dn)); ++ ++ /* ++ * Non affinity bits must be set to 0 in the DT ++ */ ++ if (hwid & ~MPIDR_HWID_BITMASK) { ++ pr_err("%s: invalid reg property\n", dn->full_name); ++ goto next; ++ } ++ ++ /* ++ * Duplicate MPIDRs are a recipe for disaster. Scan ++ * all initialized entries and check for ++ * duplicates. If any is found just ignore the cpu. ++ * cpu_logical_map was initialized to INVALID_HWID to ++ * avoid matching valid MPIDR values. ++ */ ++ for (i = 1; (i < cpu) && (i < NR_CPUS); i++) { ++ if (cpu_logical_map(i) == hwid) { ++ pr_err("%s: duplicate cpu reg properties in the DT\n", ++ dn->full_name); ++ goto next; ++ } ++ } ++ ++ /* ++ * The numbering scheme requires that the boot CPU ++ * must be assigned logical id 0. Record it so that ++ * the logical map built from DT is validated and can ++ * be used. ++ */ ++ if (hwid == cpu_logical_map(0)) { ++ if (bootcpu_valid) { ++ pr_err("%s: duplicate boot cpu reg property in DT\n", ++ dn->full_name); ++ goto next; ++ } ++ ++ bootcpu_valid = true; ++ ++ /* ++ * cpu_logical_map has already been ++ * initialized and the boot cpu doesn't need ++ * the enable-method so continue without ++ * incrementing cpu. ++ */ ++ continue; ++ } ++ ++ if (cpu >= NR_CPUS) ++ goto next; ++ ++ if (cpu_read_ops(dn, cpu) != 0) ++ goto next; ++ ++ if (cpu_ops[cpu]->cpu_init(dn, cpu)) ++ goto next; ++ ++ pr_debug("cpu logical map 0x%llx\n", hwid); ++ cpu_logical_map(cpu) = hwid; ++next: ++ cpu++; ++ } ++ ++ /* sanity check */ ++ if (cpu > NR_CPUS) ++ pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n", ++ cpu, NR_CPUS); ++ ++ if (!bootcpu_valid) { ++ pr_err("DT missing boot CPU MPIDR, not enabling secondaries\n"); ++ return; ++ } ++ ++ /* ++ * All the cpus that made it to the cpu_logical_map have been ++ * validated so set them as possible cpus. ++ */ ++ for (i = 0; i < NR_CPUS; i++) ++ if (cpu_logical_map(i) != INVALID_HWID) ++ set_cpu_possible(i, true); ++} ++ ++void __init smp_prepare_cpus(unsigned int max_cpus) ++{ ++ int err; ++ unsigned int cpu, ncores = num_possible_cpus(); ++ ++ init_cpu_topology(); ++ ++ smp_store_cpu_info(smp_processor_id()); ++ ++ /* ++ * are we trying to boot more cores than exist? ++ */ ++ if (max_cpus > ncores) ++ max_cpus = ncores; ++ ++ /* Don't bother if we're effectively UP */ ++ if (max_cpus <= 1) ++ return; ++ ++ /* ++ * Initialise the present map (which describes the set of CPUs ++ * actually populated at the present time) and release the ++ * secondaries from the bootloader. ++ * ++ * Make sure we online at most (max_cpus - 1) additional CPUs. ++ */ ++ max_cpus--; ++ for_each_possible_cpu(cpu) { ++ if (max_cpus == 0) ++ break; ++ ++ if (cpu == smp_processor_id()) ++ continue; ++ ++ if (!cpu_ops[cpu]) ++ continue; ++ ++ err = cpu_ops[cpu]->cpu_prepare(cpu); ++ if (err) ++ continue; ++ ++ set_cpu_present(cpu, true); ++ max_cpus--; ++ } ++} ++ ++ ++void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int)) ++{ ++ smp_cross_call = fn; ++} ++ ++void arch_send_call_function_ipi_mask(const struct cpumask *mask) ++{ ++ smp_cross_call(mask, IPI_CALL_FUNC); ++} ++ ++void arch_send_call_function_single_ipi(int cpu) ++{ ++ smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); ++} ++ ++static const char *ipi_types[NR_IPI] = { ++#define S(x,s) [x - IPI_RESCHEDULE] = s ++ S(IPI_RESCHEDULE, "Rescheduling interrupts"), ++ S(IPI_CALL_FUNC, "Function call interrupts"), ++ S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), ++ S(IPI_CPU_STOP, "CPU stop interrupts"), ++ S(IPI_TIMER, "Timer broadcast interrupts"), ++}; ++ ++void show_ipi_list(struct seq_file *p, int prec) ++{ ++ unsigned int cpu, i; ++ ++ for (i = 0; i < NR_IPI; i++) { ++ seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i + IPI_RESCHEDULE, ++ prec >= 4 ? " " : ""); ++ for_each_online_cpu(cpu) ++ seq_printf(p, "%10u ", ++ __get_irq_stat(cpu, ipi_irqs[i])); ++ seq_printf(p, " %s\n", ipi_types[i]); ++ } ++} ++ ++u64 smp_irq_stat_cpu(unsigned int cpu) ++{ ++ u64 sum = 0; ++ int i; ++ ++ for (i = 0; i < NR_IPI; i++) ++ sum += __get_irq_stat(cpu, ipi_irqs[i]); ++ ++ return sum; ++} ++ ++static DEFINE_RAW_SPINLOCK(stop_lock); ++ ++/* ++ * ipi_cpu_stop - handle IPI from smp_send_stop() ++ */ ++static void ipi_cpu_stop(unsigned int cpu) ++{ ++ if (system_state == SYSTEM_BOOTING || ++ system_state == SYSTEM_RUNNING) { ++ raw_spin_lock(&stop_lock); ++ pr_crit("CPU%u: stopping\n", cpu); ++ dump_stack(); ++ raw_spin_unlock(&stop_lock); ++ } ++ ++ set_cpu_online(cpu, false); ++ ++ local_irq_disable(); ++ ++ while (1) ++ cpu_relax(); ++} ++ ++/* ++ * Main handler for inter-processor interrupts ++ */ ++void handle_IPI(int ipinr, struct pt_regs *regs) ++{ ++ unsigned int cpu = smp_processor_id(); ++ struct pt_regs *old_regs = set_irq_regs(regs); ++ ++ if (ipinr >= IPI_RESCHEDULE && ipinr < IPI_RESCHEDULE + NR_IPI) ++ __inc_irq_stat(cpu, ipi_irqs[ipinr - IPI_RESCHEDULE]); ++ ++ switch (ipinr) { ++ case IPI_RESCHEDULE: ++ scheduler_ipi(); ++ break; ++ ++ case IPI_CALL_FUNC: ++ irq_enter(); ++ generic_smp_call_function_interrupt(); ++ irq_exit(); ++ break; ++ ++ case IPI_CALL_FUNC_SINGLE: ++ irq_enter(); ++ generic_smp_call_function_single_interrupt(); ++ irq_exit(); ++ break; ++ ++ case IPI_CPU_STOP: ++ irq_enter(); ++ ipi_cpu_stop(cpu); ++ irq_exit(); ++ break; ++ ++#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST ++ case IPI_TIMER: ++ irq_enter(); ++ tick_receive_broadcast(); ++ irq_exit(); ++ break; ++#endif ++ ++ default: ++ pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); ++ break; ++ } ++ set_irq_regs(old_regs); ++} ++ ++void smp_send_reschedule(int cpu) ++{ ++ smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE); ++} ++ ++#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST ++void tick_broadcast(const struct cpumask *mask) ++{ ++ smp_cross_call(mask, IPI_TIMER); ++} ++#endif ++ ++void smp_send_stop(void) ++{ ++ unsigned long timeout; ++ ++ if (num_online_cpus() > 1) { ++ cpumask_t mask; ++ ++ cpumask_copy(&mask, cpu_online_mask); ++ cpu_clear(smp_processor_id(), mask); ++ ++ smp_cross_call(&mask, IPI_CPU_STOP); ++ } ++ ++ /* Wait up to one second for other CPUs to stop */ ++ timeout = USEC_PER_SEC; ++ while (num_online_cpus() > 1 && timeout--) ++ udelay(1); ++ ++ if (num_online_cpus() > 1) ++ pr_warning("SMP: failed to stop secondary CPUs\n"); ++} ++ ++/* ++ * not supported here ++ */ ++int setup_profiling_timer(unsigned int multiplier) ++{ ++ return -EINVAL; ++} +diff -Nur linux-3.14.36/arch/arm64/kernel/stacktrace.c linux-openelec/arch/arm64/kernel/stacktrace.c +--- linux-3.14.36/arch/arm64/kernel/stacktrace.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/stacktrace.c 2015-05-06 12:05:43.000000000 -0500 +@@ -35,7 +35,7 @@ + * ldp x29, x30, [sp] + * add sp, sp, #0x10 + */ +-int unwind_frame(struct stackframe *frame) ++int notrace unwind_frame(struct stackframe *frame) + { + unsigned long high, low; + unsigned long fp = frame->fp; +diff -Nur linux-3.14.36/arch/arm64/kernel/topology.c linux-openelec/arch/arm64/kernel/topology.c +--- linux-3.14.36/arch/arm64/kernel/topology.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/arch/arm64/kernel/topology.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,558 @@ ++/* ++ * arch/arm64/kernel/topology.c ++ * ++ * Copyright (C) 2011,2013,2014 Linaro Limited. ++ * ++ * Based on the arm32 version written by Vincent Guittot in turn based on ++ * arch/sh/kernel/topology.c ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ++ * cpu power table ++ * This per cpu data structure describes the relative capacity of each core. ++ * On a heteregenous system, cores don't have the same computation capacity ++ * and we reflect that difference in the cpu_power field so the scheduler can ++ * take this difference into account during load balance. A per cpu structure ++ * is preferred because each CPU updates its own cpu_power field during the ++ * load balance except for idle cores. One idle core is selected to run the ++ * rebalance_domains for all idle cores and the cpu_power can be updated ++ * during this sequence. ++ */ ++static DEFINE_PER_CPU(unsigned long, cpu_scale); ++ ++unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu) ++{ ++ return per_cpu(cpu_scale, cpu); ++} ++ ++static void set_power_scale(unsigned int cpu, unsigned long power) ++{ ++ per_cpu(cpu_scale, cpu) = power; ++} ++ ++static int __init get_cpu_for_node(struct device_node *node) ++{ ++ struct device_node *cpu_node; ++ int cpu; ++ ++ cpu_node = of_parse_phandle(node, "cpu", 0); ++ if (!cpu_node) ++ return -1; ++ ++ for_each_possible_cpu(cpu) { ++ if (of_get_cpu_node(cpu, NULL) == cpu_node) { ++ of_node_put(cpu_node); ++ return cpu; ++ } ++ } ++ ++ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name); ++ ++ of_node_put(cpu_node); ++ return -1; ++} ++ ++static int __init parse_core(struct device_node *core, int cluster_id, ++ int core_id) ++{ ++ char name[10]; ++ bool leaf = true; ++ int i = 0; ++ int cpu; ++ struct device_node *t; ++ ++ do { ++ snprintf(name, sizeof(name), "thread%d", i); ++ t = of_get_child_by_name(core, name); ++ if (t) { ++ leaf = false; ++ cpu = get_cpu_for_node(t); ++ if (cpu >= 0) { ++ cpu_topology[cpu].cluster_id = cluster_id; ++ cpu_topology[cpu].core_id = core_id; ++ cpu_topology[cpu].thread_id = i; ++ } else { ++ pr_err("%s: Can't get CPU for thread\n", ++ t->full_name); ++ of_node_put(t); ++ return -EINVAL; ++ } ++ of_node_put(t); ++ } ++ i++; ++ } while (t); ++ ++ cpu = get_cpu_for_node(core); ++ if (cpu >= 0) { ++ if (!leaf) { ++ pr_err("%s: Core has both threads and CPU\n", ++ core->full_name); ++ return -EINVAL; ++ } ++ ++ cpu_topology[cpu].cluster_id = cluster_id; ++ cpu_topology[cpu].core_id = core_id; ++ } else if (leaf) { ++ pr_err("%s: Can't get CPU for leaf core\n", core->full_name); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int __init parse_cluster(struct device_node *cluster, int depth) ++{ ++ char name[10]; ++ bool leaf = true; ++ bool has_cores = false; ++ struct device_node *c; ++ static int cluster_id __initdata; ++ int core_id = 0; ++ int i, ret; ++ ++ /* ++ * First check for child clusters; we currently ignore any ++ * information about the nesting of clusters and present the ++ * scheduler with a flat list of them. ++ */ ++ i = 0; ++ do { ++ snprintf(name, sizeof(name), "cluster%d", i); ++ c = of_get_child_by_name(cluster, name); ++ if (c) { ++ leaf = false; ++ ret = parse_cluster(c, depth + 1); ++ of_node_put(c); ++ if (ret != 0) ++ return ret; ++ } ++ i++; ++ } while (c); ++ ++ /* Now check for cores */ ++ i = 0; ++ do { ++ snprintf(name, sizeof(name), "core%d", i); ++ c = of_get_child_by_name(cluster, name); ++ if (c) { ++ has_cores = true; ++ ++ if (depth == 0) { ++ pr_err("%s: cpu-map children should be clusters\n", ++ c->full_name); ++ of_node_put(c); ++ return -EINVAL; ++ } ++ ++ if (leaf) { ++ ret = parse_core(c, cluster_id, core_id++); ++ } else { ++ pr_err("%s: Non-leaf cluster with core %s\n", ++ cluster->full_name, name); ++ ret = -EINVAL; ++ } ++ ++ of_node_put(c); ++ if (ret != 0) ++ return ret; ++ } ++ i++; ++ } while (c); ++ ++ if (leaf && !has_cores) ++ pr_warn("%s: empty cluster\n", cluster->full_name); ++ ++ if (leaf) ++ cluster_id++; ++ ++ return 0; ++} ++ ++struct cpu_efficiency { ++ const char *compatible; ++ unsigned long efficiency; ++}; ++ ++/* ++ * Table of relative efficiency of each processors ++ * The efficiency value must fit in 20bit and the final ++ * cpu_scale value must be in the range ++ * 0 < cpu_scale < 3*SCHED_POWER_SCALE/2 ++ * in order to return at most 1 when DIV_ROUND_CLOSEST ++ * is used to compute the capacity of a CPU. ++ * Processors that are not defined in the table, ++ * use the default SCHED_POWER_SCALE value for cpu_scale. ++ */ ++static const struct cpu_efficiency table_efficiency[] = { ++ { "arm,cortex-a57", 3891 }, ++ { "arm,cortex-a53", 2048 }, ++ { NULL, }, ++}; ++ ++static unsigned long *__cpu_capacity; ++#define cpu_capacity(cpu) __cpu_capacity[cpu] ++ ++static unsigned long middle_capacity = 1; ++ ++/* ++ * Iterate all CPUs' descriptor in DT and compute the efficiency ++ * (as per table_efficiency). Also calculate a middle efficiency ++ * as close as possible to (max{eff_i} - min{eff_i}) / 2 ++ * This is later used to scale the cpu_power field such that an ++ * 'average' CPU is of middle power. Also see the comments near ++ * table_efficiency[] and update_cpu_power(). ++ */ ++static int __init parse_dt_topology(void) ++{ ++ struct device_node *cn, *map; ++ int ret = 0; ++ int cpu; ++ ++ cn = of_find_node_by_path("/cpus"); ++ if (!cn) { ++ pr_err("No CPU information found in DT\n"); ++ return 0; ++ } ++ ++ /* ++ * When topology is provided cpu-map is essentially a root ++ * cluster with restricted subnodes. ++ */ ++ map = of_get_child_by_name(cn, "cpu-map"); ++ if (!map) ++ goto out; ++ ++ ret = parse_cluster(map, 0); ++ if (ret != 0) ++ goto out_map; ++ ++ /* ++ * Check that all cores are in the topology; the SMP code will ++ * only mark cores described in the DT as possible. ++ */ ++ for_each_possible_cpu(cpu) { ++ if (cpu_topology[cpu].cluster_id == -1) { ++ pr_err("CPU%d: No topology information specified\n", ++ cpu); ++ ret = -EINVAL; ++ } ++ } ++ ++out_map: ++ of_node_put(map); ++out: ++ of_node_put(cn); ++ return ret; ++} ++ ++static void __init parse_dt_cpu_power(void) ++{ ++ const struct cpu_efficiency *cpu_eff; ++ struct device_node *cn; ++ unsigned long min_capacity = ULONG_MAX; ++ unsigned long max_capacity = 0; ++ unsigned long capacity = 0; ++ int cpu; ++ ++ __cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity), ++ GFP_NOWAIT); ++ ++ for_each_possible_cpu(cpu) { ++ const u32 *rate; ++ int len; ++ ++ /* Too early to use cpu->of_node */ ++ cn = of_get_cpu_node(cpu, NULL); ++ if (!cn) { ++ pr_err("Missing device node for CPU %d\n", cpu); ++ continue; ++ } ++ ++ for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++) ++ if (of_device_is_compatible(cn, cpu_eff->compatible)) ++ break; ++ ++ if (cpu_eff->compatible == NULL) { ++ pr_warn("%s: Unknown CPU type\n", cn->full_name); ++ continue; ++ } ++ ++ rate = of_get_property(cn, "clock-frequency", &len); ++ if (!rate || len != 4) { ++ pr_err("%s: Missing clock-frequency property\n", ++ cn->full_name); ++ continue; ++ } ++ ++ capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency; ++ ++ /* Save min capacity of the system */ ++ if (capacity < min_capacity) ++ min_capacity = capacity; ++ ++ /* Save max capacity of the system */ ++ if (capacity > max_capacity) ++ max_capacity = capacity; ++ ++ cpu_capacity(cpu) = capacity; ++ } ++ ++ /* If min and max capacities are equal we bypass the update of the ++ * cpu_scale because all CPUs have the same capacity. Otherwise, we ++ * compute a middle_capacity factor that will ensure that the capacity ++ * of an 'average' CPU of the system will be as close as possible to ++ * SCHED_POWER_SCALE, which is the default value, but with the ++ * constraint explained near table_efficiency[]. ++ */ ++ if (min_capacity == max_capacity) ++ return; ++ else if (4 * max_capacity < (3 * (max_capacity + min_capacity))) ++ middle_capacity = (min_capacity + max_capacity) ++ >> (SCHED_POWER_SHIFT+1); ++ else ++ middle_capacity = ((max_capacity / 3) ++ >> (SCHED_POWER_SHIFT-1)) + 1; ++} ++ ++/* ++ * Look for a customed capacity of a CPU in the cpu_topo_data table during the ++ * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the ++ * function returns directly for SMP system. ++ */ ++static void update_cpu_power(unsigned int cpu) ++{ ++ if (!cpu_capacity(cpu)) ++ return; ++ ++ set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity); ++ ++ pr_info("CPU%u: update cpu_power %lu\n", ++ cpu, arch_scale_freq_power(NULL, cpu)); ++} ++ ++/* ++ * cpu topology table ++ */ ++struct cpu_topology cpu_topology[NR_CPUS]; ++EXPORT_SYMBOL_GPL(cpu_topology); ++ ++const struct cpumask *cpu_coregroup_mask(int cpu) ++{ ++ return &cpu_topology[cpu].core_sibling; ++} ++ ++static void update_siblings_masks(unsigned int cpuid) ++{ ++ struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; ++ int cpu; ++ ++ if (cpuid_topo->cluster_id == -1) { ++ /* ++ * DT does not contain topology information for this cpu. ++ */ ++ pr_debug("CPU%u: No topology information configured\n", cpuid); ++ return; ++ } ++ ++ /* update core and thread sibling masks */ ++ for_each_possible_cpu(cpu) { ++ cpu_topo = &cpu_topology[cpu]; ++ ++ if (cpuid_topo->cluster_id != cpu_topo->cluster_id) ++ continue; ++ ++ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); ++ if (cpu != cpuid) ++ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling); ++ ++ if (cpuid_topo->core_id != cpu_topo->core_id) ++ continue; ++ ++ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling); ++ if (cpu != cpuid) ++ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling); ++ } ++} ++ ++void store_cpu_topology(unsigned int cpuid) ++{ ++ update_siblings_masks(cpuid); ++ update_cpu_power(cpuid); ++} ++ ++#ifdef CONFIG_SCHED_HMP ++ ++/* ++ * Retrieve logical cpu index corresponding to a given MPIDR[23:0] ++ * - mpidr: MPIDR[23:0] to be used for the look-up ++ * ++ * Returns the cpu logical index or -EINVAL on look-up error ++ */ ++static inline int get_logical_index(u32 mpidr) ++{ ++ int cpu; ++ for (cpu = 0; cpu < nr_cpu_ids; cpu++) ++ if (cpu_logical_map(cpu) == mpidr) ++ return cpu; ++ return -EINVAL; ++} ++ ++static const char * const little_cores[] = { ++ "arm,cortex-a53", ++ NULL, ++}; ++ ++static bool is_little_cpu(struct device_node *cn) ++{ ++ const char * const *lc; ++ for (lc = little_cores; *lc; lc++) ++ if (of_device_is_compatible(cn, *lc)) ++ return true; ++ return false; ++} ++ ++void __init arch_get_fast_and_slow_cpus(struct cpumask *fast, ++ struct cpumask *slow) ++{ ++ struct device_node *cn = NULL; ++ int cpu; ++ ++ cpumask_clear(fast); ++ cpumask_clear(slow); ++ ++ /* ++ * Use the config options if they are given. This helps testing ++ * HMP scheduling on systems without a big.LITTLE architecture. ++ */ ++ if (strlen(CONFIG_HMP_FAST_CPU_MASK) && strlen(CONFIG_HMP_SLOW_CPU_MASK)) { ++ if (cpulist_parse(CONFIG_HMP_FAST_CPU_MASK, fast)) ++ WARN(1, "Failed to parse HMP fast cpu mask!\n"); ++ if (cpulist_parse(CONFIG_HMP_SLOW_CPU_MASK, slow)) ++ WARN(1, "Failed to parse HMP slow cpu mask!\n"); ++ return; ++ } ++ ++ /* ++ * Else, parse device tree for little cores. ++ */ ++ while ((cn = of_find_node_by_type(cn, "cpu"))) { ++ ++ const u32 *mpidr; ++ int len; ++ ++ mpidr = of_get_property(cn, "reg", &len); ++ if (!mpidr || len != 8) { ++ pr_err("%s missing reg property\n", cn->full_name); ++ continue; ++ } ++ ++ cpu = get_logical_index(be32_to_cpup(mpidr+1)); ++ if (cpu == -EINVAL) { ++ pr_err("couldn't get logical index for mpidr %x\n", ++ be32_to_cpup(mpidr+1)); ++ break; ++ } ++ ++ if (is_little_cpu(cn)) ++ cpumask_set_cpu(cpu, slow); ++ else ++ cpumask_set_cpu(cpu, fast); ++ } ++ ++ if (!cpumask_empty(fast) && !cpumask_empty(slow)) ++ return; ++ ++ /* ++ * We didn't find both big and little cores so let's call all cores ++ * fast as this will keep the system running, with all cores being ++ * treated equal. ++ */ ++ cpumask_setall(fast); ++ cpumask_clear(slow); ++} ++ ++struct cpumask hmp_slow_cpu_mask; ++ ++void __init arch_get_hmp_domains(struct list_head *hmp_domains_list) ++{ ++ struct cpumask hmp_fast_cpu_mask; ++ struct hmp_domain *domain; ++ ++ arch_get_fast_and_slow_cpus(&hmp_fast_cpu_mask, &hmp_slow_cpu_mask); ++ ++ /* ++ * Initialize hmp_domains ++ * Must be ordered with respect to compute capacity. ++ * Fastest domain at head of list. ++ */ ++ if(!cpumask_empty(&hmp_slow_cpu_mask)) { ++ domain = (struct hmp_domain *) ++ kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); ++ cpumask_copy(&domain->possible_cpus, &hmp_slow_cpu_mask); ++ cpumask_and(&domain->cpus, cpu_online_mask, &domain->possible_cpus); ++ list_add(&domain->hmp_domains, hmp_domains_list); ++ } ++ domain = (struct hmp_domain *) ++ kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); ++ cpumask_copy(&domain->possible_cpus, &hmp_fast_cpu_mask); ++ cpumask_and(&domain->cpus, cpu_online_mask, &domain->possible_cpus); ++ list_add(&domain->hmp_domains, hmp_domains_list); ++} ++#endif /* CONFIG_SCHED_HMP */ ++ ++static void __init reset_cpu_topology(void) ++{ ++ unsigned int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ struct cpu_topology *cpu_topo = &cpu_topology[cpu]; ++ ++ cpu_topo->thread_id = -1; ++ cpu_topo->core_id = 0; ++ cpu_topo->cluster_id = -1; ++ ++ cpumask_clear(&cpu_topo->core_sibling); ++ cpumask_set_cpu(cpu, &cpu_topo->core_sibling); ++ cpumask_clear(&cpu_topo->thread_sibling); ++ cpumask_set_cpu(cpu, &cpu_topo->thread_sibling); ++ } ++} ++ ++static void __init reset_cpu_power(void) ++{ ++ unsigned int cpu; ++ ++ for_each_possible_cpu(cpu) ++ set_power_scale(cpu, SCHED_POWER_SCALE); ++} ++ ++void __init init_cpu_topology(void) ++{ ++ reset_cpu_topology(); ++ ++ /* ++ * Discard anything that was parsed if we hit an error so we ++ * don't use partial information. ++ */ ++ if (parse_dt_topology()) ++ reset_cpu_topology(); ++ ++ reset_cpu_power(); ++ parse_dt_cpu_power(); ++} +diff -Nur linux-3.14.36/arch/arm64/kernel/vdso/Makefile linux-openelec/arch/arm64/kernel/vdso/Makefile +--- linux-3.14.36/arch/arm64/kernel/vdso/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/vdso/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -47,9 +47,9 @@ + $(call if_changed_dep,vdsoas) + + # Actual build commands +-quiet_cmd_vdsold = VDSOL $@ ++quiet_cmd_vdsold = VDSOL $@ + cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@ +-quiet_cmd_vdsoas = VDSOA $@ ++quiet_cmd_vdsoas = VDSOA $@ + cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $< + + # Install commands for the unstripped file +diff -Nur linux-3.14.36/arch/arm64/kernel/vdso.c linux-openelec/arch/arm64/kernel/vdso.c +--- linux-3.14.36/arch/arm64/kernel/vdso.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/vdso.c 2015-05-06 12:05:43.000000000 -0500 +@@ -156,11 +156,12 @@ + int uses_interp) + { + struct mm_struct *mm = current->mm; +- unsigned long vdso_base, vdso_mapping_len; ++ unsigned long vdso_base, vdso_text_len, vdso_mapping_len; + int ret; + ++ vdso_text_len = vdso_pages << PAGE_SHIFT; + /* Be sure to map the data page */ +- vdso_mapping_len = (vdso_pages + 1) << PAGE_SHIFT; ++ vdso_mapping_len = vdso_text_len + PAGE_SIZE; + + down_write(&mm->mmap_sem); + vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0); +@@ -170,35 +171,52 @@ + } + mm->context.vdso = (void *)vdso_base; + +- ret = install_special_mapping(mm, vdso_base, vdso_mapping_len, ++ ret = install_special_mapping(mm, vdso_base, vdso_text_len, + VM_READ|VM_EXEC| + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, + vdso_pagelist); +- if (ret) { +- mm->context.vdso = NULL; ++ if (ret) ++ goto up_fail; ++ ++ vdso_base += vdso_text_len; ++ ret = install_special_mapping(mm, vdso_base, PAGE_SIZE, ++ VM_READ|VM_MAYREAD, ++ vdso_pagelist + vdso_pages); ++ if (ret) + goto up_fail; +- } + +-up_fail: + up_write(&mm->mmap_sem); ++ return 0; + ++up_fail: ++ mm->context.vdso = NULL; ++ up_write(&mm->mmap_sem); + return ret; + } + + const char *arch_vma_name(struct vm_area_struct *vma) + { ++ unsigned long vdso_text; ++ ++ if (!vma->vm_mm) ++ return NULL; ++ ++ vdso_text = (unsigned long)vma->vm_mm->context.vdso; ++ + /* + * We can re-use the vdso pointer in mm_context_t for identifying + * the vectors page for compat applications. The vDSO will always + * sit above TASK_UNMAPPED_BASE and so we don't need to worry about + * it conflicting with the vectors base. + */ +- if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso) { ++ if (vma->vm_start == vdso_text) { + #ifdef CONFIG_COMPAT + if (vma->vm_start == AARCH32_VECTORS_BASE) + return "[vectors]"; + #endif + return "[vdso]"; ++ } else if (vma->vm_start == (vdso_text + (vdso_pages << PAGE_SHIFT))) { ++ return "[vvar]"; + } + + return NULL; +diff -Nur linux-3.14.36/arch/arm64/kernel/vmlinux.lds.S linux-openelec/arch/arm64/kernel/vmlinux.lds.S +--- linux-3.14.36/arch/arm64/kernel/vmlinux.lds.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/kernel/vmlinux.lds.S 2015-05-06 12:05:43.000000000 -0500 +@@ -104,6 +104,13 @@ + _edata = .; + + BSS_SECTION(0, 0, 0) ++ ++ . = ALIGN(PAGE_SIZE); ++ idmap_pg_dir = .; ++ . += IDMAP_DIR_SIZE; ++ swapper_pg_dir = .; ++ . += SWAPPER_DIR_SIZE; ++ + _end = .; + + STABS_DEBUG +diff -Nur linux-3.14.36/arch/arm64/Makefile linux-openelec/arch/arm64/Makefile +--- linux-3.14.36/arch/arm64/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -45,6 +45,7 @@ + core-y += arch/arm64/kernel/ arch/arm64/mm/ + core-$(CONFIG_KVM) += arch/arm64/kvm/ + core-$(CONFIG_XEN) += arch/arm64/xen/ ++core-$(CONFIG_CRYPTO) += arch/arm64/crypto/ + libs-y := arch/arm64/lib/ $(libs-y) + libs-y += $(LIBGCC) + +diff -Nur linux-3.14.36/arch/arm64/mm/cache.S linux-openelec/arch/arm64/mm/cache.S +--- linux-3.14.36/arch/arm64/mm/cache.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/mm/cache.S 2015-05-06 12:05:43.000000000 -0500 +@@ -30,7 +30,7 @@ + * + * Corrupted registers: x0-x7, x9-x11 + */ +-ENTRY(__flush_dcache_all) ++__flush_dcache_all: + dsb sy // ensure ordering with previous memory accesses + mrs x0, clidr_el1 // read clidr + and x3, x0, #0x7000000 // extract loc from clidr +@@ -166,3 +166,97 @@ + dsb sy + ret + ENDPROC(__flush_dcache_area) ++ ++/* ++ * __inval_cache_range(start, end) ++ * - start - start address of region ++ * - end - end address of region ++ */ ++ENTRY(__inval_cache_range) ++ /* FALLTHROUGH */ ++ ++/* ++ * __dma_inv_range(start, end) ++ * - start - virtual start address of region ++ * - end - virtual end address of region ++ */ ++__dma_inv_range: ++ dcache_line_size x2, x3 ++ sub x3, x2, #1 ++ tst x1, x3 // end cache line aligned? ++ bic x1, x1, x3 ++ b.eq 1f ++ dc civac, x1 // clean & invalidate D / U line ++1: tst x0, x3 // start cache line aligned? ++ bic x0, x0, x3 ++ b.eq 2f ++ dc civac, x0 // clean & invalidate D / U line ++ b 3f ++2: dc ivac, x0 // invalidate D / U line ++3: add x0, x0, x2 ++ cmp x0, x1 ++ b.lo 2b ++ dsb sy ++ ret ++ENDPROC(__inval_cache_range) ++ENDPROC(__dma_inv_range) ++ ++/* ++ * __dma_clean_range(start, end) ++ * - start - virtual start address of region ++ * - end - virtual end address of region ++ */ ++__dma_clean_range: ++ dcache_line_size x2, x3 ++ sub x3, x2, #1 ++ bic x0, x0, x3 ++1: dc cvac, x0 // clean D / U line ++ add x0, x0, x2 ++ cmp x0, x1 ++ b.lo 1b ++ dsb sy ++ ret ++ENDPROC(__dma_clean_range) ++ ++/* ++ * __dma_flush_range(start, end) ++ * - start - virtual start address of region ++ * - end - virtual end address of region ++ */ ++ENTRY(__dma_flush_range) ++ dcache_line_size x2, x3 ++ sub x3, x2, #1 ++ bic x0, x0, x3 ++1: dc civac, x0 // clean & invalidate D / U line ++ add x0, x0, x2 ++ cmp x0, x1 ++ b.lo 1b ++ dsb sy ++ ret ++ENDPROC(__dma_flush_range) ++ ++/* ++ * __dma_map_area(start, size, dir) ++ * - start - kernel virtual start address ++ * - size - size of region ++ * - dir - DMA direction ++ */ ++ENTRY(__dma_map_area) ++ add x1, x1, x0 ++ cmp w2, #DMA_FROM_DEVICE ++ b.eq __dma_inv_range ++ b __dma_clean_range ++ENDPROC(__dma_map_area) ++ ++/* ++ * __dma_unmap_area(start, size, dir) ++ * - start - kernel virtual start address ++ * - size - size of region ++ * - dir - DMA direction ++ */ ++ENTRY(__dma_unmap_area) ++ add x1, x1, x0 ++ cmp w2, #DMA_TO_DEVICE ++ b.ne __dma_inv_range ++ ret ++ENDPROC(__dma_unmap_area) +diff -Nur linux-3.14.36/arch/arm64/mm/copypage.c linux-openelec/arch/arm64/mm/copypage.c +--- linux-3.14.36/arch/arm64/mm/copypage.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/mm/copypage.c 2015-05-06 12:05:43.000000000 -0500 +@@ -27,8 +27,10 @@ + copy_page(kto, kfrom); + __flush_dcache_area(kto, PAGE_SIZE); + } ++EXPORT_SYMBOL_GPL(__cpu_copy_user_page); + + void __cpu_clear_user_page(void *kaddr, unsigned long vaddr) + { + clear_page(kaddr); + } ++EXPORT_SYMBOL_GPL(__cpu_clear_user_page); +diff -Nur linux-3.14.36/arch/arm64/mm/dma-mapping.c linux-openelec/arch/arm64/mm/dma-mapping.c +--- linux-3.14.36/arch/arm64/mm/dma-mapping.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/mm/dma-mapping.c 2015-05-06 12:05:43.000000000 -0500 +@@ -22,26 +22,39 @@ + #include + #include + #include ++#include ++#include + #include + #include ++#include + + #include + + struct dma_map_ops *dma_ops; + EXPORT_SYMBOL(dma_ops); + +-static void *arm64_swiotlb_alloc_coherent(struct device *dev, size_t size, +- dma_addr_t *dma_handle, gfp_t flags, +- struct dma_attrs *attrs) ++static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, ++ bool coherent) ++{ ++ if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) ++ return pgprot_writecombine(prot); ++ else if (!coherent) ++ return pgprot_dmacoherent(prot); ++ return prot; ++} ++ ++static void *__dma_alloc_coherent(struct device *dev, size_t size, ++ dma_addr_t *dma_handle, gfp_t flags, ++ struct dma_attrs *attrs) + { + if (dev == NULL) { + WARN_ONCE(1, "Use an actual device structure for DMA allocation\n"); + return NULL; + } + +- if (IS_ENABLED(CONFIG_ZONE_DMA32) && ++ if (IS_ENABLED(CONFIG_ZONE_DMA) && + dev->coherent_dma_mask <= DMA_BIT_MASK(32)) +- flags |= GFP_DMA32; ++ flags |= GFP_DMA; + if (IS_ENABLED(CONFIG_DMA_CMA)) { + struct page *page; + +@@ -58,9 +71,9 @@ + } + } + +-static void arm64_swiotlb_free_coherent(struct device *dev, size_t size, +- void *vaddr, dma_addr_t dma_handle, +- struct dma_attrs *attrs) ++static void __dma_free_coherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t dma_handle, ++ struct dma_attrs *attrs) + { + if (dev == NULL) { + WARN_ONCE(1, "Use an actual device structure for DMA allocation\n"); +@@ -78,9 +91,212 @@ + } + } + +-static struct dma_map_ops arm64_swiotlb_dma_ops = { +- .alloc = arm64_swiotlb_alloc_coherent, +- .free = arm64_swiotlb_free_coherent, ++static void *__dma_alloc_noncoherent(struct device *dev, size_t size, ++ dma_addr_t *dma_handle, gfp_t flags, ++ struct dma_attrs *attrs) ++{ ++ struct page *page, **map; ++ void *ptr, *coherent_ptr; ++ int order, i; ++ ++ size = PAGE_ALIGN(size); ++ order = get_order(size); ++ ++ ptr = __dma_alloc_coherent(dev, size, dma_handle, flags, attrs); ++ if (!ptr) ++ goto no_mem; ++ map = kmalloc(sizeof(struct page *) << order, flags & ~GFP_DMA); ++ if (!map) ++ goto no_map; ++ ++ /* remove any dirty cache lines on the kernel alias */ ++ __dma_flush_range(ptr, ptr + size); ++ ++ /* create a coherent mapping */ ++ page = virt_to_page(ptr); ++ for (i = 0; i < (size >> PAGE_SHIFT); i++) ++ map[i] = page + i; ++ coherent_ptr = vmap(map, size >> PAGE_SHIFT, VM_MAP, ++ __get_dma_pgprot(attrs, pgprot_default, false)); ++ kfree(map); ++ if (!coherent_ptr) ++ goto no_map; ++ ++ return coherent_ptr; ++ ++no_map: ++ __dma_free_coherent(dev, size, ptr, *dma_handle, attrs); ++no_mem: ++ *dma_handle = ~0; ++ return NULL; ++} ++ ++static void __dma_free_noncoherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t dma_handle, ++ struct dma_attrs *attrs) ++{ ++ void *swiotlb_addr = phys_to_virt(dma_to_phys(dev, dma_handle)); ++ ++ vunmap(vaddr); ++ __dma_free_coherent(dev, size, swiotlb_addr, dma_handle, attrs); ++} ++ ++static dma_addr_t __swiotlb_map_page(struct device *dev, struct page *page, ++ unsigned long offset, size_t size, ++ enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ dma_addr_t dev_addr; ++ ++ dev_addr = swiotlb_map_page(dev, page, offset, size, dir, attrs); ++ __dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); ++ ++ return dev_addr; ++} ++ ++ ++static void __swiotlb_unmap_page(struct device *dev, dma_addr_t dev_addr, ++ size_t size, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ __dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); ++ swiotlb_unmap_page(dev, dev_addr, size, dir, attrs); ++} ++ ++static int __swiotlb_map_sg_attrs(struct device *dev, struct scatterlist *sgl, ++ int nelems, enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ struct scatterlist *sg; ++ int i, ret; ++ ++ ret = swiotlb_map_sg_attrs(dev, sgl, nelems, dir, attrs); ++ for_each_sg(sgl, sg, ret, i) ++ __dma_map_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), ++ sg->length, dir); ++ ++ return ret; ++} ++ ++static void __swiotlb_unmap_sg_attrs(struct device *dev, ++ struct scatterlist *sgl, int nelems, ++ enum dma_data_direction dir, ++ struct dma_attrs *attrs) ++{ ++ struct scatterlist *sg; ++ int i; ++ ++ for_each_sg(sgl, sg, nelems, i) ++ __dma_unmap_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), ++ sg->length, dir); ++ swiotlb_unmap_sg_attrs(dev, sgl, nelems, dir, attrs); ++} ++ ++static void __swiotlb_sync_single_for_cpu(struct device *dev, ++ dma_addr_t dev_addr, size_t size, ++ enum dma_data_direction dir) ++{ ++ __dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); ++ swiotlb_sync_single_for_cpu(dev, dev_addr, size, dir); ++} ++ ++static void __swiotlb_sync_single_for_device(struct device *dev, ++ dma_addr_t dev_addr, size_t size, ++ enum dma_data_direction dir) ++{ ++ swiotlb_sync_single_for_device(dev, dev_addr, size, dir); ++ __dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); ++} ++ ++static void __swiotlb_sync_sg_for_cpu(struct device *dev, ++ struct scatterlist *sgl, int nelems, ++ enum dma_data_direction dir) ++{ ++ struct scatterlist *sg; ++ int i; ++ ++ for_each_sg(sgl, sg, nelems, i) ++ __dma_unmap_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), ++ sg->length, dir); ++ swiotlb_sync_sg_for_cpu(dev, sgl, nelems, dir); ++} ++ ++static void __swiotlb_sync_sg_for_device(struct device *dev, ++ struct scatterlist *sgl, int nelems, ++ enum dma_data_direction dir) ++{ ++ struct scatterlist *sg; ++ int i; ++ ++ swiotlb_sync_sg_for_device(dev, sgl, nelems, dir); ++ for_each_sg(sgl, sg, nelems, i) ++ __dma_map_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), ++ sg->length, dir); ++} ++ ++/* vma->vm_page_prot must be set appropriately before calling this function */ ++static int __dma_common_mmap(struct device *dev, struct vm_area_struct *vma, ++ void *cpu_addr, dma_addr_t dma_addr, size_t size) ++{ ++ int ret = -ENXIO; ++ unsigned long nr_vma_pages = (vma->vm_end - vma->vm_start) >> ++ PAGE_SHIFT; ++ unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; ++ unsigned long pfn = dma_to_phys(dev, dma_addr) >> PAGE_SHIFT; ++ unsigned long off = vma->vm_pgoff; ++ ++ if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) ++ return ret; ++ ++ if (off < nr_pages && nr_vma_pages <= (nr_pages - off)) { ++ ret = remap_pfn_range(vma, vma->vm_start, ++ pfn + off, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot); ++ } ++ ++ return ret; ++} ++ ++static int __swiotlb_mmap_noncoherent(struct device *dev, ++ struct vm_area_struct *vma, ++ void *cpu_addr, dma_addr_t dma_addr, size_t size, ++ struct dma_attrs *attrs) ++{ ++ vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, false); ++ return __dma_common_mmap(dev, vma, cpu_addr, dma_addr, size); ++} ++ ++static int __swiotlb_mmap_coherent(struct device *dev, ++ struct vm_area_struct *vma, ++ void *cpu_addr, dma_addr_t dma_addr, size_t size, ++ struct dma_attrs *attrs) ++{ ++ /* Just use whatever page_prot attributes were specified */ ++ return __dma_common_mmap(dev, vma, cpu_addr, dma_addr, size); ++} ++ ++struct dma_map_ops noncoherent_swiotlb_dma_ops = { ++ .alloc = __dma_alloc_noncoherent, ++ .free = __dma_free_noncoherent, ++ .mmap = __swiotlb_mmap_noncoherent, ++ .map_page = __swiotlb_map_page, ++ .unmap_page = __swiotlb_unmap_page, ++ .map_sg = __swiotlb_map_sg_attrs, ++ .unmap_sg = __swiotlb_unmap_sg_attrs, ++ .sync_single_for_cpu = __swiotlb_sync_single_for_cpu, ++ .sync_single_for_device = __swiotlb_sync_single_for_device, ++ .sync_sg_for_cpu = __swiotlb_sync_sg_for_cpu, ++ .sync_sg_for_device = __swiotlb_sync_sg_for_device, ++ .dma_supported = swiotlb_dma_supported, ++ .mapping_error = swiotlb_dma_mapping_error, ++}; ++EXPORT_SYMBOL(noncoherent_swiotlb_dma_ops); ++ ++struct dma_map_ops coherent_swiotlb_dma_ops = { ++ .alloc = __dma_alloc_coherent, ++ .free = __dma_free_coherent, ++ .mmap = __swiotlb_mmap_coherent, + .map_page = swiotlb_map_page, + .unmap_page = swiotlb_unmap_page, + .map_sg = swiotlb_map_sg_attrs, +@@ -92,12 +308,47 @@ + .dma_supported = swiotlb_dma_supported, + .mapping_error = swiotlb_dma_mapping_error, + }; ++EXPORT_SYMBOL(coherent_swiotlb_dma_ops); + +-void __init arm64_swiotlb_init(void) ++static int dma_bus_notifier(struct notifier_block *nb, ++ unsigned long event, void *_dev) + { +- dma_ops = &arm64_swiotlb_dma_ops; +- swiotlb_init(1); ++ struct device *dev = _dev; ++ ++ if (event != BUS_NOTIFY_ADD_DEVICE) ++ return NOTIFY_DONE; ++ ++ if (of_property_read_bool(dev->of_node, "dma-coherent")) ++ set_dma_ops(dev, &coherent_swiotlb_dma_ops); ++ ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block platform_bus_nb = { ++ .notifier_call = dma_bus_notifier, ++}; ++ ++static struct notifier_block amba_bus_nb = { ++ .notifier_call = dma_bus_notifier, ++}; ++ ++extern int swiotlb_late_init_with_default_size(size_t default_size); ++ ++static int __init swiotlb_late_init(void) ++{ ++ size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT); ++ ++ /* ++ * These must be registered before of_platform_populate(). ++ */ ++ bus_register_notifier(&platform_bus_type, &platform_bus_nb); ++ bus_register_notifier(&amba_bustype, &amba_bus_nb); ++ ++ dma_ops = &noncoherent_swiotlb_dma_ops; ++ ++ return swiotlb_late_init_with_default_size(swiotlb_size); + } ++arch_initcall(swiotlb_late_init); + + #define PREALLOC_DMA_DEBUG_ENTRIES 4096 + +diff -Nur linux-3.14.36/arch/arm64/mm/init.c linux-openelec/arch/arm64/mm/init.c +--- linux-3.14.36/arch/arm64/mm/init.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/mm/init.c 2015-05-06 12:05:43.000000000 -0500 +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -59,22 +60,22 @@ + early_param("initrd", early_initrd); + #endif + +-#define MAX_DMA32_PFN ((4UL * 1024 * 1024 * 1024) >> PAGE_SHIFT) +- + static void __init zone_sizes_init(unsigned long min, unsigned long max) + { + struct memblock_region *reg; + unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES]; +- unsigned long max_dma32 = min; ++ unsigned long max_dma = min; + + memset(zone_size, 0, sizeof(zone_size)); + +-#ifdef CONFIG_ZONE_DMA32 + /* 4GB maximum for 32-bit only capable devices */ +- max_dma32 = max(min, min(max, MAX_DMA32_PFN)); +- zone_size[ZONE_DMA32] = max_dma32 - min; +-#endif +- zone_size[ZONE_NORMAL] = max - max_dma32; ++ if (IS_ENABLED(CONFIG_ZONE_DMA)) { ++ unsigned long max_dma_phys = ++ (unsigned long)dma_to_phys(NULL, DMA_BIT_MASK(32) + 1); ++ max_dma = max(min, min(max, max_dma_phys >> PAGE_SHIFT)); ++ zone_size[ZONE_DMA] = max_dma - min; ++ } ++ zone_size[ZONE_NORMAL] = max - max_dma; + + memcpy(zhole_size, zone_size, sizeof(zhole_size)); + +@@ -84,15 +85,15 @@ + + if (start >= max) + continue; +-#ifdef CONFIG_ZONE_DMA32 +- if (start < max_dma32) { +- unsigned long dma_end = min(end, max_dma32); +- zhole_size[ZONE_DMA32] -= dma_end - start; ++ ++ if (IS_ENABLED(CONFIG_ZONE_DMA) && start < max_dma) { ++ unsigned long dma_end = min(end, max_dma); ++ zhole_size[ZONE_DMA] -= dma_end - start; + } +-#endif +- if (end > max_dma32) { ++ ++ if (end > max_dma) { + unsigned long normal_end = min(end, max); +- unsigned long normal_start = max(start, max_dma32); ++ unsigned long normal_start = max(start, max_dma); + zhole_size[ZONE_NORMAL] -= normal_end - normal_start; + } + } +@@ -127,20 +128,16 @@ + { + u64 *reserve_map, base, size; + +- /* Register the kernel text, kernel data and initrd with memblock */ ++ /* ++ * Register the kernel text, kernel data, initrd, and initial ++ * pagetables with memblock. ++ */ + memblock_reserve(__pa(_text), _end - _text); + #ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + memblock_reserve(__virt_to_phys(initrd_start), initrd_end - initrd_start); + #endif + +- /* +- * Reserve the page tables. These are already in use, +- * and can only be in node 0. +- */ +- memblock_reserve(__pa(swapper_pg_dir), SWAPPER_DIR_SIZE); +- memblock_reserve(__pa(idmap_pg_dir), IDMAP_DIR_SIZE); +- + /* Reserve the dtb region */ + memblock_reserve(virt_to_phys(initial_boot_params), + be32_to_cpu(initial_boot_params->totalsize)); +@@ -261,8 +258,6 @@ + */ + void __init mem_init(void) + { +- arm64_swiotlb_init(); +- + max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map; + + #ifndef CONFIG_SPARSEMEM_VMEMMAP +diff -Nur linux-3.14.36/arch/arm64/mm/proc.S linux-openelec/arch/arm64/mm/proc.S +--- linux-3.14.36/arch/arm64/mm/proc.S 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/arm64/mm/proc.S 2015-05-06 12:05:43.000000000 -0500 +@@ -173,12 +173,6 @@ + * value of the SCTLR_EL1 register. + */ + ENTRY(__cpu_setup) +- /* +- * Preserve the link register across the function call. +- */ +- mov x28, lr +- bl __flush_dcache_all +- mov lr, x28 + ic iallu // I+BTB cache invalidate + tlbi vmalle1is // invalidate I + D TLBs + dsb sy +diff -Nur linux-3.14.36/arch/avr32/kernel/cpu.c linux-openelec/arch/avr32/kernel/cpu.c +--- linux-3.14.36/arch/avr32/kernel/cpu.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/avr32/kernel/cpu.c 2015-05-06 12:05:43.000000000 -0500 +@@ -39,10 +39,12 @@ + size_t count) + { + unsigned long val; +- char *endp; ++ int ret; + +- val = simple_strtoul(buf, &endp, 0); +- if (endp == buf || val > 0x3f) ++ ret = kstrtoul(buf, 0, &val); ++ if (ret) ++ return ret; ++ if (val > 0x3f) + return -EINVAL; + val = (val << 12) | (sysreg_read(PCCR) & 0xfffc0fff); + sysreg_write(PCCR, val); +@@ -61,11 +63,11 @@ + const char *buf, size_t count) + { + unsigned long val; +- char *endp; ++ int ret; + +- val = simple_strtoul(buf, &endp, 0); +- if (endp == buf) +- return -EINVAL; ++ ret = kstrtoul(buf, 0, &val); ++ if (ret) ++ return ret; + sysreg_write(PCNT0, val); + + return count; +@@ -84,10 +86,12 @@ + size_t count) + { + unsigned long val; +- char *endp; ++ int ret; + +- val = simple_strtoul(buf, &endp, 0); +- if (endp == buf || val > 0x3f) ++ ret = kstrtoul(buf, 0, &val); ++ if (ret) ++ return ret; ++ if (val > 0x3f) + return -EINVAL; + val = (val << 18) | (sysreg_read(PCCR) & 0xff03ffff); + sysreg_write(PCCR, val); +@@ -106,11 +110,11 @@ + size_t count) + { + unsigned long val; +- char *endp; ++ int ret; + +- val = simple_strtoul(buf, &endp, 0); +- if (endp == buf) +- return -EINVAL; ++ ret = kstrtoul(buf, 0, &val); ++ if (ret) ++ return ret; + sysreg_write(PCNT1, val); + + return count; +@@ -129,11 +133,11 @@ + size_t count) + { + unsigned long val; +- char *endp; ++ int ret; + +- val = simple_strtoul(buf, &endp, 0); +- if (endp == buf) +- return -EINVAL; ++ ret = kstrtoul(buf, 0, &val); ++ if (ret) ++ return ret; + sysreg_write(PCCNT, val); + + return count; +@@ -152,11 +156,11 @@ + size_t count) + { + unsigned long pccr, val; +- char *endp; ++ int ret; + +- val = simple_strtoul(buf, &endp, 0); +- if (endp == buf) +- return -EINVAL; ++ ret = kstrtoul(buf, 0, &val); ++ if (ret) ++ return ret; + if (val) + val = 1; + +diff -Nur linux-3.14.36/arch/blackfin/include/asm/ftrace.h linux-openelec/arch/blackfin/include/asm/ftrace.h +--- linux-3.14.36/arch/blackfin/include/asm/ftrace.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/blackfin/include/asm/ftrace.h 2015-05-06 12:05:43.000000000 -0500 +@@ -66,16 +66,7 @@ + + #endif /* CONFIG_FRAME_POINTER */ + +-#define HAVE_ARCH_CALLER_ADDR +- +-/* inline function or macro may lead to unexpected result */ +-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +-#define CALLER_ADDR1 ((unsigned long)return_address(1)) +-#define CALLER_ADDR2 ((unsigned long)return_address(2)) +-#define CALLER_ADDR3 ((unsigned long)return_address(3)) +-#define CALLER_ADDR4 ((unsigned long)return_address(4)) +-#define CALLER_ADDR5 ((unsigned long)return_address(5)) +-#define CALLER_ADDR6 ((unsigned long)return_address(6)) ++#define ftrace_return_address(n) return_address(n) + + #endif /* __ASSEMBLY__ */ + +diff -Nur linux-3.14.36/arch/hexagon/include/asm/elf.h linux-openelec/arch/hexagon/include/asm/elf.h +--- linux-3.14.36/arch/hexagon/include/asm/elf.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/hexagon/include/asm/elf.h 2015-05-06 12:05:44.000000000 -0500 +@@ -1,7 +1,7 @@ + /* + * ELF definitions for the Hexagon architecture + * +- * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2010-2013, The Linux Foundation. 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 and +diff -Nur linux-3.14.36/arch/parisc/include/asm/ftrace.h linux-openelec/arch/parisc/include/asm/ftrace.h +--- linux-3.14.36/arch/parisc/include/asm/ftrace.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/parisc/include/asm/ftrace.h 2015-05-06 12:05:43.000000000 -0500 +@@ -24,15 +24,7 @@ + + extern unsigned long return_address(unsigned int); + +-#define HAVE_ARCH_CALLER_ADDR +- +-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +-#define CALLER_ADDR1 return_address(1) +-#define CALLER_ADDR2 return_address(2) +-#define CALLER_ADDR3 return_address(3) +-#define CALLER_ADDR4 return_address(4) +-#define CALLER_ADDR5 return_address(5) +-#define CALLER_ADDR6 return_address(6) ++#define ftrace_return_address(n) return_address(n) + + #endif /* __ASSEMBLY__ */ + +diff -Nur linux-3.14.36/arch/s390/include/asm/cio.h linux-openelec/arch/s390/include/asm/cio.h +--- linux-3.14.36/arch/s390/include/asm/cio.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/s390/include/asm/cio.h 2015-05-06 12:05:43.000000000 -0500 +@@ -199,7 +199,7 @@ + /** + * struct irb - interruption response block + * @scsw: subchannel status word +- * @esw: extened status word ++ * @esw: extended status word + * @ecw: extended control word + * + * The irb that is handed to the device driver when an interrupt occurs. For +diff -Nur linux-3.14.36/arch/sh/include/asm/ftrace.h linux-openelec/arch/sh/include/asm/ftrace.h +--- linux-3.14.36/arch/sh/include/asm/ftrace.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/sh/include/asm/ftrace.h 2015-05-06 12:05:44.000000000 -0500 +@@ -40,15 +40,7 @@ + /* arch/sh/kernel/return_address.c */ + extern void *return_address(unsigned int); + +-#define HAVE_ARCH_CALLER_ADDR +- +-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +-#define CALLER_ADDR1 ((unsigned long)return_address(1)) +-#define CALLER_ADDR2 ((unsigned long)return_address(2)) +-#define CALLER_ADDR3 ((unsigned long)return_address(3)) +-#define CALLER_ADDR4 ((unsigned long)return_address(4)) +-#define CALLER_ADDR5 ((unsigned long)return_address(5)) +-#define CALLER_ADDR6 ((unsigned long)return_address(6)) ++#define ftrace_return_address(n) return_address(n) + + #endif /* __ASSEMBLY__ */ + +diff -Nur linux-3.14.36/arch/x86/kernel/setup.c linux-openelec/arch/x86/kernel/setup.c +--- linux-3.14.36/arch/x86/kernel/setup.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/arch/x86/kernel/setup.c 2015-05-06 12:05:44.000000000 -0500 +@@ -1120,7 +1120,7 @@ + setup_real_mode(); + + memblock_set_current_limit(get_max_mapped()); +- dma_contiguous_reserve(0); ++ dma_contiguous_reserve(max_pfn_mapped << PAGE_SHIFT); + + /* + * NOTE: On x86-32, only from this point on, fixmaps are ready for use. +diff -Nur linux-3.14.36/block/bfq-cgroup.c linux-openelec/block/bfq-cgroup.c +--- linux-3.14.36/block/bfq-cgroup.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/block/bfq-cgroup.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,932 @@ ++/* ++ * BFQ: CGROUPS support. ++ * ++ * Based on ideas and code from CFQ: ++ * Copyright (C) 2003 Jens Axboe ++ * ++ * Copyright (C) 2008 Fabio Checconi ++ * Paolo Valente ++ * ++ * Copyright (C) 2010 Paolo Valente ++ * ++ * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ ++ * file. ++ */ ++ ++#ifdef CONFIG_CGROUP_BFQIO ++ ++static DEFINE_MUTEX(bfqio_mutex); ++ ++static bool bfqio_is_removed(struct bfqio_cgroup *bgrp) ++{ ++ return bgrp ? !bgrp->online : false; ++} ++ ++static struct bfqio_cgroup bfqio_root_cgroup = { ++ .weight = BFQ_DEFAULT_GRP_WEIGHT, ++ .ioprio = BFQ_DEFAULT_GRP_IOPRIO, ++ .ioprio_class = BFQ_DEFAULT_GRP_CLASS, ++}; ++ ++static inline void bfq_init_entity(struct bfq_entity *entity, ++ struct bfq_group *bfqg) ++{ ++ entity->weight = entity->new_weight; ++ entity->orig_weight = entity->new_weight; ++ entity->ioprio = entity->new_ioprio; ++ entity->ioprio_class = entity->new_ioprio_class; ++ entity->parent = bfqg->my_entity; ++ entity->sched_data = &bfqg->sched_data; ++} ++ ++static struct bfqio_cgroup *css_to_bfqio(struct cgroup_subsys_state *css) ++{ ++ return css ? container_of(css, struct bfqio_cgroup, css) : NULL; ++} ++ ++/* ++ * Search the bfq_group for bfqd into the hash table (by now only a list) ++ * of bgrp. Must be called under rcu_read_lock(). ++ */ ++static struct bfq_group *bfqio_lookup_group(struct bfqio_cgroup *bgrp, ++ struct bfq_data *bfqd) ++{ ++ struct bfq_group *bfqg; ++ void *key; ++ ++ hlist_for_each_entry_rcu(bfqg, &bgrp->group_data, group_node) { ++ key = rcu_dereference(bfqg->bfqd); ++ if (key == bfqd) ++ return bfqg; ++ } ++ ++ return NULL; ++} ++ ++static inline void bfq_group_init_entity(struct bfqio_cgroup *bgrp, ++ struct bfq_group *bfqg) ++{ ++ struct bfq_entity *entity = &bfqg->entity; ++ ++ /* ++ * If the weight of the entity has never been set via the sysfs ++ * interface, then bgrp->weight == 0. In this case we initialize ++ * the weight from the current ioprio value. Otherwise, the group ++ * weight, if set, has priority over the ioprio value. ++ */ ++ if (bgrp->weight == 0) { ++ entity->new_weight = bfq_ioprio_to_weight(bgrp->ioprio); ++ entity->new_ioprio = bgrp->ioprio; ++ } else { ++ entity->new_weight = bgrp->weight; ++ entity->new_ioprio = bfq_weight_to_ioprio(bgrp->weight); ++ } ++ entity->orig_weight = entity->weight = entity->new_weight; ++ entity->ioprio = entity->new_ioprio; ++ entity->ioprio_class = entity->new_ioprio_class = bgrp->ioprio_class; ++ entity->my_sched_data = &bfqg->sched_data; ++ bfqg->active_entities = 0; ++} ++ ++static inline void bfq_group_set_parent(struct bfq_group *bfqg, ++ struct bfq_group *parent) ++{ ++ struct bfq_entity *entity; ++ ++ BUG_ON(parent == NULL); ++ BUG_ON(bfqg == NULL); ++ ++ entity = &bfqg->entity; ++ entity->parent = parent->my_entity; ++ entity->sched_data = &parent->sched_data; ++} ++ ++/** ++ * bfq_group_chain_alloc - allocate a chain of groups. ++ * @bfqd: queue descriptor. ++ * @css: the leaf cgroup_subsys_state this chain starts from. ++ * ++ * Allocate a chain of groups starting from the one belonging to ++ * @cgroup up to the root cgroup. Stop if a cgroup on the chain ++ * to the root has already an allocated group on @bfqd. ++ */ ++static struct bfq_group *bfq_group_chain_alloc(struct bfq_data *bfqd, ++ struct cgroup_subsys_state *css) ++{ ++ struct bfqio_cgroup *bgrp; ++ struct bfq_group *bfqg, *prev = NULL, *leaf = NULL; ++ ++ for (; css != NULL; css = css->parent) { ++ bgrp = css_to_bfqio(css); ++ ++ bfqg = bfqio_lookup_group(bgrp, bfqd); ++ if (bfqg != NULL) { ++ /* ++ * All the cgroups in the path from there to the ++ * root must have a bfq_group for bfqd, so we don't ++ * need any more allocations. ++ */ ++ break; ++ } ++ ++ bfqg = kzalloc(sizeof(*bfqg), GFP_ATOMIC); ++ if (bfqg == NULL) ++ goto cleanup; ++ ++ bfq_group_init_entity(bgrp, bfqg); ++ bfqg->my_entity = &bfqg->entity; ++ ++ if (leaf == NULL) { ++ leaf = bfqg; ++ prev = leaf; ++ } else { ++ bfq_group_set_parent(prev, bfqg); ++ /* ++ * Build a list of allocated nodes using the bfqd ++ * filed, that is still unused and will be ++ * initialized only after the node will be ++ * connected. ++ */ ++ prev->bfqd = bfqg; ++ prev = bfqg; ++ } ++ } ++ ++ return leaf; ++ ++cleanup: ++ while (leaf != NULL) { ++ prev = leaf; ++ leaf = leaf->bfqd; ++ kfree(prev); ++ } ++ ++ return NULL; ++} ++ ++/** ++ * bfq_group_chain_link - link an allocated group chain to a cgroup ++ * hierarchy. ++ * @bfqd: the queue descriptor. ++ * @css: the leaf cgroup_subsys_state to start from. ++ * @leaf: the leaf group (to be associated to @cgroup). ++ * ++ * Try to link a chain of groups to a cgroup hierarchy, connecting the ++ * nodes bottom-up, so we can be sure that when we find a cgroup in the ++ * hierarchy that already as a group associated to @bfqd all the nodes ++ * in the path to the root cgroup have one too. ++ * ++ * On locking: the queue lock protects the hierarchy (there is a hierarchy ++ * per device) while the bfqio_cgroup lock protects the list of groups ++ * belonging to the same cgroup. ++ */ ++static void bfq_group_chain_link(struct bfq_data *bfqd, ++ struct cgroup_subsys_state *css, ++ struct bfq_group *leaf) ++{ ++ struct bfqio_cgroup *bgrp; ++ struct bfq_group *bfqg, *next, *prev = NULL; ++ unsigned long flags; ++ ++ assert_spin_locked(bfqd->queue->queue_lock); ++ ++ for (; css != NULL && leaf != NULL; css = css->parent) { ++ bgrp = css_to_bfqio(css); ++ next = leaf->bfqd; ++ ++ bfqg = bfqio_lookup_group(bgrp, bfqd); ++ BUG_ON(bfqg != NULL); ++ ++ spin_lock_irqsave(&bgrp->lock, flags); ++ ++ rcu_assign_pointer(leaf->bfqd, bfqd); ++ hlist_add_head_rcu(&leaf->group_node, &bgrp->group_data); ++ hlist_add_head(&leaf->bfqd_node, &bfqd->group_list); ++ ++ spin_unlock_irqrestore(&bgrp->lock, flags); ++ ++ prev = leaf; ++ leaf = next; ++ } ++ ++ BUG_ON(css == NULL && leaf != NULL); ++ if (css != NULL && prev != NULL) { ++ bgrp = css_to_bfqio(css); ++ bfqg = bfqio_lookup_group(bgrp, bfqd); ++ bfq_group_set_parent(prev, bfqg); ++ } ++} ++ ++/** ++ * bfq_find_alloc_group - return the group associated to @bfqd in @cgroup. ++ * @bfqd: queue descriptor. ++ * @cgroup: cgroup being searched for. ++ * ++ * Return a group associated to @bfqd in @cgroup, allocating one if ++ * necessary. When a group is returned all the cgroups in the path ++ * to the root have a group associated to @bfqd. ++ * ++ * If the allocation fails, return the root group: this breaks guarantees ++ * but is a safe fallback. If this loss becomes a problem it can be ++ * mitigated using the equivalent weight (given by the product of the ++ * weights of the groups in the path from @group to the root) in the ++ * root scheduler. ++ * ++ * We allocate all the missing nodes in the path from the leaf cgroup ++ * to the root and we connect the nodes only after all the allocations ++ * have been successful. ++ */ ++static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, ++ struct cgroup_subsys_state *css) ++{ ++ struct bfqio_cgroup *bgrp = css_to_bfqio(css); ++ struct bfq_group *bfqg; ++ ++ bfqg = bfqio_lookup_group(bgrp, bfqd); ++ if (bfqg != NULL) ++ return bfqg; ++ ++ bfqg = bfq_group_chain_alloc(bfqd, css); ++ if (bfqg != NULL) ++ bfq_group_chain_link(bfqd, css, bfqg); ++ else ++ bfqg = bfqd->root_group; ++ ++ return bfqg; ++} ++ ++/** ++ * bfq_bfqq_move - migrate @bfqq to @bfqg. ++ * @bfqd: queue descriptor. ++ * @bfqq: the queue to move. ++ * @entity: @bfqq's entity. ++ * @bfqg: the group to move to. ++ * ++ * Move @bfqq to @bfqg, deactivating it from its old group and reactivating ++ * it on the new one. Avoid putting the entity on the old group idle tree. ++ * ++ * Must be called under the queue lock; the cgroup owning @bfqg must ++ * not disappear (by now this just means that we are called under ++ * rcu_read_lock()). ++ */ ++static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ struct bfq_entity *entity, struct bfq_group *bfqg) ++{ ++ int busy, resume; ++ ++ busy = bfq_bfqq_busy(bfqq); ++ resume = !RB_EMPTY_ROOT(&bfqq->sort_list); ++ ++ BUG_ON(resume && !entity->on_st); ++ BUG_ON(busy && !resume && entity->on_st && ++ bfqq != bfqd->in_service_queue); ++ ++ if (busy) { ++ BUG_ON(atomic_read(&bfqq->ref) < 2); ++ ++ if (!resume) ++ bfq_del_bfqq_busy(bfqd, bfqq, 0); ++ else ++ bfq_deactivate_bfqq(bfqd, bfqq, 0); ++ } else if (entity->on_st) ++ bfq_put_idle_entity(bfq_entity_service_tree(entity), entity); ++ ++ /* ++ * Here we use a reference to bfqg. We don't need a refcounter ++ * as the cgroup reference will not be dropped, so that its ++ * destroy() callback will not be invoked. ++ */ ++ entity->parent = bfqg->my_entity; ++ entity->sched_data = &bfqg->sched_data; ++ ++ if (busy && resume) ++ bfq_activate_bfqq(bfqd, bfqq); ++ ++ if (bfqd->in_service_queue == NULL && !bfqd->rq_in_driver) ++ bfq_schedule_dispatch(bfqd); ++} ++ ++/** ++ * __bfq_bic_change_cgroup - move @bic to @cgroup. ++ * @bfqd: the queue descriptor. ++ * @bic: the bic to move. ++ * @cgroup: the cgroup to move to. ++ * ++ * Move bic to cgroup, assuming that bfqd->queue is locked; the caller ++ * has to make sure that the reference to cgroup is valid across the call. ++ * ++ * NOTE: an alternative approach might have been to store the current ++ * cgroup in bfqq and getting a reference to it, reducing the lookup ++ * time here, at the price of slightly more complex code. ++ */ ++static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd, ++ struct bfq_io_cq *bic, ++ struct cgroup_subsys_state *css) ++{ ++ struct bfq_queue *async_bfqq = bic_to_bfqq(bic, 0); ++ struct bfq_queue *sync_bfqq = bic_to_bfqq(bic, 1); ++ struct bfq_entity *entity; ++ struct bfq_group *bfqg; ++ struct bfqio_cgroup *bgrp; ++ ++ bgrp = css_to_bfqio(css); ++ ++ bfqg = bfq_find_alloc_group(bfqd, css); ++ if (async_bfqq != NULL) { ++ entity = &async_bfqq->entity; ++ ++ if (entity->sched_data != &bfqg->sched_data) { ++ bic_set_bfqq(bic, NULL, 0); ++ bfq_log_bfqq(bfqd, async_bfqq, ++ "bic_change_group: %p %d", ++ async_bfqq, atomic_read(&async_bfqq->ref)); ++ bfq_put_queue(async_bfqq); ++ } ++ } ++ ++ if (sync_bfqq != NULL) { ++ entity = &sync_bfqq->entity; ++ if (entity->sched_data != &bfqg->sched_data) ++ bfq_bfqq_move(bfqd, sync_bfqq, entity, bfqg); ++ } ++ ++ return bfqg; ++} ++ ++/** ++ * bfq_bic_change_cgroup - move @bic to @cgroup. ++ * @bic: the bic being migrated. ++ * @cgroup: the destination cgroup. ++ * ++ * When the task owning @bic is moved to @cgroup, @bic is immediately ++ * moved into its new parent group. ++ */ ++static void bfq_bic_change_cgroup(struct bfq_io_cq *bic, ++ struct cgroup_subsys_state *css) ++{ ++ struct bfq_data *bfqd; ++ unsigned long uninitialized_var(flags); ++ ++ bfqd = bfq_get_bfqd_locked(&(bic->icq.q->elevator->elevator_data), ++ &flags); ++ if (bfqd != NULL) { ++ __bfq_bic_change_cgroup(bfqd, bic, css); ++ bfq_put_bfqd_unlock(bfqd, &flags); ++ } ++} ++ ++/** ++ * bfq_bic_update_cgroup - update the cgroup of @bic. ++ * @bic: the @bic to update. ++ * ++ * Make sure that @bic is enqueued in the cgroup of the current task. ++ * We need this in addition to moving bics during the cgroup attach ++ * phase because the task owning @bic could be at its first disk ++ * access or we may end up in the root cgroup as the result of a ++ * memory allocation failure and here we try to move to the right ++ * group. ++ * ++ * Must be called under the queue lock. It is safe to use the returned ++ * value even after the rcu_read_unlock() as the migration/destruction ++ * paths act under the queue lock too. IOW it is impossible to race with ++ * group migration/destruction and end up with an invalid group as: ++ * a) here cgroup has not yet been destroyed, nor its destroy callback ++ * has started execution, as current holds a reference to it, ++ * b) if it is destroyed after rcu_read_unlock() [after current is ++ * migrated to a different cgroup] its attach() callback will have ++ * taken care of remove all the references to the old cgroup data. ++ */ ++static struct bfq_group *bfq_bic_update_cgroup(struct bfq_io_cq *bic) ++{ ++ struct bfq_data *bfqd = bic_to_bfqd(bic); ++ struct bfq_group *bfqg; ++ struct cgroup_subsys_state *css; ++ ++ BUG_ON(bfqd == NULL); ++ ++ rcu_read_lock(); ++ css = task_css(current, bfqio_subsys_id); ++ bfqg = __bfq_bic_change_cgroup(bfqd, bic, css); ++ rcu_read_unlock(); ++ ++ return bfqg; ++} ++ ++/** ++ * bfq_flush_idle_tree - deactivate any entity on the idle tree of @st. ++ * @st: the service tree being flushed. ++ */ ++static inline void bfq_flush_idle_tree(struct bfq_service_tree *st) ++{ ++ struct bfq_entity *entity = st->first_idle; ++ ++ for (; entity != NULL; entity = st->first_idle) ++ __bfq_deactivate_entity(entity, 0); ++} ++ ++/** ++ * bfq_reparent_leaf_entity - move leaf entity to the root_group. ++ * @bfqd: the device data structure with the root group. ++ * @entity: the entity to move. ++ */ ++static inline void bfq_reparent_leaf_entity(struct bfq_data *bfqd, ++ struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ ++ BUG_ON(bfqq == NULL); ++ bfq_bfqq_move(bfqd, bfqq, entity, bfqd->root_group); ++ return; ++} ++ ++/** ++ * bfq_reparent_active_entities - move to the root group all active ++ * entities. ++ * @bfqd: the device data structure with the root group. ++ * @bfqg: the group to move from. ++ * @st: the service tree with the entities. ++ * ++ * Needs queue_lock to be taken and reference to be valid over the call. ++ */ ++static inline void bfq_reparent_active_entities(struct bfq_data *bfqd, ++ struct bfq_group *bfqg, ++ struct bfq_service_tree *st) ++{ ++ struct rb_root *active = &st->active; ++ struct bfq_entity *entity = NULL; ++ ++ if (!RB_EMPTY_ROOT(&st->active)) ++ entity = bfq_entity_of(rb_first(active)); ++ ++ for (; entity != NULL; entity = bfq_entity_of(rb_first(active))) ++ bfq_reparent_leaf_entity(bfqd, entity); ++ ++ if (bfqg->sched_data.in_service_entity != NULL) ++ bfq_reparent_leaf_entity(bfqd, ++ bfqg->sched_data.in_service_entity); ++ ++ return; ++} ++ ++/** ++ * bfq_destroy_group - destroy @bfqg. ++ * @bgrp: the bfqio_cgroup containing @bfqg. ++ * @bfqg: the group being destroyed. ++ * ++ * Destroy @bfqg, making sure that it is not referenced from its parent. ++ */ ++static void bfq_destroy_group(struct bfqio_cgroup *bgrp, struct bfq_group *bfqg) ++{ ++ struct bfq_data *bfqd; ++ struct bfq_service_tree *st; ++ struct bfq_entity *entity = bfqg->my_entity; ++ unsigned long uninitialized_var(flags); ++ int i; ++ ++ hlist_del(&bfqg->group_node); ++ ++ /* ++ * Empty all service_trees belonging to this group before ++ * deactivating the group itself. ++ */ ++ for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) { ++ st = bfqg->sched_data.service_tree + i; ++ ++ /* ++ * The idle tree may still contain bfq_queues belonging ++ * to exited task because they never migrated to a different ++ * cgroup from the one being destroyed now. No one else ++ * can access them so it's safe to act without any lock. ++ */ ++ bfq_flush_idle_tree(st); ++ ++ /* ++ * It may happen that some queues are still active ++ * (busy) upon group destruction (if the corresponding ++ * processes have been forced to terminate). We move ++ * all the leaf entities corresponding to these queues ++ * to the root_group. ++ * Also, it may happen that the group has an entity ++ * in service, which is disconnected from the active ++ * tree: it must be moved, too. ++ * There is no need to put the sync queues, as the ++ * scheduler has taken no reference. ++ */ ++ bfqd = bfq_get_bfqd_locked(&bfqg->bfqd, &flags); ++ if (bfqd != NULL) { ++ bfq_reparent_active_entities(bfqd, bfqg, st); ++ bfq_put_bfqd_unlock(bfqd, &flags); ++ } ++ BUG_ON(!RB_EMPTY_ROOT(&st->active)); ++ BUG_ON(!RB_EMPTY_ROOT(&st->idle)); ++ } ++ BUG_ON(bfqg->sched_data.next_in_service != NULL); ++ BUG_ON(bfqg->sched_data.in_service_entity != NULL); ++ ++ /* ++ * We may race with device destruction, take extra care when ++ * dereferencing bfqg->bfqd. ++ */ ++ bfqd = bfq_get_bfqd_locked(&bfqg->bfqd, &flags); ++ if (bfqd != NULL) { ++ hlist_del(&bfqg->bfqd_node); ++ __bfq_deactivate_entity(entity, 0); ++ bfq_put_async_queues(bfqd, bfqg); ++ bfq_put_bfqd_unlock(bfqd, &flags); ++ } ++ BUG_ON(entity->tree != NULL); ++ ++ /* ++ * No need to defer the kfree() to the end of the RCU grace ++ * period: we are called from the destroy() callback of our ++ * cgroup, so we can be sure that no one is a) still using ++ * this cgroup or b) doing lookups in it. ++ */ ++ kfree(bfqg); ++} ++ ++static void bfq_end_wr_async(struct bfq_data *bfqd) ++{ ++ struct hlist_node *tmp; ++ struct bfq_group *bfqg; ++ ++ hlist_for_each_entry_safe(bfqg, tmp, &bfqd->group_list, bfqd_node) ++ bfq_end_wr_async_queues(bfqd, bfqg); ++ bfq_end_wr_async_queues(bfqd, bfqd->root_group); ++} ++ ++/** ++ * bfq_disconnect_groups - disconnect @bfqd from all its groups. ++ * @bfqd: the device descriptor being exited. ++ * ++ * When the device exits we just make sure that no lookup can return ++ * the now unused group structures. They will be deallocated on cgroup ++ * destruction. ++ */ ++static void bfq_disconnect_groups(struct bfq_data *bfqd) ++{ ++ struct hlist_node *tmp; ++ struct bfq_group *bfqg; ++ ++ bfq_log(bfqd, "disconnect_groups beginning"); ++ hlist_for_each_entry_safe(bfqg, tmp, &bfqd->group_list, bfqd_node) { ++ hlist_del(&bfqg->bfqd_node); ++ ++ __bfq_deactivate_entity(bfqg->my_entity, 0); ++ ++ /* ++ * Don't remove from the group hash, just set an ++ * invalid key. No lookups can race with the ++ * assignment as bfqd is being destroyed; this ++ * implies also that new elements cannot be added ++ * to the list. ++ */ ++ rcu_assign_pointer(bfqg->bfqd, NULL); ++ ++ bfq_log(bfqd, "disconnect_groups: put async for group %p", ++ bfqg); ++ bfq_put_async_queues(bfqd, bfqg); ++ } ++} ++ ++static inline void bfq_free_root_group(struct bfq_data *bfqd) ++{ ++ struct bfqio_cgroup *bgrp = &bfqio_root_cgroup; ++ struct bfq_group *bfqg = bfqd->root_group; ++ ++ bfq_put_async_queues(bfqd, bfqg); ++ ++ spin_lock_irq(&bgrp->lock); ++ hlist_del_rcu(&bfqg->group_node); ++ spin_unlock_irq(&bgrp->lock); ++ ++ /* ++ * No need to synchronize_rcu() here: since the device is gone ++ * there cannot be any read-side access to its root_group. ++ */ ++ kfree(bfqg); ++} ++ ++static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) ++{ ++ struct bfq_group *bfqg; ++ struct bfqio_cgroup *bgrp; ++ int i; ++ ++ bfqg = kzalloc_node(sizeof(*bfqg), GFP_KERNEL, node); ++ if (bfqg == NULL) ++ return NULL; ++ ++ bfqg->entity.parent = NULL; ++ for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) ++ bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; ++ ++ bgrp = &bfqio_root_cgroup; ++ spin_lock_irq(&bgrp->lock); ++ rcu_assign_pointer(bfqg->bfqd, bfqd); ++ hlist_add_head_rcu(&bfqg->group_node, &bgrp->group_data); ++ spin_unlock_irq(&bgrp->lock); ++ ++ return bfqg; ++} ++ ++#define SHOW_FUNCTION(__VAR) \ ++static u64 bfqio_cgroup_##__VAR##_read(struct cgroup_subsys_state *css, \ ++ struct cftype *cftype) \ ++{ \ ++ struct bfqio_cgroup *bgrp = css_to_bfqio(css); \ ++ u64 ret = -ENODEV; \ ++ \ ++ mutex_lock(&bfqio_mutex); \ ++ if (bfqio_is_removed(bgrp)) \ ++ goto out_unlock; \ ++ \ ++ spin_lock_irq(&bgrp->lock); \ ++ ret = bgrp->__VAR; \ ++ spin_unlock_irq(&bgrp->lock); \ ++ \ ++out_unlock: \ ++ mutex_unlock(&bfqio_mutex); \ ++ return ret; \ ++} ++ ++SHOW_FUNCTION(weight); ++SHOW_FUNCTION(ioprio); ++SHOW_FUNCTION(ioprio_class); ++#undef SHOW_FUNCTION ++ ++#define STORE_FUNCTION(__VAR, __MIN, __MAX) \ ++static int bfqio_cgroup_##__VAR##_write(struct cgroup_subsys_state *css,\ ++ struct cftype *cftype, \ ++ u64 val) \ ++{ \ ++ struct bfqio_cgroup *bgrp = css_to_bfqio(css); \ ++ struct bfq_group *bfqg; \ ++ int ret = -EINVAL; \ ++ \ ++ if (val < (__MIN) || val > (__MAX)) \ ++ return ret; \ ++ \ ++ ret = -ENODEV; \ ++ mutex_lock(&bfqio_mutex); \ ++ if (bfqio_is_removed(bgrp)) \ ++ goto out_unlock; \ ++ ret = 0; \ ++ \ ++ spin_lock_irq(&bgrp->lock); \ ++ bgrp->__VAR = (unsigned short)val; \ ++ hlist_for_each_entry(bfqg, &bgrp->group_data, group_node) { \ ++ /* \ ++ * Setting the ioprio_changed flag of the entity \ ++ * to 1 with new_##__VAR == ##__VAR would re-set \ ++ * the value of the weight to its ioprio mapping. \ ++ * Set the flag only if necessary. \ ++ */ \ ++ if ((unsigned short)val != bfqg->entity.new_##__VAR) { \ ++ bfqg->entity.new_##__VAR = (unsigned short)val; \ ++ /* \ ++ * Make sure that the above new value has been \ ++ * stored in bfqg->entity.new_##__VAR before \ ++ * setting the ioprio_changed flag. In fact, \ ++ * this flag may be read asynchronously (in \ ++ * critical sections protected by a different \ ++ * lock than that held here), and finding this \ ++ * flag set may cause the execution of the code \ ++ * for updating parameters whose value may \ ++ * depend also on bfqg->entity.new_##__VAR (in \ ++ * __bfq_entity_update_weight_prio). \ ++ * This barrier makes sure that the new value \ ++ * of bfqg->entity.new_##__VAR is correctly \ ++ * seen in that code. \ ++ */ \ ++ smp_wmb(); \ ++ bfqg->entity.ioprio_changed = 1; \ ++ } \ ++ } \ ++ spin_unlock_irq(&bgrp->lock); \ ++ \ ++out_unlock: \ ++ mutex_unlock(&bfqio_mutex); \ ++ return ret; \ ++} ++ ++STORE_FUNCTION(weight, BFQ_MIN_WEIGHT, BFQ_MAX_WEIGHT); ++STORE_FUNCTION(ioprio, 0, IOPRIO_BE_NR - 1); ++STORE_FUNCTION(ioprio_class, IOPRIO_CLASS_RT, IOPRIO_CLASS_IDLE); ++#undef STORE_FUNCTION ++ ++static struct cftype bfqio_files[] = { ++ { ++ .name = "weight", ++ .read_u64 = bfqio_cgroup_weight_read, ++ .write_u64 = bfqio_cgroup_weight_write, ++ }, ++ { ++ .name = "ioprio", ++ .read_u64 = bfqio_cgroup_ioprio_read, ++ .write_u64 = bfqio_cgroup_ioprio_write, ++ }, ++ { ++ .name = "ioprio_class", ++ .read_u64 = bfqio_cgroup_ioprio_class_read, ++ .write_u64 = bfqio_cgroup_ioprio_class_write, ++ }, ++ { }, /* terminate */ ++}; ++ ++static struct cgroup_subsys_state *bfqio_create(struct cgroup_subsys_state ++ *parent_css) ++{ ++ struct bfqio_cgroup *bgrp; ++ ++ if (parent_css != NULL) { ++ bgrp = kzalloc(sizeof(*bgrp), GFP_KERNEL); ++ if (bgrp == NULL) ++ return ERR_PTR(-ENOMEM); ++ } else ++ bgrp = &bfqio_root_cgroup; ++ ++ spin_lock_init(&bgrp->lock); ++ INIT_HLIST_HEAD(&bgrp->group_data); ++ bgrp->ioprio = BFQ_DEFAULT_GRP_IOPRIO; ++ bgrp->ioprio_class = BFQ_DEFAULT_GRP_CLASS; ++ ++ return &bgrp->css; ++} ++ ++/* ++ * We cannot support shared io contexts, as we have no means to support ++ * two tasks with the same ioc in two different groups without major rework ++ * of the main bic/bfqq data structures. By now we allow a task to change ++ * its cgroup only if it's the only owner of its ioc; the drawback of this ++ * behavior is that a group containing a task that forked using CLONE_IO ++ * will not be destroyed until the tasks sharing the ioc die. ++ */ ++static int bfqio_can_attach(struct cgroup_subsys_state *css, ++ struct cgroup_taskset *tset) ++{ ++ struct task_struct *task; ++ struct io_context *ioc; ++ int ret = 0; ++ ++ cgroup_taskset_for_each(task, css, tset) { ++ /* ++ * task_lock() is needed to avoid races with ++ * exit_io_context() ++ */ ++ task_lock(task); ++ ioc = task->io_context; ++ if (ioc != NULL && atomic_read(&ioc->nr_tasks) > 1) ++ /* ++ * ioc == NULL means that the task is either too ++ * young or exiting: if it has still no ioc the ++ * ioc can't be shared, if the task is exiting the ++ * attach will fail anyway, no matter what we ++ * return here. ++ */ ++ ret = -EINVAL; ++ task_unlock(task); ++ if (ret) ++ break; ++ } ++ ++ return ret; ++} ++ ++static void bfqio_attach(struct cgroup_subsys_state *css, ++ struct cgroup_taskset *tset) ++{ ++ struct task_struct *task; ++ struct io_context *ioc; ++ struct io_cq *icq; ++ ++ /* ++ * IMPORTANT NOTE: The move of more than one process at a time to a ++ * new group has not yet been tested. ++ */ ++ cgroup_taskset_for_each(task, css, tset) { ++ ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); ++ if (ioc) { ++ /* ++ * Handle cgroup change here. ++ */ ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(icq, &ioc->icq_list, ioc_node) ++ if (!strncmp( ++ icq->q->elevator->type->elevator_name, ++ "bfq", ELV_NAME_MAX)) ++ bfq_bic_change_cgroup(icq_to_bic(icq), ++ css); ++ rcu_read_unlock(); ++ put_io_context(ioc); ++ } ++ } ++} ++ ++static void bfqio_destroy(struct cgroup_subsys_state *css) ++{ ++ struct bfqio_cgroup *bgrp = css_to_bfqio(css); ++ struct hlist_node *tmp; ++ struct bfq_group *bfqg; ++ ++ /* ++ * Since we are destroying the cgroup, there are no more tasks ++ * referencing it, and all the RCU grace periods that may have ++ * referenced it are ended (as the destruction of the parent ++ * cgroup is RCU-safe); bgrp->group_data will not be accessed by ++ * anything else and we don't need any synchronization. ++ */ ++ hlist_for_each_entry_safe(bfqg, tmp, &bgrp->group_data, group_node) ++ bfq_destroy_group(bgrp, bfqg); ++ ++ BUG_ON(!hlist_empty(&bgrp->group_data)); ++ ++ kfree(bgrp); ++} ++ ++static int bfqio_css_online(struct cgroup_subsys_state *css) ++{ ++ struct bfqio_cgroup *bgrp = css_to_bfqio(css); ++ ++ mutex_lock(&bfqio_mutex); ++ bgrp->online = true; ++ mutex_unlock(&bfqio_mutex); ++ ++ return 0; ++} ++ ++static void bfqio_css_offline(struct cgroup_subsys_state *css) ++{ ++ struct bfqio_cgroup *bgrp = css_to_bfqio(css); ++ ++ mutex_lock(&bfqio_mutex); ++ bgrp->online = false; ++ mutex_unlock(&bfqio_mutex); ++} ++ ++struct cgroup_subsys bfqio_subsys = { ++ .name = "bfqio", ++ .css_alloc = bfqio_create, ++ .css_online = bfqio_css_online, ++ .css_offline = bfqio_css_offline, ++ .can_attach = bfqio_can_attach, ++ .attach = bfqio_attach, ++ .css_free = bfqio_destroy, ++ .subsys_id = bfqio_subsys_id, ++ .base_cftypes = bfqio_files, ++}; ++#else ++static inline void bfq_init_entity(struct bfq_entity *entity, ++ struct bfq_group *bfqg) ++{ ++ entity->weight = entity->new_weight; ++ entity->orig_weight = entity->new_weight; ++ entity->ioprio = entity->new_ioprio; ++ entity->ioprio_class = entity->new_ioprio_class; ++ entity->sched_data = &bfqg->sched_data; ++} ++ ++static inline struct bfq_group * ++bfq_bic_update_cgroup(struct bfq_io_cq *bic) ++{ ++ struct bfq_data *bfqd = bic_to_bfqd(bic); ++ return bfqd->root_group; ++} ++ ++static inline void bfq_bfqq_move(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ struct bfq_entity *entity, ++ struct bfq_group *bfqg) ++{ ++} ++ ++static void bfq_end_wr_async(struct bfq_data *bfqd) ++{ ++ bfq_end_wr_async_queues(bfqd, bfqd->root_group); ++} ++ ++static inline void bfq_disconnect_groups(struct bfq_data *bfqd) ++{ ++ bfq_put_async_queues(bfqd, bfqd->root_group); ++} ++ ++static inline void bfq_free_root_group(struct bfq_data *bfqd) ++{ ++ kfree(bfqd->root_group); ++} ++ ++static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) ++{ ++ struct bfq_group *bfqg; ++ int i; ++ ++ bfqg = kmalloc_node(sizeof(*bfqg), GFP_KERNEL | __GFP_ZERO, node); ++ if (bfqg == NULL) ++ return NULL; ++ ++ for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) ++ bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; ++ ++ return bfqg; ++} ++#endif +diff -Nur linux-3.14.36/block/bfq.h linux-openelec/block/bfq.h +--- linux-3.14.36/block/bfq.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/block/bfq.h 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,770 @@ ++/* ++ * BFQ-v7r5 for 3.14.0: data structures and common functions prototypes. ++ * ++ * Based on ideas and code from CFQ: ++ * Copyright (C) 2003 Jens Axboe ++ * ++ * Copyright (C) 2008 Fabio Checconi ++ * Paolo Valente ++ * ++ * Copyright (C) 2010 Paolo Valente ++ */ ++ ++#ifndef _BFQ_H ++#define _BFQ_H ++ ++#include ++#include ++#include ++#include ++ ++#define BFQ_IOPRIO_CLASSES 3 ++#define BFQ_CL_IDLE_TIMEOUT (HZ/5) ++ ++#define BFQ_MIN_WEIGHT 1 ++#define BFQ_MAX_WEIGHT 1000 ++ ++#define BFQ_DEFAULT_GRP_WEIGHT 10 ++#define BFQ_DEFAULT_GRP_IOPRIO 0 ++#define BFQ_DEFAULT_GRP_CLASS IOPRIO_CLASS_BE ++ ++struct bfq_entity; ++ ++/** ++ * struct bfq_service_tree - per ioprio_class service tree. ++ * @active: tree for active entities (i.e., those backlogged). ++ * @idle: tree for idle entities (i.e., those not backlogged, with V <= F_i). ++ * @first_idle: idle entity with minimum F_i. ++ * @last_idle: idle entity with maximum F_i. ++ * @vtime: scheduler virtual time. ++ * @wsum: scheduler weight sum; active and idle entities contribute to it. ++ * ++ * Each service tree represents a B-WF2Q+ scheduler on its own. Each ++ * ioprio_class has its own independent scheduler, and so its own ++ * bfq_service_tree. All the fields are protected by the queue lock ++ * of the containing bfqd. ++ */ ++struct bfq_service_tree { ++ struct rb_root active; ++ struct rb_root idle; ++ ++ struct bfq_entity *first_idle; ++ struct bfq_entity *last_idle; ++ ++ u64 vtime; ++ unsigned long wsum; ++}; ++ ++/** ++ * struct bfq_sched_data - multi-class scheduler. ++ * @in_service_entity: entity in service. ++ * @next_in_service: head-of-the-line entity in the scheduler. ++ * @service_tree: array of service trees, one per ioprio_class. ++ * ++ * bfq_sched_data is the basic scheduler queue. It supports three ++ * ioprio_classes, and can be used either as a toplevel queue or as ++ * an intermediate queue on a hierarchical setup. ++ * @next_in_service points to the active entity of the sched_data ++ * service trees that will be scheduled next. ++ * ++ * The supported ioprio_classes are the same as in CFQ, in descending ++ * priority order, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE. ++ * Requests from higher priority queues are served before all the ++ * requests from lower priority queues; among requests of the same ++ * queue requests are served according to B-WF2Q+. ++ * All the fields are protected by the queue lock of the containing bfqd. ++ */ ++struct bfq_sched_data { ++ struct bfq_entity *in_service_entity; ++ struct bfq_entity *next_in_service; ++ struct bfq_service_tree service_tree[BFQ_IOPRIO_CLASSES]; ++}; ++ ++/** ++ * struct bfq_weight_counter - counter of the number of all active entities ++ * with a given weight. ++ * @weight: weight of the entities that this counter refers to. ++ * @num_active: number of active entities with this weight. ++ * @weights_node: weights tree member (see bfq_data's @queue_weights_tree ++ * and @group_weights_tree). ++ */ ++struct bfq_weight_counter { ++ short int weight; ++ unsigned int num_active; ++ struct rb_node weights_node; ++}; ++ ++/** ++ * struct bfq_entity - schedulable entity. ++ * @rb_node: service_tree member. ++ * @weight_counter: pointer to the weight counter associated with this entity. ++ * @on_st: flag, true if the entity is on a tree (either the active or ++ * the idle one of its service_tree). ++ * @finish: B-WF2Q+ finish timestamp (aka F_i). ++ * @start: B-WF2Q+ start timestamp (aka S_i). ++ * @tree: tree the entity is enqueued into; %NULL if not on a tree. ++ * @min_start: minimum start time of the (active) subtree rooted at ++ * this entity; used for O(log N) lookups into active trees. ++ * @service: service received during the last round of service. ++ * @budget: budget used to calculate F_i; F_i = S_i + @budget / @weight. ++ * @weight: weight of the queue ++ * @parent: parent entity, for hierarchical scheduling. ++ * @my_sched_data: for non-leaf nodes in the cgroup hierarchy, the ++ * associated scheduler queue, %NULL on leaf nodes. ++ * @sched_data: the scheduler queue this entity belongs to. ++ * @ioprio: the ioprio in use. ++ * @new_weight: when a weight change is requested, the new weight value. ++ * @orig_weight: original weight, used to implement weight boosting ++ * @new_ioprio: when an ioprio change is requested, the new ioprio value. ++ * @ioprio_class: the ioprio_class in use. ++ * @new_ioprio_class: when an ioprio_class change is requested, the new ++ * ioprio_class value. ++ * @ioprio_changed: flag, true when the user requested a weight, ioprio or ++ * ioprio_class change. ++ * ++ * A bfq_entity is used to represent either a bfq_queue (leaf node in the ++ * cgroup hierarchy) or a bfq_group into the upper level scheduler. Each ++ * entity belongs to the sched_data of the parent group in the cgroup ++ * hierarchy. Non-leaf entities have also their own sched_data, stored ++ * in @my_sched_data. ++ * ++ * Each entity stores independently its priority values; this would ++ * allow different weights on different devices, but this ++ * functionality is not exported to userspace by now. Priorities and ++ * weights are updated lazily, first storing the new values into the ++ * new_* fields, then setting the @ioprio_changed flag. As soon as ++ * there is a transition in the entity state that allows the priority ++ * update to take place the effective and the requested priority ++ * values are synchronized. ++ * ++ * Unless cgroups are used, the weight value is calculated from the ++ * ioprio to export the same interface as CFQ. When dealing with ++ * ``well-behaved'' queues (i.e., queues that do not spend too much ++ * time to consume their budget and have true sequential behavior, and ++ * when there are no external factors breaking anticipation) the ++ * relative weights at each level of the cgroups hierarchy should be ++ * guaranteed. All the fields are protected by the queue lock of the ++ * containing bfqd. ++ */ ++struct bfq_entity { ++ struct rb_node rb_node; ++ struct bfq_weight_counter *weight_counter; ++ ++ int on_st; ++ ++ u64 finish; ++ u64 start; ++ ++ struct rb_root *tree; ++ ++ u64 min_start; ++ ++ unsigned long service, budget; ++ unsigned short weight, new_weight; ++ unsigned short orig_weight; ++ ++ struct bfq_entity *parent; ++ ++ struct bfq_sched_data *my_sched_data; ++ struct bfq_sched_data *sched_data; ++ ++ unsigned short ioprio, new_ioprio; ++ unsigned short ioprio_class, new_ioprio_class; ++ ++ int ioprio_changed; ++}; ++ ++struct bfq_group; ++ ++/** ++ * struct bfq_queue - leaf schedulable entity. ++ * @ref: reference counter. ++ * @bfqd: parent bfq_data. ++ * @new_bfqq: shared bfq_queue if queue is cooperating with ++ * one or more other queues. ++ * @pos_node: request-position tree member (see bfq_data's @rq_pos_tree). ++ * @pos_root: request-position tree root (see bfq_data's @rq_pos_tree). ++ * @sort_list: sorted list of pending requests. ++ * @next_rq: if fifo isn't expired, next request to serve. ++ * @queued: nr of requests queued in @sort_list. ++ * @allocated: currently allocated requests. ++ * @meta_pending: pending metadata requests. ++ * @fifo: fifo list of requests in sort_list. ++ * @entity: entity representing this queue in the scheduler. ++ * @max_budget: maximum budget allowed from the feedback mechanism. ++ * @budget_timeout: budget expiration (in jiffies). ++ * @dispatched: number of requests on the dispatch list or inside driver. ++ * @flags: status flags. ++ * @bfqq_list: node for active/idle bfqq list inside our bfqd. ++ * @seek_samples: number of seeks sampled ++ * @seek_total: sum of the distances of the seeks sampled ++ * @seek_mean: mean seek distance ++ * @last_request_pos: position of the last request enqueued ++ * @requests_within_timer: number of consecutive pairs of request completion ++ * and arrival, such that the queue becomes idle ++ * after the completion, but the next request arrives ++ * within an idle time slice; used only if the queue's ++ * IO_bound has been cleared. ++ * @pid: pid of the process owning the queue, used for logging purposes. ++ * @last_wr_start_finish: start time of the current weight-raising period if ++ * the @bfq-queue is being weight-raised, otherwise ++ * finish time of the last weight-raising period ++ * @wr_cur_max_time: current max raising time for this queue ++ * @soft_rt_next_start: minimum time instant such that, only if a new ++ * request is enqueued after this time instant in an ++ * idle @bfq_queue with no outstanding requests, then ++ * the task associated with the queue it is deemed as ++ * soft real-time (see the comments to the function ++ * bfq_bfqq_softrt_next_start()) ++ * @last_idle_bklogged: time of the last transition of the @bfq_queue from ++ * idle to backlogged ++ * @service_from_backlogged: cumulative service received from the @bfq_queue ++ * since the last transition from idle to ++ * backlogged ++ * @bic: pointer to the bfq_io_cq owning the bfq_queue, set to %NULL if the ++ * queue is shared ++ * ++ * A bfq_queue is a leaf request queue; it can be associated with an ++ * io_context or more, if it is async or shared between cooperating ++ * processes. @cgroup holds a reference to the cgroup, to be sure that it ++ * does not disappear while a bfqq still references it (mostly to avoid ++ * races between request issuing and task migration followed by cgroup ++ * destruction). ++ * All the fields are protected by the queue lock of the containing bfqd. ++ */ ++struct bfq_queue { ++ atomic_t ref; ++ struct bfq_data *bfqd; ++ ++ /* fields for cooperating queues handling */ ++ struct bfq_queue *new_bfqq; ++ struct rb_node pos_node; ++ struct rb_root *pos_root; ++ ++ struct rb_root sort_list; ++ struct request *next_rq; ++ int queued[2]; ++ int allocated[2]; ++ int meta_pending; ++ struct list_head fifo; ++ ++ struct bfq_entity entity; ++ ++ unsigned long max_budget; ++ unsigned long budget_timeout; ++ ++ int dispatched; ++ ++ unsigned int flags; ++ ++ struct list_head bfqq_list; ++ ++ unsigned int seek_samples; ++ u64 seek_total; ++ sector_t seek_mean; ++ sector_t last_request_pos; ++ ++ unsigned int requests_within_timer; ++ ++ pid_t pid; ++ struct bfq_io_cq *bic; ++ ++ /* weight-raising fields */ ++ unsigned long wr_cur_max_time; ++ unsigned long soft_rt_next_start; ++ unsigned long last_wr_start_finish; ++ unsigned int wr_coeff; ++ unsigned long last_idle_bklogged; ++ unsigned long service_from_backlogged; ++}; ++ ++/** ++ * struct bfq_ttime - per process thinktime stats. ++ * @ttime_total: total process thinktime ++ * @ttime_samples: number of thinktime samples ++ * @ttime_mean: average process thinktime ++ */ ++struct bfq_ttime { ++ unsigned long last_end_request; ++ ++ unsigned long ttime_total; ++ unsigned long ttime_samples; ++ unsigned long ttime_mean; ++}; ++ ++/** ++ * struct bfq_io_cq - per (request_queue, io_context) structure. ++ * @icq: associated io_cq structure ++ * @bfqq: array of two process queues, the sync and the async ++ * @ttime: associated @bfq_ttime struct ++ * @wr_time_left: snapshot of the time left before weight raising ends ++ * for the sync queue associated to this process; this ++ * snapshot is taken to remember this value while the weight ++ * raising is suspended because the queue is merged with a ++ * shared queue, and is used to set @raising_cur_max_time ++ * when the queue is split from the shared queue and its ++ * weight is raised again ++ * @saved_idle_window: same purpose as the previous field for the idle ++ * window ++ * @saved_IO_bound: same purpose as the previous two fields for the I/O ++ * bound classification of a queue ++ * @cooperations: counter of consecutive successful queue merges underwent ++ * by any of the process' @bfq_queues ++ * @failed_cooperations: counter of consecutive failed queue merges of any ++ * of the process' @bfq_queues ++ */ ++struct bfq_io_cq { ++ struct io_cq icq; /* must be the first member */ ++ struct bfq_queue *bfqq[2]; ++ struct bfq_ttime ttime; ++ int ioprio; ++ ++ unsigned int wr_time_left; ++ unsigned int saved_idle_window; ++ unsigned int saved_IO_bound; ++ ++ unsigned int cooperations; ++ unsigned int failed_cooperations; ++}; ++ ++enum bfq_device_speed { ++ BFQ_BFQD_FAST, ++ BFQ_BFQD_SLOW, ++}; ++ ++/** ++ * struct bfq_data - per device data structure. ++ * @queue: request queue for the managed device. ++ * @root_group: root bfq_group for the device. ++ * @rq_pos_tree: rbtree sorted by next_request position, used when ++ * determining if two or more queues have interleaving ++ * requests (see bfq_close_cooperator()). ++ * @active_numerous_groups: number of bfq_groups containing more than one ++ * active @bfq_entity. ++ * @queue_weights_tree: rbtree of weight counters of @bfq_queues, sorted by ++ * weight. Used to keep track of whether all @bfq_queues ++ * have the same weight. The tree contains one counter ++ * for each distinct weight associated to some active ++ * and not weight-raised @bfq_queue (see the comments to ++ * the functions bfq_weights_tree_[add|remove] for ++ * further details). ++ * @group_weights_tree: rbtree of non-queue @bfq_entity weight counters, sorted ++ * by weight. Used to keep track of whether all ++ * @bfq_groups have the same weight. The tree contains ++ * one counter for each distinct weight associated to ++ * some active @bfq_group (see the comments to the ++ * functions bfq_weights_tree_[add|remove] for further ++ * details). ++ * @busy_queues: number of bfq_queues containing requests (including the ++ * queue in service, even if it is idling). ++ * @busy_in_flight_queues: number of @bfq_queues containing pending or ++ * in-flight requests, plus the @bfq_queue in ++ * service, even if idle but waiting for the ++ * possible arrival of its next sync request. This ++ * field is updated only if the device is rotational, ++ * but used only if the device is also NCQ-capable. ++ * The reason why the field is updated also for non- ++ * NCQ-capable rotational devices is related to the ++ * fact that the value of @hw_tag may be set also ++ * later than when busy_in_flight_queues may need to ++ * be incremented for the first time(s). Taking also ++ * this possibility into account, to avoid unbalanced ++ * increments/decrements, would imply more overhead ++ * than just updating busy_in_flight_queues ++ * regardless of the value of @hw_tag. ++ * @const_seeky_busy_in_flight_queues: number of constantly-seeky @bfq_queues ++ * (that is, seeky queues that expired ++ * for budget timeout at least once) ++ * containing pending or in-flight ++ * requests, including the in-service ++ * @bfq_queue if constantly seeky. This ++ * field is updated only if the device ++ * is rotational, but used only if the ++ * device is also NCQ-capable (see the ++ * comments to @busy_in_flight_queues). ++ * @wr_busy_queues: number of weight-raised busy @bfq_queues. ++ * @queued: number of queued requests. ++ * @rq_in_driver: number of requests dispatched and waiting for completion. ++ * @sync_flight: number of sync requests in the driver. ++ * @max_rq_in_driver: max number of reqs in driver in the last ++ * @hw_tag_samples completed requests. ++ * @hw_tag_samples: nr of samples used to calculate hw_tag. ++ * @hw_tag: flag set to one if the driver is showing a queueing behavior. ++ * @budgets_assigned: number of budgets assigned. ++ * @idle_slice_timer: timer set when idling for the next sequential request ++ * from the queue in service. ++ * @unplug_work: delayed work to restart dispatching on the request queue. ++ * @in_service_queue: bfq_queue in service. ++ * @in_service_bic: bfq_io_cq (bic) associated with the @in_service_queue. ++ * @last_position: on-disk position of the last served request. ++ * @last_budget_start: beginning of the last budget. ++ * @last_idling_start: beginning of the last idle slice. ++ * @peak_rate: peak transfer rate observed for a budget. ++ * @peak_rate_samples: number of samples used to calculate @peak_rate. ++ * @bfq_max_budget: maximum budget allotted to a bfq_queue before ++ * rescheduling. ++ * @group_list: list of all the bfq_groups active on the device. ++ * @active_list: list of all the bfq_queues active on the device. ++ * @idle_list: list of all the bfq_queues idle on the device. ++ * @bfq_quantum: max number of requests dispatched per dispatch round. ++ * @bfq_fifo_expire: timeout for async/sync requests; when it expires ++ * requests are served in fifo order. ++ * @bfq_back_penalty: weight of backward seeks wrt forward ones. ++ * @bfq_back_max: maximum allowed backward seek. ++ * @bfq_slice_idle: maximum idling time. ++ * @bfq_user_max_budget: user-configured max budget value ++ * (0 for auto-tuning). ++ * @bfq_max_budget_async_rq: maximum budget (in nr of requests) allotted to ++ * async queues. ++ * @bfq_timeout: timeout for bfq_queues to consume their budget; used to ++ * to prevent seeky queues to impose long latencies to well ++ * behaved ones (this also implies that seeky queues cannot ++ * receive guarantees in the service domain; after a timeout ++ * they are charged for the whole allocated budget, to try ++ * to preserve a behavior reasonably fair among them, but ++ * without service-domain guarantees). ++ * @bfq_coop_thresh: number of queue merges after which a @bfq_queue is ++ * no more granted any weight-raising. ++ * @bfq_failed_cooperations: number of consecutive failed cooperation ++ * chances after which weight-raising is restored ++ * to a queue subject to more than bfq_coop_thresh ++ * queue merges. ++ * @bfq_requests_within_timer: number of consecutive requests that must be ++ * issued within the idle time slice to set ++ * again idling to a queue which was marked as ++ * non-I/O-bound (see the definition of the ++ * IO_bound flag for further details). ++ * @bfq_wr_coeff: Maximum factor by which the weight of a weight-raised ++ * queue is multiplied ++ * @bfq_wr_max_time: maximum duration of a weight-raising period (jiffies) ++ * @bfq_wr_rt_max_time: maximum duration for soft real-time processes ++ * @bfq_wr_min_idle_time: minimum idle period after which weight-raising ++ * may be reactivated for a queue (in jiffies) ++ * @bfq_wr_min_inter_arr_async: minimum period between request arrivals ++ * after which weight-raising may be ++ * reactivated for an already busy queue ++ * (in jiffies) ++ * @bfq_wr_max_softrt_rate: max service-rate for a soft real-time queue, ++ * sectors per seconds ++ * @RT_prod: cached value of the product R*T used for computing the maximum ++ * duration of the weight raising automatically ++ * @device_speed: device-speed class for the low-latency heuristic ++ * @oom_bfqq: fallback dummy bfqq for extreme OOM conditions ++ * ++ * All the fields are protected by the @queue lock. ++ */ ++struct bfq_data { ++ struct request_queue *queue; ++ ++ struct bfq_group *root_group; ++ struct rb_root rq_pos_tree; ++ ++#ifdef CONFIG_CGROUP_BFQIO ++ int active_numerous_groups; ++#endif ++ ++ struct rb_root queue_weights_tree; ++ struct rb_root group_weights_tree; ++ ++ int busy_queues; ++ int busy_in_flight_queues; ++ int const_seeky_busy_in_flight_queues; ++ int wr_busy_queues; ++ int queued; ++ int rq_in_driver; ++ int sync_flight; ++ ++ int max_rq_in_driver; ++ int hw_tag_samples; ++ int hw_tag; ++ ++ int budgets_assigned; ++ ++ struct timer_list idle_slice_timer; ++ struct work_struct unplug_work; ++ ++ struct bfq_queue *in_service_queue; ++ struct bfq_io_cq *in_service_bic; ++ ++ sector_t last_position; ++ ++ ktime_t last_budget_start; ++ ktime_t last_idling_start; ++ int peak_rate_samples; ++ u64 peak_rate; ++ unsigned long bfq_max_budget; ++ ++ struct hlist_head group_list; ++ struct list_head active_list; ++ struct list_head idle_list; ++ ++ unsigned int bfq_quantum; ++ unsigned int bfq_fifo_expire[2]; ++ unsigned int bfq_back_penalty; ++ unsigned int bfq_back_max; ++ unsigned int bfq_slice_idle; ++ u64 bfq_class_idle_last_service; ++ ++ unsigned int bfq_user_max_budget; ++ unsigned int bfq_max_budget_async_rq; ++ unsigned int bfq_timeout[2]; ++ ++ unsigned int bfq_coop_thresh; ++ unsigned int bfq_failed_cooperations; ++ unsigned int bfq_requests_within_timer; ++ ++ bool low_latency; ++ ++ /* parameters of the low_latency heuristics */ ++ unsigned int bfq_wr_coeff; ++ unsigned int bfq_wr_max_time; ++ unsigned int bfq_wr_rt_max_time; ++ unsigned int bfq_wr_min_idle_time; ++ unsigned long bfq_wr_min_inter_arr_async; ++ unsigned int bfq_wr_max_softrt_rate; ++ u64 RT_prod; ++ enum bfq_device_speed device_speed; ++ ++ struct bfq_queue oom_bfqq; ++}; ++ ++enum bfqq_state_flags { ++ BFQ_BFQQ_FLAG_busy = 0, /* has requests or is in service */ ++ BFQ_BFQQ_FLAG_wait_request, /* waiting for a request */ ++ BFQ_BFQQ_FLAG_must_alloc, /* must be allowed rq alloc */ ++ BFQ_BFQQ_FLAG_fifo_expire, /* FIFO checked in this slice */ ++ BFQ_BFQQ_FLAG_idle_window, /* slice idling enabled */ ++ BFQ_BFQQ_FLAG_prio_changed, /* task priority has changed */ ++ BFQ_BFQQ_FLAG_sync, /* synchronous queue */ ++ BFQ_BFQQ_FLAG_budget_new, /* no completion with this budget */ ++ BFQ_BFQQ_FLAG_IO_bound, /* ++ * bfqq has timed-out at least once ++ * having consumed at most 2/10 of ++ * its budget ++ */ ++ BFQ_BFQQ_FLAG_constantly_seeky, /* ++ * bfqq has proved to be slow and ++ * seeky until budget timeout ++ */ ++ BFQ_BFQQ_FLAG_softrt_update, /* ++ * may need softrt-next-start ++ * update ++ */ ++ BFQ_BFQQ_FLAG_coop, /* bfqq is shared */ ++ BFQ_BFQQ_FLAG_split_coop, /* shared bfqq will be split */ ++ BFQ_BFQQ_FLAG_just_split, /* queue has just been split */ ++}; ++ ++#define BFQ_BFQQ_FNS(name) \ ++static inline void bfq_mark_bfqq_##name(struct bfq_queue *bfqq) \ ++{ \ ++ (bfqq)->flags |= (1 << BFQ_BFQQ_FLAG_##name); \ ++} \ ++static inline void bfq_clear_bfqq_##name(struct bfq_queue *bfqq) \ ++{ \ ++ (bfqq)->flags &= ~(1 << BFQ_BFQQ_FLAG_##name); \ ++} \ ++static inline int bfq_bfqq_##name(const struct bfq_queue *bfqq) \ ++{ \ ++ return ((bfqq)->flags & (1 << BFQ_BFQQ_FLAG_##name)) != 0; \ ++} ++ ++BFQ_BFQQ_FNS(busy); ++BFQ_BFQQ_FNS(wait_request); ++BFQ_BFQQ_FNS(must_alloc); ++BFQ_BFQQ_FNS(fifo_expire); ++BFQ_BFQQ_FNS(idle_window); ++BFQ_BFQQ_FNS(prio_changed); ++BFQ_BFQQ_FNS(sync); ++BFQ_BFQQ_FNS(budget_new); ++BFQ_BFQQ_FNS(IO_bound); ++BFQ_BFQQ_FNS(constantly_seeky); ++BFQ_BFQQ_FNS(coop); ++BFQ_BFQQ_FNS(split_coop); ++BFQ_BFQQ_FNS(just_split); ++BFQ_BFQQ_FNS(softrt_update); ++#undef BFQ_BFQQ_FNS ++ ++/* Logging facilities. */ ++#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) \ ++ blk_add_trace_msg((bfqd)->queue, "bfq%d " fmt, (bfqq)->pid, ##args) ++ ++#define bfq_log(bfqd, fmt, args...) \ ++ blk_add_trace_msg((bfqd)->queue, "bfq " fmt, ##args) ++ ++/* Expiration reasons. */ ++enum bfqq_expiration { ++ BFQ_BFQQ_TOO_IDLE = 0, /* ++ * queue has been idling for ++ * too long ++ */ ++ BFQ_BFQQ_BUDGET_TIMEOUT, /* budget took too long to be used */ ++ BFQ_BFQQ_BUDGET_EXHAUSTED, /* budget consumed */ ++ BFQ_BFQQ_NO_MORE_REQUESTS, /* the queue has no more requests */ ++}; ++ ++#ifdef CONFIG_CGROUP_BFQIO ++/** ++ * struct bfq_group - per (device, cgroup) data structure. ++ * @entity: schedulable entity to insert into the parent group sched_data. ++ * @sched_data: own sched_data, to contain child entities (they may be ++ * both bfq_queues and bfq_groups). ++ * @group_node: node to be inserted into the bfqio_cgroup->group_data ++ * list of the containing cgroup's bfqio_cgroup. ++ * @bfqd_node: node to be inserted into the @bfqd->group_list list ++ * of the groups active on the same device; used for cleanup. ++ * @bfqd: the bfq_data for the device this group acts upon. ++ * @async_bfqq: array of async queues for all the tasks belonging to ++ * the group, one queue per ioprio value per ioprio_class, ++ * except for the idle class that has only one queue. ++ * @async_idle_bfqq: async queue for the idle class (ioprio is ignored). ++ * @my_entity: pointer to @entity, %NULL for the toplevel group; used ++ * to avoid too many special cases during group creation/ ++ * migration. ++ * @active_entities: number of active entities belonging to the group; ++ * unused for the root group. Used to know whether there ++ * are groups with more than one active @bfq_entity ++ * (see the comments to the function ++ * bfq_bfqq_must_not_expire()). ++ * ++ * Each (device, cgroup) pair has its own bfq_group, i.e., for each cgroup ++ * there is a set of bfq_groups, each one collecting the lower-level ++ * entities belonging to the group that are acting on the same device. ++ * ++ * Locking works as follows: ++ * o @group_node is protected by the bfqio_cgroup lock, and is accessed ++ * via RCU from its readers. ++ * o @bfqd is protected by the queue lock, RCU is used to access it ++ * from the readers. ++ * o All the other fields are protected by the @bfqd queue lock. ++ */ ++struct bfq_group { ++ struct bfq_entity entity; ++ struct bfq_sched_data sched_data; ++ ++ struct hlist_node group_node; ++ struct hlist_node bfqd_node; ++ ++ void *bfqd; ++ ++ struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; ++ struct bfq_queue *async_idle_bfqq; ++ ++ struct bfq_entity *my_entity; ++ ++ int active_entities; ++}; ++ ++/** ++ * struct bfqio_cgroup - bfq cgroup data structure. ++ * @css: subsystem state for bfq in the containing cgroup. ++ * @online: flag marked when the subsystem is inserted. ++ * @weight: cgroup weight. ++ * @ioprio: cgroup ioprio. ++ * @ioprio_class: cgroup ioprio_class. ++ * @lock: spinlock that protects @ioprio, @ioprio_class and @group_data. ++ * @group_data: list containing the bfq_group belonging to this cgroup. ++ * ++ * @group_data is accessed using RCU, with @lock protecting the updates, ++ * @ioprio and @ioprio_class are protected by @lock. ++ */ ++struct bfqio_cgroup { ++ struct cgroup_subsys_state css; ++ bool online; ++ ++ unsigned short weight, ioprio, ioprio_class; ++ ++ spinlock_t lock; ++ struct hlist_head group_data; ++}; ++#else ++struct bfq_group { ++ struct bfq_sched_data sched_data; ++ ++ struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; ++ struct bfq_queue *async_idle_bfqq; ++}; ++#endif ++ ++static inline struct bfq_service_tree * ++bfq_entity_service_tree(struct bfq_entity *entity) ++{ ++ struct bfq_sched_data *sched_data = entity->sched_data; ++ unsigned int idx = entity->ioprio_class - 1; ++ ++ BUG_ON(idx >= BFQ_IOPRIO_CLASSES); ++ BUG_ON(sched_data == NULL); ++ ++ return sched_data->service_tree + idx; ++} ++ ++static inline struct bfq_queue *bic_to_bfqq(struct bfq_io_cq *bic, ++ int is_sync) ++{ ++ return bic->bfqq[!!is_sync]; ++} ++ ++static inline void bic_set_bfqq(struct bfq_io_cq *bic, ++ struct bfq_queue *bfqq, int is_sync) ++{ ++ bic->bfqq[!!is_sync] = bfqq; ++} ++ ++static inline struct bfq_data *bic_to_bfqd(struct bfq_io_cq *bic) ++{ ++ return bic->icq.q->elevator->elevator_data; ++} ++ ++/** ++ * bfq_get_bfqd_locked - get a lock to a bfqd using a RCU protected pointer. ++ * @ptr: a pointer to a bfqd. ++ * @flags: storage for the flags to be saved. ++ * ++ * This function allows bfqg->bfqd to be protected by the ++ * queue lock of the bfqd they reference; the pointer is dereferenced ++ * under RCU, so the storage for bfqd is assured to be safe as long ++ * as the RCU read side critical section does not end. After the ++ * bfqd->queue->queue_lock is taken the pointer is rechecked, to be ++ * sure that no other writer accessed it. If we raced with a writer, ++ * the function returns NULL, with the queue unlocked, otherwise it ++ * returns the dereferenced pointer, with the queue locked. ++ */ ++static inline struct bfq_data *bfq_get_bfqd_locked(void **ptr, ++ unsigned long *flags) ++{ ++ struct bfq_data *bfqd; ++ ++ rcu_read_lock(); ++ bfqd = rcu_dereference(*(struct bfq_data **)ptr); ++ ++ if (bfqd != NULL) { ++ spin_lock_irqsave(bfqd->queue->queue_lock, *flags); ++ if (*ptr == bfqd) ++ goto out; ++ spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); ++ } ++ ++ bfqd = NULL; ++out: ++ rcu_read_unlock(); ++ return bfqd; ++} ++ ++static inline void bfq_put_bfqd_unlock(struct bfq_data *bfqd, ++ unsigned long *flags) ++{ ++ spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); ++} ++ ++static void bfq_changed_ioprio(struct bfq_io_cq *bic); ++static void bfq_put_queue(struct bfq_queue *bfqq); ++static void bfq_dispatch_insert(struct request_queue *q, struct request *rq); ++static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, ++ struct bfq_group *bfqg, int is_sync, ++ struct bfq_io_cq *bic, gfp_t gfp_mask); ++static void bfq_end_wr_async_queues(struct bfq_data *bfqd, ++ struct bfq_group *bfqg); ++static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg); ++static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq); ++ ++#endif /* _BFQ_H */ +diff -Nur linux-3.14.36/block/bfq-ioc.c linux-openelec/block/bfq-ioc.c +--- linux-3.14.36/block/bfq-ioc.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/block/bfq-ioc.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,36 @@ ++/* ++ * BFQ: I/O context handling. ++ * ++ * Based on ideas and code from CFQ: ++ * Copyright (C) 2003 Jens Axboe ++ * ++ * Copyright (C) 2008 Fabio Checconi ++ * Paolo Valente ++ * ++ * Copyright (C) 2010 Paolo Valente ++ */ ++ ++/** ++ * icq_to_bic - convert iocontext queue structure to bfq_io_cq. ++ * @icq: the iocontext queue. ++ */ ++static inline struct bfq_io_cq *icq_to_bic(struct io_cq *icq) ++{ ++ /* bic->icq is the first member, %NULL will convert to %NULL */ ++ return container_of(icq, struct bfq_io_cq, icq); ++} ++ ++/** ++ * bfq_bic_lookup - search into @ioc a bic associated to @bfqd. ++ * @bfqd: the lookup key. ++ * @ioc: the io_context of the process doing I/O. ++ * ++ * Queue lock must be held. ++ */ ++static inline struct bfq_io_cq *bfq_bic_lookup(struct bfq_data *bfqd, ++ struct io_context *ioc) ++{ ++ if (ioc) ++ return icq_to_bic(ioc_lookup_icq(ioc, bfqd->queue)); ++ return NULL; ++} +diff -Nur linux-3.14.36/block/bfq-iosched.c linux-openelec/block/bfq-iosched.c +--- linux-3.14.36/block/bfq-iosched.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/block/bfq-iosched.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,3919 @@ ++/* ++ * Budget Fair Queueing (BFQ) disk scheduler. ++ * ++ * Based on ideas and code from CFQ: ++ * Copyright (C) 2003 Jens Axboe ++ * ++ * Copyright (C) 2008 Fabio Checconi ++ * Paolo Valente ++ * ++ * Copyright (C) 2010 Paolo Valente ++ * ++ * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ ++ * file. ++ * ++ * BFQ is a proportional-share storage-I/O scheduling algorithm based on ++ * the slice-by-slice service scheme of CFQ. But BFQ assigns budgets, ++ * measured in number of sectors, to processes instead of time slices. The ++ * device is not granted to the in-service process for a given time slice, ++ * but until it has exhausted its assigned budget. This change from the time ++ * to the service domain allows BFQ to distribute the device throughput ++ * among processes as desired, without any distortion due to ZBR, workload ++ * fluctuations or other factors. BFQ uses an ad hoc internal scheduler, ++ * called B-WF2Q+, to schedule processes according to their budgets. More ++ * precisely, BFQ schedules queues associated to processes. Thanks to the ++ * accurate policy of B-WF2Q+, BFQ can afford to assign high budgets to ++ * I/O-bound processes issuing sequential requests (to boost the ++ * throughput), and yet guarantee a low latency to interactive and soft ++ * real-time applications. ++ * ++ * BFQ is described in [1], where also a reference to the initial, more ++ * theoretical paper on BFQ can be found. The interested reader can find ++ * in the latter paper full details on the main algorithm, as well as ++ * formulas of the guarantees and formal proofs of all the properties. ++ * With respect to the version of BFQ presented in these papers, this ++ * implementation adds a few more heuristics, such as the one that ++ * guarantees a low latency to soft real-time applications, and a ++ * hierarchical extension based on H-WF2Q+. ++ * ++ * B-WF2Q+ is based on WF2Q+, that is described in [2], together with ++ * H-WF2Q+, while the augmented tree used to implement B-WF2Q+ with O(log N) ++ * complexity derives from the one introduced with EEVDF in [3]. ++ * ++ * [1] P. Valente and M. Andreolini, ``Improving Application Responsiveness ++ * with the BFQ Disk I/O Scheduler'', ++ * Proceedings of the 5th Annual International Systems and Storage ++ * Conference (SYSTOR '12), June 2012. ++ * ++ * http://algogroup.unimo.it/people/paolo/disk_sched/bf1-v1-suite-results.pdf ++ * ++ * [2] Jon C.R. Bennett and H. Zhang, ``Hierarchical Packet Fair Queueing ++ * Algorithms,'' IEEE/ACM Transactions on Networking, 5(5):675-689, ++ * Oct 1997. ++ * ++ * http://www.cs.cmu.edu/~hzhang/papers/TON-97-Oct.ps.gz ++ * ++ * [3] I. Stoica and H. Abdel-Wahab, ``Earliest Eligible Virtual Deadline ++ * First: A Flexible and Accurate Mechanism for Proportional Share ++ * Resource Allocation,'' technical report. ++ * ++ * http://www.cs.berkeley.edu/~istoica/papers/eevdf-tr-95.pdf ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "bfq.h" ++#include "blk.h" ++ ++/* Max number of dispatches in one round of service. */ ++static const int bfq_quantum = 4; ++ ++/* Expiration time of sync (0) and async (1) requests, in jiffies. */ ++static const int bfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; ++ ++/* Maximum backwards seek, in KiB. */ ++static const int bfq_back_max = 16 * 1024; ++ ++/* Penalty of a backwards seek, in number of sectors. */ ++static const int bfq_back_penalty = 2; ++ ++/* Idling period duration, in jiffies. */ ++static int bfq_slice_idle = HZ / 125; ++ ++/* Default maximum budget values, in sectors and number of requests. */ ++static const int bfq_default_max_budget = 16 * 1024; ++static const int bfq_max_budget_async_rq = 4; ++ ++/* ++ * Async to sync throughput distribution is controlled as follows: ++ * when an async request is served, the entity is charged the number ++ * of sectors of the request, multiplied by the factor below ++ */ ++static const int bfq_async_charge_factor = 10; ++ ++/* Default timeout values, in jiffies, approximating CFQ defaults. */ ++static const int bfq_timeout_sync = HZ / 8; ++static int bfq_timeout_async = HZ / 25; ++ ++struct kmem_cache *bfq_pool; ++ ++/* Below this threshold (in ms), we consider thinktime immediate. */ ++#define BFQ_MIN_TT 2 ++ ++/* hw_tag detection: parallel requests threshold and min samples needed. */ ++#define BFQ_HW_QUEUE_THRESHOLD 4 ++#define BFQ_HW_QUEUE_SAMPLES 32 ++ ++#define BFQQ_SEEK_THR (sector_t)(8 * 1024) ++#define BFQQ_SEEKY(bfqq) ((bfqq)->seek_mean > BFQQ_SEEK_THR) ++ ++/* Min samples used for peak rate estimation (for autotuning). */ ++#define BFQ_PEAK_RATE_SAMPLES 32 ++ ++/* Shift used for peak rate fixed precision calculations. */ ++#define BFQ_RATE_SHIFT 16 ++ ++/* ++ * By default, BFQ computes the duration of the weight raising for ++ * interactive applications automatically, using the following formula: ++ * duration = (R / r) * T, where r is the peak rate of the device, and ++ * R and T are two reference parameters. ++ * In particular, R is the peak rate of the reference device (see below), ++ * and T is a reference time: given the systems that are likely to be ++ * installed on the reference device according to its speed class, T is ++ * about the maximum time needed, under BFQ and while reading two files in ++ * parallel, to load typical large applications on these systems. ++ * In practice, the slower/faster the device at hand is, the more/less it ++ * takes to load applications with respect to the reference device. ++ * Accordingly, the longer/shorter BFQ grants weight raising to interactive ++ * applications. ++ * ++ * BFQ uses four different reference pairs (R, T), depending on: ++ * . whether the device is rotational or non-rotational; ++ * . whether the device is slow, such as old or portable HDDs, as well as ++ * SD cards, or fast, such as newer HDDs and SSDs. ++ * ++ * The device's speed class is dynamically (re)detected in ++ * bfq_update_peak_rate() every time the estimated peak rate is updated. ++ * ++ * In the following definitions, R_slow[0]/R_fast[0] and T_slow[0]/T_fast[0] ++ * are the reference values for a slow/fast rotational device, whereas ++ * R_slow[1]/R_fast[1] and T_slow[1]/T_fast[1] are the reference values for ++ * a slow/fast non-rotational device. Finally, device_speed_thresh are the ++ * thresholds used to switch between speed classes. ++ * Both the reference peak rates and the thresholds are measured in ++ * sectors/usec, left-shifted by BFQ_RATE_SHIFT. ++ */ ++static int R_slow[2] = {1536, 10752}; ++static int R_fast[2] = {17415, 34791}; ++/* ++ * To improve readability, a conversion function is used to initialize the ++ * following arrays, which entails that they can be initialized only in a ++ * function. ++ */ ++static int T_slow[2]; ++static int T_fast[2]; ++static int device_speed_thresh[2]; ++ ++#define BFQ_SERVICE_TREE_INIT ((struct bfq_service_tree) \ ++ { RB_ROOT, RB_ROOT, NULL, NULL, 0, 0 }) ++ ++#define RQ_BIC(rq) ((struct bfq_io_cq *) (rq)->elv.priv[0]) ++#define RQ_BFQQ(rq) ((rq)->elv.priv[1]) ++ ++static inline void bfq_schedule_dispatch(struct bfq_data *bfqd); ++ ++#include "bfq-ioc.c" ++#include "bfq-sched.c" ++#include "bfq-cgroup.c" ++ ++#define bfq_class_idle(bfqq) ((bfqq)->entity.ioprio_class ==\ ++ IOPRIO_CLASS_IDLE) ++#define bfq_class_rt(bfqq) ((bfqq)->entity.ioprio_class ==\ ++ IOPRIO_CLASS_RT) ++ ++#define bfq_sample_valid(samples) ((samples) > 80) ++ ++/* ++ * We regard a request as SYNC, if either it's a read or has the SYNC bit ++ * set (in which case it could also be a direct WRITE). ++ */ ++static inline int bfq_bio_sync(struct bio *bio) ++{ ++ if (bio_data_dir(bio) == READ || (bio->bi_rw & REQ_SYNC)) ++ return 1; ++ ++ return 0; ++} ++ ++/* ++ * Scheduler run of queue, if there are requests pending and no one in the ++ * driver that will restart queueing. ++ */ ++static inline void bfq_schedule_dispatch(struct bfq_data *bfqd) ++{ ++ if (bfqd->queued != 0) { ++ bfq_log(bfqd, "schedule dispatch"); ++ kblockd_schedule_work(bfqd->queue, &bfqd->unplug_work); ++ } ++} ++ ++/* ++ * Lifted from AS - choose which of rq1 and rq2 that is best served now. ++ * We choose the request that is closesr to the head right now. Distance ++ * behind the head is penalized and only allowed to a certain extent. ++ */ ++static struct request *bfq_choose_req(struct bfq_data *bfqd, ++ struct request *rq1, ++ struct request *rq2, ++ sector_t last) ++{ ++ sector_t s1, s2, d1 = 0, d2 = 0; ++ unsigned long back_max; ++#define BFQ_RQ1_WRAP 0x01 /* request 1 wraps */ ++#define BFQ_RQ2_WRAP 0x02 /* request 2 wraps */ ++ unsigned wrap = 0; /* bit mask: requests behind the disk head? */ ++ ++ if (rq1 == NULL || rq1 == rq2) ++ return rq2; ++ if (rq2 == NULL) ++ return rq1; ++ ++ if (rq_is_sync(rq1) && !rq_is_sync(rq2)) ++ return rq1; ++ else if (rq_is_sync(rq2) && !rq_is_sync(rq1)) ++ return rq2; ++ if ((rq1->cmd_flags & REQ_META) && !(rq2->cmd_flags & REQ_META)) ++ return rq1; ++ else if ((rq2->cmd_flags & REQ_META) && !(rq1->cmd_flags & REQ_META)) ++ return rq2; ++ ++ s1 = blk_rq_pos(rq1); ++ s2 = blk_rq_pos(rq2); ++ ++ /* ++ * By definition, 1KiB is 2 sectors. ++ */ ++ back_max = bfqd->bfq_back_max * 2; ++ ++ /* ++ * Strict one way elevator _except_ in the case where we allow ++ * short backward seeks which are biased as twice the cost of a ++ * similar forward seek. ++ */ ++ if (s1 >= last) ++ d1 = s1 - last; ++ else if (s1 + back_max >= last) ++ d1 = (last - s1) * bfqd->bfq_back_penalty; ++ else ++ wrap |= BFQ_RQ1_WRAP; ++ ++ if (s2 >= last) ++ d2 = s2 - last; ++ else if (s2 + back_max >= last) ++ d2 = (last - s2) * bfqd->bfq_back_penalty; ++ else ++ wrap |= BFQ_RQ2_WRAP; ++ ++ /* Found required data */ ++ ++ /* ++ * By doing switch() on the bit mask "wrap" we avoid having to ++ * check two variables for all permutations: --> faster! ++ */ ++ switch (wrap) { ++ case 0: /* common case for CFQ: rq1 and rq2 not wrapped */ ++ if (d1 < d2) ++ return rq1; ++ else if (d2 < d1) ++ return rq2; ++ else { ++ if (s1 >= s2) ++ return rq1; ++ else ++ return rq2; ++ } ++ ++ case BFQ_RQ2_WRAP: ++ return rq1; ++ case BFQ_RQ1_WRAP: ++ return rq2; ++ case (BFQ_RQ1_WRAP|BFQ_RQ2_WRAP): /* both rqs wrapped */ ++ default: ++ /* ++ * Since both rqs are wrapped, ++ * start with the one that's further behind head ++ * (--> only *one* back seek required), ++ * since back seek takes more time than forward. ++ */ ++ if (s1 <= s2) ++ return rq1; ++ else ++ return rq2; ++ } ++} ++ ++static struct bfq_queue * ++bfq_rq_pos_tree_lookup(struct bfq_data *bfqd, struct rb_root *root, ++ sector_t sector, struct rb_node **ret_parent, ++ struct rb_node ***rb_link) ++{ ++ struct rb_node **p, *parent; ++ struct bfq_queue *bfqq = NULL; ++ ++ parent = NULL; ++ p = &root->rb_node; ++ while (*p) { ++ struct rb_node **n; ++ ++ parent = *p; ++ bfqq = rb_entry(parent, struct bfq_queue, pos_node); ++ ++ /* ++ * Sort strictly based on sector. Smallest to the left, ++ * largest to the right. ++ */ ++ if (sector > blk_rq_pos(bfqq->next_rq)) ++ n = &(*p)->rb_right; ++ else if (sector < blk_rq_pos(bfqq->next_rq)) ++ n = &(*p)->rb_left; ++ else ++ break; ++ p = n; ++ bfqq = NULL; ++ } ++ ++ *ret_parent = parent; ++ if (rb_link) ++ *rb_link = p; ++ ++ bfq_log(bfqd, "rq_pos_tree_lookup %llu: returning %d", ++ (long long unsigned)sector, ++ bfqq != NULL ? bfqq->pid : 0); ++ ++ return bfqq; ++} ++ ++static void bfq_rq_pos_tree_add(struct bfq_data *bfqd, struct bfq_queue *bfqq) ++{ ++ struct rb_node **p, *parent; ++ struct bfq_queue *__bfqq; ++ ++ if (bfqq->pos_root != NULL) { ++ rb_erase(&bfqq->pos_node, bfqq->pos_root); ++ bfqq->pos_root = NULL; ++ } ++ ++ if (bfq_class_idle(bfqq)) ++ return; ++ if (!bfqq->next_rq) ++ return; ++ ++ bfqq->pos_root = &bfqd->rq_pos_tree; ++ __bfqq = bfq_rq_pos_tree_lookup(bfqd, bfqq->pos_root, ++ blk_rq_pos(bfqq->next_rq), &parent, &p); ++ if (__bfqq == NULL) { ++ rb_link_node(&bfqq->pos_node, parent, p); ++ rb_insert_color(&bfqq->pos_node, bfqq->pos_root); ++ } else ++ bfqq->pos_root = NULL; ++} ++ ++/* ++ * Tell whether there are active queues or groups with differentiated weights. ++ */ ++static inline bool bfq_differentiated_weights(struct bfq_data *bfqd) ++{ ++ BUG_ON(!bfqd->hw_tag); ++ /* ++ * For weights to differ, at least one of the trees must contain ++ * at least two nodes. ++ */ ++ return (!RB_EMPTY_ROOT(&bfqd->queue_weights_tree) && ++ (bfqd->queue_weights_tree.rb_node->rb_left || ++ bfqd->queue_weights_tree.rb_node->rb_right) ++#ifdef CONFIG_CGROUP_BFQIO ++ ) || ++ (!RB_EMPTY_ROOT(&bfqd->group_weights_tree) && ++ (bfqd->group_weights_tree.rb_node->rb_left || ++ bfqd->group_weights_tree.rb_node->rb_right) ++#endif ++ ); ++} ++ ++/* ++ * If the weight-counter tree passed as input contains no counter for ++ * the weight of the input entity, then add that counter; otherwise just ++ * increment the existing counter. ++ * ++ * Note that weight-counter trees contain few nodes in mostly symmetric ++ * scenarios. For example, if all queues have the same weight, then the ++ * weight-counter tree for the queues may contain at most one node. ++ * This holds even if low_latency is on, because weight-raised queues ++ * are not inserted in the tree. ++ * In most scenarios, the rate at which nodes are created/destroyed ++ * should be low too. ++ */ ++static void bfq_weights_tree_add(struct bfq_data *bfqd, ++ struct bfq_entity *entity, ++ struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* ++ * Do not insert if: ++ * - the device does not support queueing; ++ * - the entity is already associated with a counter, which happens if: ++ * 1) the entity is associated with a queue, 2) a request arrival ++ * has caused the queue to become both non-weight-raised, and hence ++ * change its weight, and backlogged; in this respect, each ++ * of the two events causes an invocation of this function, ++ * 3) this is the invocation of this function caused by the second ++ * event. This second invocation is actually useless, and we handle ++ * this fact by exiting immediately. More efficient or clearer ++ * solutions might possibly be adopted. ++ */ ++ if (!bfqd->hw_tag || entity->weight_counter) ++ return; ++ ++ while (*new) { ++ struct bfq_weight_counter *__counter = container_of(*new, ++ struct bfq_weight_counter, ++ weights_node); ++ parent = *new; ++ ++ if (entity->weight == __counter->weight) { ++ entity->weight_counter = __counter; ++ goto inc_counter; ++ } ++ if (entity->weight < __counter->weight) ++ new = &((*new)->rb_left); ++ else ++ new = &((*new)->rb_right); ++ } ++ ++ entity->weight_counter = kzalloc(sizeof(struct bfq_weight_counter), ++ GFP_ATOMIC); ++ entity->weight_counter->weight = entity->weight; ++ rb_link_node(&entity->weight_counter->weights_node, parent, new); ++ rb_insert_color(&entity->weight_counter->weights_node, root); ++ ++inc_counter: ++ entity->weight_counter->num_active++; ++} ++ ++/* ++ * Decrement the weight counter associated with the entity, and, if the ++ * counter reaches 0, remove the counter from the tree. ++ * See the comments to the function bfq_weights_tree_add() for considerations ++ * about overhead. ++ */ ++static void bfq_weights_tree_remove(struct bfq_data *bfqd, ++ struct bfq_entity *entity, ++ struct rb_root *root) ++{ ++ /* ++ * Check whether the entity is actually associated with a counter. ++ * In fact, the device may not be considered NCQ-capable for a while, ++ * which implies that no insertion in the weight trees is performed, ++ * after which the device may start to be deemed NCQ-capable, and hence ++ * this function may start to be invoked. This may cause the function ++ * to be invoked for entities that are not associated with any counter. ++ */ ++ if (!entity->weight_counter) ++ return; ++ ++ BUG_ON(RB_EMPTY_ROOT(root)); ++ BUG_ON(entity->weight_counter->weight != entity->weight); ++ ++ BUG_ON(!entity->weight_counter->num_active); ++ entity->weight_counter->num_active--; ++ if (entity->weight_counter->num_active > 0) ++ goto reset_entity_pointer; ++ ++ rb_erase(&entity->weight_counter->weights_node, root); ++ kfree(entity->weight_counter); ++ ++reset_entity_pointer: ++ entity->weight_counter = NULL; ++} ++ ++static struct request *bfq_find_next_rq(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ struct request *last) ++{ ++ struct rb_node *rbnext = rb_next(&last->rb_node); ++ struct rb_node *rbprev = rb_prev(&last->rb_node); ++ struct request *next = NULL, *prev = NULL; ++ ++ BUG_ON(RB_EMPTY_NODE(&last->rb_node)); ++ ++ if (rbprev != NULL) ++ prev = rb_entry_rq(rbprev); ++ ++ if (rbnext != NULL) ++ next = rb_entry_rq(rbnext); ++ else { ++ rbnext = rb_first(&bfqq->sort_list); ++ if (rbnext && rbnext != &last->rb_node) ++ next = rb_entry_rq(rbnext); ++ } ++ ++ return bfq_choose_req(bfqd, next, prev, blk_rq_pos(last)); ++} ++ ++/* see the definition of bfq_async_charge_factor for details */ ++static inline unsigned long bfq_serv_to_charge(struct request *rq, ++ struct bfq_queue *bfqq) ++{ ++ return blk_rq_sectors(rq) * ++ (1 + ((!bfq_bfqq_sync(bfqq)) * (bfqq->wr_coeff == 1) * ++ bfq_async_charge_factor)); ++} ++ ++/** ++ * bfq_updated_next_req - update the queue after a new next_rq selection. ++ * @bfqd: the device data the queue belongs to. ++ * @bfqq: the queue to update. ++ * ++ * If the first request of a queue changes we make sure that the queue ++ * has enough budget to serve at least its first request (if the ++ * request has grown). We do this because if the queue has not enough ++ * budget for its first request, it has to go through two dispatch ++ * rounds to actually get it dispatched. ++ */ ++static void bfq_updated_next_req(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ struct bfq_service_tree *st = bfq_entity_service_tree(entity); ++ struct request *next_rq = bfqq->next_rq; ++ unsigned long new_budget; ++ ++ if (next_rq == NULL) ++ return; ++ ++ if (bfqq == bfqd->in_service_queue) ++ /* ++ * In order not to break guarantees, budgets cannot be ++ * changed after an entity has been selected. ++ */ ++ return; ++ ++ BUG_ON(entity->tree != &st->active); ++ BUG_ON(entity == entity->sched_data->in_service_entity); ++ ++ new_budget = max_t(unsigned long, bfqq->max_budget, ++ bfq_serv_to_charge(next_rq, bfqq)); ++ if (entity->budget != new_budget) { ++ entity->budget = new_budget; ++ bfq_log_bfqq(bfqd, bfqq, "updated next rq: new budget %lu", ++ new_budget); ++ bfq_activate_bfqq(bfqd, bfqq); ++ } ++} ++ ++static inline unsigned int bfq_wr_duration(struct bfq_data *bfqd) ++{ ++ u64 dur; ++ ++ if (bfqd->bfq_wr_max_time > 0) ++ return bfqd->bfq_wr_max_time; ++ ++ dur = bfqd->RT_prod; ++ do_div(dur, bfqd->peak_rate); ++ ++ return dur; ++} ++ ++static inline unsigned ++bfq_bfqq_cooperations(struct bfq_queue *bfqq) ++{ ++ return bfqq->bic ? bfqq->bic->cooperations : 0; ++} ++ ++static inline void ++bfq_bfqq_resume_state(struct bfq_queue *bfqq, struct bfq_io_cq *bic) ++{ ++ if (bic->saved_idle_window) ++ bfq_mark_bfqq_idle_window(bfqq); ++ else ++ bfq_clear_bfqq_idle_window(bfqq); ++ if (bic->saved_IO_bound) ++ bfq_mark_bfqq_IO_bound(bfqq); ++ else ++ bfq_clear_bfqq_IO_bound(bfqq); ++ if (bic->wr_time_left && bfqq->bfqd->low_latency && ++ bic->cooperations < bfqq->bfqd->bfq_coop_thresh) { ++ /* ++ * Start a weight raising period with the duration given by ++ * the raising_time_left snapshot. ++ */ ++ if (bfq_bfqq_busy(bfqq)) ++ bfqq->bfqd->wr_busy_queues++; ++ bfqq->wr_coeff = bfqq->bfqd->bfq_wr_coeff; ++ bfqq->wr_cur_max_time = bic->wr_time_left; ++ bfqq->last_wr_start_finish = jiffies; ++ bfqq->entity.ioprio_changed = 1; ++ } ++ /* ++ * Clear wr_time_left to prevent bfq_bfqq_save_state() from ++ * getting confused about the queue's need of a weight-raising ++ * period. ++ */ ++ bic->wr_time_left = 0; ++} ++ ++/* ++ * Must be called with the queue_lock held. ++ */ ++static int bfqq_process_refs(struct bfq_queue *bfqq) ++{ ++ int process_refs, io_refs; ++ ++ io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE]; ++ process_refs = atomic_read(&bfqq->ref) - io_refs - bfqq->entity.on_st; ++ BUG_ON(process_refs < 0); ++ return process_refs; ++} ++ ++static void bfq_add_request(struct request *rq) ++{ ++ struct bfq_queue *bfqq = RQ_BFQQ(rq); ++ struct bfq_entity *entity = &bfqq->entity; ++ struct bfq_data *bfqd = bfqq->bfqd; ++ struct request *next_rq, *prev; ++ unsigned long old_wr_coeff = bfqq->wr_coeff; ++ int idle_for_long_time = 0; ++ ++ bfq_log_bfqq(bfqd, bfqq, "add_request %d", rq_is_sync(rq)); ++ bfqq->queued[rq_is_sync(rq)]++; ++ bfqd->queued++; ++ ++ elv_rb_add(&bfqq->sort_list, rq); ++ ++ /* ++ * Check if this request is a better next-serve candidate. ++ */ ++ prev = bfqq->next_rq; ++ next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq, bfqd->last_position); ++ BUG_ON(next_rq == NULL); ++ bfqq->next_rq = next_rq; ++ ++ /* ++ * Adjust priority tree position, if next_rq changes. ++ */ ++ if (prev != bfqq->next_rq) ++ bfq_rq_pos_tree_add(bfqd, bfqq); ++ ++ if (!bfq_bfqq_busy(bfqq)) { ++ int soft_rt = bfqd->bfq_wr_max_softrt_rate > 0 && ++ bfq_bfqq_cooperations(bfqq) < bfqd->bfq_coop_thresh && ++ time_is_before_jiffies(bfqq->soft_rt_next_start); ++ idle_for_long_time = bfq_bfqq_cooperations(bfqq) < ++ bfqd->bfq_coop_thresh && ++ time_is_before_jiffies( ++ bfqq->budget_timeout + ++ bfqd->bfq_wr_min_idle_time); ++ entity->budget = max_t(unsigned long, bfqq->max_budget, ++ bfq_serv_to_charge(next_rq, bfqq)); ++ ++ if (!bfq_bfqq_IO_bound(bfqq)) { ++ if (time_before(jiffies, ++ RQ_BIC(rq)->ttime.last_end_request + ++ bfqd->bfq_slice_idle)) { ++ bfqq->requests_within_timer++; ++ if (bfqq->requests_within_timer >= ++ bfqd->bfq_requests_within_timer) ++ bfq_mark_bfqq_IO_bound(bfqq); ++ } else ++ bfqq->requests_within_timer = 0; ++ } ++ ++ if (!bfqd->low_latency) ++ goto add_bfqq_busy; ++ ++ if (bfq_bfqq_just_split(bfqq)) ++ goto set_ioprio_changed; ++ ++ /* ++ * If the queue: ++ * - is not being boosted, ++ * - has been idle for enough time, ++ * - is not a sync queue or is linked to a bfq_io_cq (it is ++ * shared "for its nature" or it is not shared and its ++ * requests have not been redirected to a shared queue) ++ * start a weight-raising period. ++ */ ++ if (old_wr_coeff == 1 && (idle_for_long_time || soft_rt) && ++ (!bfq_bfqq_sync(bfqq) || bfqq->bic != NULL)) { ++ bfqq->wr_coeff = bfqd->bfq_wr_coeff; ++ if (idle_for_long_time) ++ bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); ++ else ++ bfqq->wr_cur_max_time = ++ bfqd->bfq_wr_rt_max_time; ++ bfq_log_bfqq(bfqd, bfqq, ++ "wrais starting at %lu, rais_max_time %u", ++ jiffies, ++ jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ } else if (old_wr_coeff > 1) { ++ if (idle_for_long_time) ++ bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); ++ else if (bfq_bfqq_cooperations(bfqq) >= ++ bfqd->bfq_coop_thresh || ++ (bfqq->wr_cur_max_time == ++ bfqd->bfq_wr_rt_max_time && ++ !soft_rt)) { ++ bfqq->wr_coeff = 1; ++ bfq_log_bfqq(bfqd, bfqq, ++ "wrais ending at %lu, rais_max_time %u", ++ jiffies, ++ jiffies_to_msecs(bfqq-> ++ wr_cur_max_time)); ++ } else if (time_before( ++ bfqq->last_wr_start_finish + ++ bfqq->wr_cur_max_time, ++ jiffies + ++ bfqd->bfq_wr_rt_max_time) && ++ soft_rt) { ++ /* ++ * ++ * The remaining weight-raising time is lower ++ * than bfqd->bfq_wr_rt_max_time, which means ++ * that the application is enjoying weight ++ * raising either because deemed soft-rt in ++ * the near past, or because deemed interactive ++ * a long ago. ++ * In both cases, resetting now the current ++ * remaining weight-raising time for the ++ * application to the weight-raising duration ++ * for soft rt applications would not cause any ++ * latency increase for the application (as the ++ * new duration would be higher than the ++ * remaining time). ++ * ++ * In addition, the application is now meeting ++ * the requirements for being deemed soft rt. ++ * In the end we can correctly and safely ++ * (re)charge the weight-raising duration for ++ * the application with the weight-raising ++ * duration for soft rt applications. ++ * ++ * In particular, doing this recharge now, i.e., ++ * before the weight-raising period for the ++ * application finishes, reduces the probability ++ * of the following negative scenario: ++ * 1) the weight of a soft rt application is ++ * raised at startup (as for any newly ++ * created application), ++ * 2) since the application is not interactive, ++ * at a certain time weight-raising is ++ * stopped for the application, ++ * 3) at that time the application happens to ++ * still have pending requests, and hence ++ * is destined to not have a chance to be ++ * deemed soft rt before these requests are ++ * completed (see the comments to the ++ * function bfq_bfqq_softrt_next_start() ++ * for details on soft rt detection), ++ * 4) these pending requests experience a high ++ * latency because the application is not ++ * weight-raised while they are pending. ++ */ ++ bfqq->last_wr_start_finish = jiffies; ++ bfqq->wr_cur_max_time = ++ bfqd->bfq_wr_rt_max_time; ++ } ++ } ++set_ioprio_changed: ++ if (old_wr_coeff != bfqq->wr_coeff) ++ entity->ioprio_changed = 1; ++add_bfqq_busy: ++ bfqq->last_idle_bklogged = jiffies; ++ bfqq->service_from_backlogged = 0; ++ bfq_clear_bfqq_softrt_update(bfqq); ++ bfq_add_bfqq_busy(bfqd, bfqq); ++ } else { ++ if (bfqd->low_latency && old_wr_coeff == 1 && !rq_is_sync(rq) && ++ time_is_before_jiffies( ++ bfqq->last_wr_start_finish + ++ bfqd->bfq_wr_min_inter_arr_async)) { ++ bfqq->wr_coeff = bfqd->bfq_wr_coeff; ++ bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); ++ ++ bfqd->wr_busy_queues++; ++ entity->ioprio_changed = 1; ++ bfq_log_bfqq(bfqd, bfqq, ++ "non-idle wrais starting at %lu, rais_max_time %u", ++ jiffies, ++ jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ } ++ if (prev != bfqq->next_rq) ++ bfq_updated_next_req(bfqd, bfqq); ++ } ++ ++ if (bfqd->low_latency && ++ (old_wr_coeff == 1 || bfqq->wr_coeff == 1 || ++ idle_for_long_time)) ++ bfqq->last_wr_start_finish = jiffies; ++} ++ ++static struct request *bfq_find_rq_fmerge(struct bfq_data *bfqd, ++ struct bio *bio) ++{ ++ struct task_struct *tsk = current; ++ struct bfq_io_cq *bic; ++ struct bfq_queue *bfqq; ++ ++ bic = bfq_bic_lookup(bfqd, tsk->io_context); ++ if (bic == NULL) ++ return NULL; ++ ++ bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); ++ if (bfqq != NULL) ++ return elv_rb_find(&bfqq->sort_list, bio_end_sector(bio)); ++ ++ return NULL; ++} ++ ++static void bfq_activate_request(struct request_queue *q, struct request *rq) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ ++ bfqd->rq_in_driver++; ++ bfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq); ++ bfq_log(bfqd, "activate_request: new bfqd->last_position %llu", ++ (long long unsigned)bfqd->last_position); ++} ++ ++static inline void bfq_deactivate_request(struct request_queue *q, ++ struct request *rq) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ ++ BUG_ON(bfqd->rq_in_driver == 0); ++ bfqd->rq_in_driver--; ++} ++ ++static void bfq_remove_request(struct request *rq) ++{ ++ struct bfq_queue *bfqq = RQ_BFQQ(rq); ++ struct bfq_data *bfqd = bfqq->bfqd; ++ const int sync = rq_is_sync(rq); ++ ++ if (bfqq->next_rq == rq) { ++ bfqq->next_rq = bfq_find_next_rq(bfqd, bfqq, rq); ++ bfq_updated_next_req(bfqd, bfqq); ++ } ++ ++ list_del_init(&rq->queuelist); ++ BUG_ON(bfqq->queued[sync] == 0); ++ bfqq->queued[sync]--; ++ bfqd->queued--; ++ elv_rb_del(&bfqq->sort_list, rq); ++ ++ if (RB_EMPTY_ROOT(&bfqq->sort_list)) { ++ if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->in_service_queue) ++ bfq_del_bfqq_busy(bfqd, bfqq, 1); ++ /* ++ * Remove queue from request-position tree as it is empty. ++ */ ++ if (bfqq->pos_root != NULL) { ++ rb_erase(&bfqq->pos_node, bfqq->pos_root); ++ bfqq->pos_root = NULL; ++ } ++ } ++ ++ if (rq->cmd_flags & REQ_META) { ++ BUG_ON(bfqq->meta_pending == 0); ++ bfqq->meta_pending--; ++ } ++} ++ ++static int bfq_merge(struct request_queue *q, struct request **req, ++ struct bio *bio) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ struct request *__rq; ++ ++ __rq = bfq_find_rq_fmerge(bfqd, bio); ++ if (__rq != NULL && elv_rq_merge_ok(__rq, bio)) { ++ *req = __rq; ++ return ELEVATOR_FRONT_MERGE; ++ } ++ ++ return ELEVATOR_NO_MERGE; ++} ++ ++static void bfq_merged_request(struct request_queue *q, struct request *req, ++ int type) ++{ ++ if (type == ELEVATOR_FRONT_MERGE && ++ rb_prev(&req->rb_node) && ++ blk_rq_pos(req) < ++ blk_rq_pos(container_of(rb_prev(&req->rb_node), ++ struct request, rb_node))) { ++ struct bfq_queue *bfqq = RQ_BFQQ(req); ++ struct bfq_data *bfqd = bfqq->bfqd; ++ struct request *prev, *next_rq; ++ ++ /* Reposition request in its sort_list */ ++ elv_rb_del(&bfqq->sort_list, req); ++ elv_rb_add(&bfqq->sort_list, req); ++ /* Choose next request to be served for bfqq */ ++ prev = bfqq->next_rq; ++ next_rq = bfq_choose_req(bfqd, bfqq->next_rq, req, ++ bfqd->last_position); ++ BUG_ON(next_rq == NULL); ++ bfqq->next_rq = next_rq; ++ /* ++ * If next_rq changes, update both the queue's budget to ++ * fit the new request and the queue's position in its ++ * rq_pos_tree. ++ */ ++ if (prev != bfqq->next_rq) { ++ bfq_updated_next_req(bfqd, bfqq); ++ bfq_rq_pos_tree_add(bfqd, bfqq); ++ } ++ } ++} ++ ++static void bfq_merged_requests(struct request_queue *q, struct request *rq, ++ struct request *next) ++{ ++ struct bfq_queue *bfqq = RQ_BFQQ(rq); ++ ++ /* ++ * Reposition in fifo if next is older than rq. ++ */ ++ if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) && ++ time_before(rq_fifo_time(next), rq_fifo_time(rq))) { ++ list_move(&rq->queuelist, &next->queuelist); ++ rq_set_fifo_time(rq, rq_fifo_time(next)); ++ } ++ ++ if (bfqq->next_rq == next) ++ bfqq->next_rq = rq; ++ ++ bfq_remove_request(next); ++} ++ ++/* Must be called with bfqq != NULL */ ++static inline void bfq_bfqq_end_wr(struct bfq_queue *bfqq) ++{ ++ BUG_ON(bfqq == NULL); ++ if (bfq_bfqq_busy(bfqq)) ++ bfqq->bfqd->wr_busy_queues--; ++ bfqq->wr_coeff = 1; ++ bfqq->wr_cur_max_time = 0; ++ /* Trigger a weight change on the next activation of the queue */ ++ bfqq->entity.ioprio_changed = 1; ++} ++ ++static void bfq_end_wr_async_queues(struct bfq_data *bfqd, ++ struct bfq_group *bfqg) ++{ ++ int i, j; ++ ++ for (i = 0; i < 2; i++) ++ for (j = 0; j < IOPRIO_BE_NR; j++) ++ if (bfqg->async_bfqq[i][j] != NULL) ++ bfq_bfqq_end_wr(bfqg->async_bfqq[i][j]); ++ if (bfqg->async_idle_bfqq != NULL) ++ bfq_bfqq_end_wr(bfqg->async_idle_bfqq); ++} ++ ++static void bfq_end_wr(struct bfq_data *bfqd) ++{ ++ struct bfq_queue *bfqq; ++ ++ spin_lock_irq(bfqd->queue->queue_lock); ++ ++ list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) ++ bfq_bfqq_end_wr(bfqq); ++ list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) ++ bfq_bfqq_end_wr(bfqq); ++ bfq_end_wr_async(bfqd); ++ ++ spin_unlock_irq(bfqd->queue->queue_lock); ++} ++ ++static inline sector_t bfq_io_struct_pos(void *io_struct, bool request) ++{ ++ if (request) ++ return blk_rq_pos(io_struct); ++ else ++ return ((struct bio *)io_struct)->bi_iter.bi_sector; ++} ++ ++static inline sector_t bfq_dist_from(sector_t pos1, ++ sector_t pos2) ++{ ++ if (pos1 >= pos2) ++ return pos1 - pos2; ++ else ++ return pos2 - pos1; ++} ++ ++static inline int bfq_rq_close_to_sector(void *io_struct, bool request, ++ sector_t sector) ++{ ++ return bfq_dist_from(bfq_io_struct_pos(io_struct, request), sector) <= ++ BFQQ_SEEK_THR; ++} ++ ++static struct bfq_queue *bfqq_close(struct bfq_data *bfqd, sector_t sector) ++{ ++ struct rb_root *root = &bfqd->rq_pos_tree; ++ struct rb_node *parent, *node; ++ struct bfq_queue *__bfqq; ++ ++ if (RB_EMPTY_ROOT(root)) ++ return NULL; ++ ++ /* ++ * First, if we find a request starting at the end of the last ++ * request, choose it. ++ */ ++ __bfqq = bfq_rq_pos_tree_lookup(bfqd, root, sector, &parent, NULL); ++ if (__bfqq != NULL) ++ return __bfqq; ++ ++ /* ++ * If the exact sector wasn't found, the parent of the NULL leaf ++ * will contain the closest sector (rq_pos_tree sorted by ++ * next_request position). ++ */ ++ __bfqq = rb_entry(parent, struct bfq_queue, pos_node); ++ if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector)) ++ return __bfqq; ++ ++ if (blk_rq_pos(__bfqq->next_rq) < sector) ++ node = rb_next(&__bfqq->pos_node); ++ else ++ node = rb_prev(&__bfqq->pos_node); ++ if (node == NULL) ++ return NULL; ++ ++ __bfqq = rb_entry(node, struct bfq_queue, pos_node); ++ if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector)) ++ return __bfqq; ++ ++ return NULL; ++} ++ ++/* ++ * bfqd - obvious ++ * cur_bfqq - passed in so that we don't decide that the current queue ++ * is closely cooperating with itself ++ * sector - used as a reference point to search for a close queue ++ */ ++static struct bfq_queue *bfq_close_cooperator(struct bfq_data *bfqd, ++ struct bfq_queue *cur_bfqq, ++ sector_t sector) ++{ ++ struct bfq_queue *bfqq; ++ ++ if (bfq_class_idle(cur_bfqq)) ++ return NULL; ++ if (!bfq_bfqq_sync(cur_bfqq)) ++ return NULL; ++ if (BFQQ_SEEKY(cur_bfqq)) ++ return NULL; ++ ++ /* If device has only one backlogged bfq_queue, don't search. */ ++ if (bfqd->busy_queues == 1) ++ return NULL; ++ ++ /* ++ * We should notice if some of the queues are cooperating, e.g. ++ * working closely on the same area of the disk. In that case, ++ * we can group them together and don't waste time idling. ++ */ ++ bfqq = bfqq_close(bfqd, sector); ++ if (bfqq == NULL || bfqq == cur_bfqq) ++ return NULL; ++ ++ /* ++ * Do not merge queues from different bfq_groups. ++ */ ++ if (bfqq->entity.parent != cur_bfqq->entity.parent) ++ return NULL; ++ ++ /* ++ * It only makes sense to merge sync queues. ++ */ ++ if (!bfq_bfqq_sync(bfqq)) ++ return NULL; ++ if (BFQQ_SEEKY(bfqq)) ++ return NULL; ++ ++ /* ++ * Do not merge queues of different priority classes. ++ */ ++ if (bfq_class_rt(bfqq) != bfq_class_rt(cur_bfqq)) ++ return NULL; ++ ++ return bfqq; ++} ++ ++static struct bfq_queue * ++bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) ++{ ++ int process_refs, new_process_refs; ++ struct bfq_queue *__bfqq; ++ ++ /* ++ * If there are no process references on the new_bfqq, then it is ++ * unsafe to follow the ->new_bfqq chain as other bfqq's in the chain ++ * may have dropped their last reference (not just their last process ++ * reference). ++ */ ++ if (!bfqq_process_refs(new_bfqq)) ++ return NULL; ++ ++ /* Avoid a circular list and skip interim queue merges. */ ++ while ((__bfqq = new_bfqq->new_bfqq)) { ++ if (__bfqq == bfqq) ++ return NULL; ++ new_bfqq = __bfqq; ++ } ++ ++ process_refs = bfqq_process_refs(bfqq); ++ new_process_refs = bfqq_process_refs(new_bfqq); ++ /* ++ * If the process for the bfqq has gone away, there is no ++ * sense in merging the queues. ++ */ ++ if (process_refs == 0 || new_process_refs == 0) ++ return NULL; ++ ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d", ++ new_bfqq->pid); ++ ++ /* ++ * Merging is just a redirection: the requests of the process ++ * owning one of the two queues are redirected to the other queue. ++ * The latter queue, in its turn, is set as shared if this is the ++ * first time that the requests of some process are redirected to ++ * it. ++ * ++ * We redirect bfqq to new_bfqq and not the opposite, because we ++ * are in the context of the process owning bfqq, hence we have ++ * the io_cq of this process. So we can immediately configure this ++ * io_cq to redirect the requests of the process to new_bfqq. ++ * ++ * NOTE, even if new_bfqq coincides with the in-service queue, the ++ * io_cq of new_bfqq is not available, because, if the in-service ++ * queue is shared, bfqd->in_service_bic may not point to the ++ * io_cq of the in-service queue. ++ * Redirecting the requests of the process owning bfqq to the ++ * currently in-service queue is in any case the best option, as ++ * we feed the in-service queue with new requests close to the ++ * last request served and, by doing so, hopefully increase the ++ * throughput. ++ */ ++ bfqq->new_bfqq = new_bfqq; ++ atomic_add(process_refs, &new_bfqq->ref); ++ return new_bfqq; ++} ++ ++/* ++ * Attempt to schedule a merge of bfqq with the currently in-service queue ++ * or with a close queue among the scheduled queues. ++ * Return NULL if no merge was scheduled, a pointer to the shared bfq_queue ++ * structure otherwise. ++ */ ++static struct bfq_queue * ++bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ void *io_struct, bool request) ++{ ++ struct bfq_queue *in_service_bfqq, *new_bfqq; ++ ++ if (bfqq->new_bfqq) ++ return bfqq->new_bfqq; ++ ++ if (!io_struct) ++ return NULL; ++ ++ in_service_bfqq = bfqd->in_service_queue; ++ ++ if (in_service_bfqq == NULL || in_service_bfqq == bfqq || ++ !bfqd->in_service_bic) ++ goto check_scheduled; ++ ++ if (bfq_class_idle(in_service_bfqq) || bfq_class_idle(bfqq)) ++ goto check_scheduled; ++ ++ if (bfq_class_rt(in_service_bfqq) != bfq_class_rt(bfqq)) ++ goto check_scheduled; ++ ++ if (in_service_bfqq->entity.parent != bfqq->entity.parent) ++ goto check_scheduled; ++ ++ if (bfq_rq_close_to_sector(io_struct, request, bfqd->last_position) && ++ bfq_bfqq_sync(in_service_bfqq) && bfq_bfqq_sync(bfqq)) { ++ new_bfqq = bfq_setup_merge(bfqq, in_service_bfqq); ++ if (new_bfqq != NULL) ++ return new_bfqq; /* Merge with in-service queue */ ++ } ++ ++ /* ++ * Check whether there is a cooperator among currently scheduled ++ * queues. The only thing we need is that the bio/request is not ++ * NULL, as we need it to establish whether a cooperator exists. ++ */ ++check_scheduled: ++ new_bfqq = bfq_close_cooperator(bfqd, bfqq, ++ bfq_io_struct_pos(io_struct, request)); ++ if (new_bfqq) ++ return bfq_setup_merge(bfqq, new_bfqq); ++ ++ return NULL; ++} ++ ++static inline void ++bfq_bfqq_save_state(struct bfq_queue *bfqq) ++{ ++ /* ++ * If bfqq->bic == NULL, the queue is already shared or its requests ++ * have already been redirected to a shared queue; both idle window ++ * and weight raising state have already been saved. Do nothing. ++ */ ++ if (bfqq->bic == NULL) ++ return; ++ if (bfqq->bic->wr_time_left) ++ /* ++ * This is the queue of a just-started process, and would ++ * deserve weight raising: we set wr_time_left to the full ++ * weight-raising duration to trigger weight-raising when ++ * and if the queue is split and the first request of the ++ * queue is enqueued. ++ */ ++ bfqq->bic->wr_time_left = bfq_wr_duration(bfqq->bfqd); ++ else if (bfqq->wr_coeff > 1) { ++ unsigned long wr_duration = ++ jiffies - bfqq->last_wr_start_finish; ++ /* ++ * It may happen that a queue's weight raising period lasts ++ * longer than its wr_cur_max_time, as weight raising is ++ * handled only when a request is enqueued or dispatched (it ++ * does not use any timer). If the weight raising period is ++ * about to end, don't save it. ++ */ ++ if (bfqq->wr_cur_max_time <= wr_duration) ++ bfqq->bic->wr_time_left = 0; ++ else ++ bfqq->bic->wr_time_left = ++ bfqq->wr_cur_max_time - wr_duration; ++ /* ++ * The bfq_queue is becoming shared or the requests of the ++ * process owning the queue are being redirected to a shared ++ * queue. Stop the weight raising period of the queue, as in ++ * both cases it should not be owned by an interactive or ++ * soft real-time application. ++ */ ++ bfq_bfqq_end_wr(bfqq); ++ } else ++ bfqq->bic->wr_time_left = 0; ++ bfqq->bic->saved_idle_window = bfq_bfqq_idle_window(bfqq); ++ bfqq->bic->saved_IO_bound = bfq_bfqq_IO_bound(bfqq); ++ bfqq->bic->cooperations++; ++ bfqq->bic->failed_cooperations = 0; ++} ++ ++static inline void ++bfq_get_bic_reference(struct bfq_queue *bfqq) ++{ ++ /* ++ * If bfqq->bic has a non-NULL value, the bic to which it belongs ++ * is about to begin using a shared bfq_queue. ++ */ ++ if (bfqq->bic) ++ atomic_long_inc(&bfqq->bic->icq.ioc->refcount); ++} ++ ++static void ++bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic, ++ struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) ++{ ++ bfq_log_bfqq(bfqd, bfqq, "merging with queue %lu", ++ (long unsigned)new_bfqq->pid); ++ /* Save weight raising and idle window of the merged queues */ ++ bfq_bfqq_save_state(bfqq); ++ bfq_bfqq_save_state(new_bfqq); ++ if (bfq_bfqq_IO_bound(bfqq)) ++ bfq_mark_bfqq_IO_bound(new_bfqq); ++ bfq_clear_bfqq_IO_bound(bfqq); ++ /* ++ * Grab a reference to the bic, to prevent it from being destroyed ++ * before being possibly touched by a bfq_split_bfqq(). ++ */ ++ bfq_get_bic_reference(bfqq); ++ bfq_get_bic_reference(new_bfqq); ++ /* ++ * Merge queues (that is, let bic redirect its requests to new_bfqq) ++ */ ++ bic_set_bfqq(bic, new_bfqq, 1); ++ bfq_mark_bfqq_coop(new_bfqq); ++ /* ++ * new_bfqq now belongs to at least two bics (it is a shared queue): ++ * set new_bfqq->bic to NULL. bfqq either: ++ * - does not belong to any bic any more, and hence bfqq->bic must ++ * be set to NULL, or ++ * - is a queue whose owning bics have already been redirected to a ++ * different queue, hence the queue is destined to not belong to ++ * any bic soon and bfqq->bic is already NULL (therefore the next ++ * assignment causes no harm). ++ */ ++ new_bfqq->bic = NULL; ++ bfqq->bic = NULL; ++ bfq_put_queue(bfqq); ++} ++ ++static inline void bfq_bfqq_increase_failed_cooperations(struct bfq_queue *bfqq) ++{ ++ struct bfq_io_cq *bic = bfqq->bic; ++ struct bfq_data *bfqd = bfqq->bfqd; ++ ++ if (bic && bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh) { ++ bic->failed_cooperations++; ++ if (bic->failed_cooperations >= bfqd->bfq_failed_cooperations) ++ bic->cooperations = 0; ++ } ++} ++ ++static int bfq_allow_merge(struct request_queue *q, struct request *rq, ++ struct bio *bio) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ struct bfq_io_cq *bic; ++ struct bfq_queue *bfqq, *new_bfqq; ++ ++ /* ++ * Disallow merge of a sync bio into an async request. ++ */ ++ if (bfq_bio_sync(bio) && !rq_is_sync(rq)) ++ return 0; ++ ++ /* ++ * Lookup the bfqq that this bio will be queued with. Allow ++ * merge only if rq is queued there. ++ * Queue lock is held here. ++ */ ++ bic = bfq_bic_lookup(bfqd, current->io_context); ++ if (bic == NULL) ++ return 0; ++ ++ bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); ++ /* ++ * We take advantage of this function to perform an early merge ++ * of the queues of possible cooperating processes. ++ */ ++ if (bfqq != NULL) { ++ new_bfqq = bfq_setup_cooperator(bfqd, bfqq, bio, false); ++ if (new_bfqq != NULL) { ++ bfq_merge_bfqqs(bfqd, bic, bfqq, new_bfqq); ++ /* ++ * If we get here, the bio will be queued in the ++ * shared queue, i.e., new_bfqq, so use new_bfqq ++ * to decide whether bio and rq can be merged. ++ */ ++ bfqq = new_bfqq; ++ } else ++ bfq_bfqq_increase_failed_cooperations(bfqq); ++ } ++ ++ return bfqq == RQ_BFQQ(rq); ++} ++ ++static void __bfq_set_in_service_queue(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq) ++{ ++ if (bfqq != NULL) { ++ bfq_mark_bfqq_must_alloc(bfqq); ++ bfq_mark_bfqq_budget_new(bfqq); ++ bfq_clear_bfqq_fifo_expire(bfqq); ++ ++ bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8; ++ ++ bfq_log_bfqq(bfqd, bfqq, ++ "set_in_service_queue, cur-budget = %lu", ++ bfqq->entity.budget); ++ } ++ ++ bfqd->in_service_queue = bfqq; ++} ++ ++/* ++ * Get and set a new queue for service. ++ */ ++static struct bfq_queue *bfq_set_in_service_queue(struct bfq_data *bfqd) ++{ ++ struct bfq_queue *bfqq = bfq_get_next_queue(bfqd); ++ ++ __bfq_set_in_service_queue(bfqd, bfqq); ++ return bfqq; ++} ++ ++/* ++ * If enough samples have been computed, return the current max budget ++ * stored in bfqd, which is dynamically updated according to the ++ * estimated disk peak rate; otherwise return the default max budget ++ */ ++static inline unsigned long bfq_max_budget(struct bfq_data *bfqd) ++{ ++ if (bfqd->budgets_assigned < 194) ++ return bfq_default_max_budget; ++ else ++ return bfqd->bfq_max_budget; ++} ++ ++/* ++ * Return min budget, which is a fraction of the current or default ++ * max budget (trying with 1/32) ++ */ ++static inline unsigned long bfq_min_budget(struct bfq_data *bfqd) ++{ ++ if (bfqd->budgets_assigned < 194) ++ return bfq_default_max_budget / 32; ++ else ++ return bfqd->bfq_max_budget / 32; ++} ++ ++static void bfq_arm_slice_timer(struct bfq_data *bfqd) ++{ ++ struct bfq_queue *bfqq = bfqd->in_service_queue; ++ struct bfq_io_cq *bic; ++ unsigned long sl; ++ ++ BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); ++ ++ /* Processes have exited, don't wait. */ ++ bic = bfqd->in_service_bic; ++ if (bic == NULL || atomic_read(&bic->icq.ioc->active_ref) == 0) ++ return; ++ ++ bfq_mark_bfqq_wait_request(bfqq); ++ ++ /* ++ * We don't want to idle for seeks, but we do want to allow ++ * fair distribution of slice time for a process doing back-to-back ++ * seeks. So allow a little bit of time for him to submit a new rq. ++ * ++ * To prevent processes with (partly) seeky workloads from ++ * being too ill-treated, grant them a small fraction of the ++ * assigned budget before reducing the waiting time to ++ * BFQ_MIN_TT. This happened to help reduce latency. ++ */ ++ sl = bfqd->bfq_slice_idle; ++ /* ++ * Unless the queue is being weight-raised, grant only minimum idle ++ * time if the queue either has been seeky for long enough or has ++ * already proved to be constantly seeky. ++ */ ++ if (bfq_sample_valid(bfqq->seek_samples) && ++ ((BFQQ_SEEKY(bfqq) && bfqq->entity.service > ++ bfq_max_budget(bfqq->bfqd) / 8) || ++ bfq_bfqq_constantly_seeky(bfqq)) && bfqq->wr_coeff == 1) ++ sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); ++ else if (bfqq->wr_coeff > 1) ++ sl = sl * 3; ++ bfqd->last_idling_start = ktime_get(); ++ mod_timer(&bfqd->idle_slice_timer, jiffies + sl); ++ bfq_log(bfqd, "arm idle: %u/%u ms", ++ jiffies_to_msecs(sl), jiffies_to_msecs(bfqd->bfq_slice_idle)); ++} ++ ++/* ++ * Set the maximum time for the in-service queue to consume its ++ * budget. This prevents seeky processes from lowering the disk ++ * throughput (always guaranteed with a time slice scheme as in CFQ). ++ */ ++static void bfq_set_budget_timeout(struct bfq_data *bfqd) ++{ ++ struct bfq_queue *bfqq = bfqd->in_service_queue; ++ unsigned int timeout_coeff; ++ if (bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time) ++ timeout_coeff = 1; ++ else ++ timeout_coeff = bfqq->entity.weight / bfqq->entity.orig_weight; ++ ++ bfqd->last_budget_start = ktime_get(); ++ ++ bfq_clear_bfqq_budget_new(bfqq); ++ bfqq->budget_timeout = jiffies + ++ bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * timeout_coeff; ++ ++ bfq_log_bfqq(bfqd, bfqq, "set budget_timeout %u", ++ jiffies_to_msecs(bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * ++ timeout_coeff)); ++} ++ ++/* ++ * Move request from internal lists to the request queue dispatch list. ++ */ ++static void bfq_dispatch_insert(struct request_queue *q, struct request *rq) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ struct bfq_queue *bfqq = RQ_BFQQ(rq); ++ ++ /* ++ * For consistency, the next instruction should have been executed ++ * after removing the request from the queue and dispatching it. ++ * We execute instead this instruction before bfq_remove_request() ++ * (and hence introduce a temporary inconsistency), for efficiency. ++ * In fact, in a forced_dispatch, this prevents two counters related ++ * to bfqq->dispatched to risk to be uselessly decremented if bfqq ++ * is not in service, and then to be incremented again after ++ * incrementing bfqq->dispatched. ++ */ ++ bfqq->dispatched++; ++ bfq_remove_request(rq); ++ elv_dispatch_sort(q, rq); ++ ++ if (bfq_bfqq_sync(bfqq)) ++ bfqd->sync_flight++; ++} ++ ++/* ++ * Return expired entry, or NULL to just start from scratch in rbtree. ++ */ ++static struct request *bfq_check_fifo(struct bfq_queue *bfqq) ++{ ++ struct request *rq = NULL; ++ ++ if (bfq_bfqq_fifo_expire(bfqq)) ++ return NULL; ++ ++ bfq_mark_bfqq_fifo_expire(bfqq); ++ ++ if (list_empty(&bfqq->fifo)) ++ return NULL; ++ ++ rq = rq_entry_fifo(bfqq->fifo.next); ++ ++ if (time_before(jiffies, rq_fifo_time(rq))) ++ return NULL; ++ ++ return rq; ++} ++ ++static inline unsigned long bfq_bfqq_budget_left(struct bfq_queue *bfqq) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ return entity->budget - entity->service; ++} ++ ++static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq) ++{ ++ BUG_ON(bfqq != bfqd->in_service_queue); ++ ++ __bfq_bfqd_reset_in_service(bfqd); ++ ++ /* ++ * If this bfqq is shared between multiple processes, check ++ * to make sure that those processes are still issuing I/Os ++ * within the mean seek distance. If not, it may be time to ++ * break the queues apart again. ++ */ ++ if (bfq_bfqq_coop(bfqq) && BFQQ_SEEKY(bfqq)) ++ bfq_mark_bfqq_split_coop(bfqq); ++ ++ if (RB_EMPTY_ROOT(&bfqq->sort_list)) { ++ /* ++ * Overloading budget_timeout field to store the time ++ * at which the queue remains with no backlog; used by ++ * the weight-raising mechanism. ++ */ ++ bfqq->budget_timeout = jiffies; ++ bfq_del_bfqq_busy(bfqd, bfqq, 1); ++ } else { ++ bfq_activate_bfqq(bfqd, bfqq); ++ /* ++ * Resort priority tree of potential close cooperators. ++ */ ++ bfq_rq_pos_tree_add(bfqd, bfqq); ++ } ++} ++ ++/** ++ * __bfq_bfqq_recalc_budget - try to adapt the budget to the @bfqq behavior. ++ * @bfqd: device data. ++ * @bfqq: queue to update. ++ * @reason: reason for expiration. ++ * ++ * Handle the feedback on @bfqq budget. See the body for detailed ++ * comments. ++ */ ++static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ enum bfqq_expiration reason) ++{ ++ struct request *next_rq; ++ unsigned long budget, min_budget; ++ ++ budget = bfqq->max_budget; ++ min_budget = bfq_min_budget(bfqd); ++ ++ BUG_ON(bfqq != bfqd->in_service_queue); ++ ++ bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last budg %lu, budg left %lu", ++ bfqq->entity.budget, bfq_bfqq_budget_left(bfqq)); ++ bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last max_budg %lu, min budg %lu", ++ budget, bfq_min_budget(bfqd)); ++ bfq_log_bfqq(bfqd, bfqq, "recalc_budg: sync %d, seeky %d", ++ bfq_bfqq_sync(bfqq), BFQQ_SEEKY(bfqd->in_service_queue)); ++ ++ if (bfq_bfqq_sync(bfqq)) { ++ switch (reason) { ++ /* ++ * Caveat: in all the following cases we trade latency ++ * for throughput. ++ */ ++ case BFQ_BFQQ_TOO_IDLE: ++ /* ++ * This is the only case where we may reduce ++ * the budget: if there is no request of the ++ * process still waiting for completion, then ++ * we assume (tentatively) that the timer has ++ * expired because the batch of requests of ++ * the process could have been served with a ++ * smaller budget. Hence, betting that ++ * process will behave in the same way when it ++ * becomes backlogged again, we reduce its ++ * next budget. As long as we guess right, ++ * this budget cut reduces the latency ++ * experienced by the process. ++ * ++ * However, if there are still outstanding ++ * requests, then the process may have not yet ++ * issued its next request just because it is ++ * still waiting for the completion of some of ++ * the still outstanding ones. So in this ++ * subcase we do not reduce its budget, on the ++ * contrary we increase it to possibly boost ++ * the throughput, as discussed in the ++ * comments to the BUDGET_TIMEOUT case. ++ */ ++ if (bfqq->dispatched > 0) /* still outstanding reqs */ ++ budget = min(budget * 2, bfqd->bfq_max_budget); ++ else { ++ if (budget > 5 * min_budget) ++ budget -= 4 * min_budget; ++ else ++ budget = min_budget; ++ } ++ break; ++ case BFQ_BFQQ_BUDGET_TIMEOUT: ++ /* ++ * We double the budget here because: 1) it ++ * gives the chance to boost the throughput if ++ * this is not a seeky process (which may have ++ * bumped into this timeout because of, e.g., ++ * ZBR), 2) together with charge_full_budget ++ * it helps give seeky processes higher ++ * timestamps, and hence be served less ++ * frequently. ++ */ ++ budget = min(budget * 2, bfqd->bfq_max_budget); ++ break; ++ case BFQ_BFQQ_BUDGET_EXHAUSTED: ++ /* ++ * The process still has backlog, and did not ++ * let either the budget timeout or the disk ++ * idling timeout expire. Hence it is not ++ * seeky, has a short thinktime and may be ++ * happy with a higher budget too. So ++ * definitely increase the budget of this good ++ * candidate to boost the disk throughput. ++ */ ++ budget = min(budget * 4, bfqd->bfq_max_budget); ++ break; ++ case BFQ_BFQQ_NO_MORE_REQUESTS: ++ /* ++ * Leave the budget unchanged. ++ */ ++ default: ++ return; ++ } ++ } else /* async queue */ ++ /* async queues get always the maximum possible budget ++ * (their ability to dispatch is limited by ++ * @bfqd->bfq_max_budget_async_rq). ++ */ ++ budget = bfqd->bfq_max_budget; ++ ++ bfqq->max_budget = budget; ++ ++ if (bfqd->budgets_assigned >= 194 && bfqd->bfq_user_max_budget == 0 && ++ bfqq->max_budget > bfqd->bfq_max_budget) ++ bfqq->max_budget = bfqd->bfq_max_budget; ++ ++ /* ++ * Make sure that we have enough budget for the next request. ++ * Since the finish time of the bfqq must be kept in sync with ++ * the budget, be sure to call __bfq_bfqq_expire() after the ++ * update. ++ */ ++ next_rq = bfqq->next_rq; ++ if (next_rq != NULL) ++ bfqq->entity.budget = max_t(unsigned long, bfqq->max_budget, ++ bfq_serv_to_charge(next_rq, bfqq)); ++ else ++ bfqq->entity.budget = bfqq->max_budget; ++ ++ bfq_log_bfqq(bfqd, bfqq, "head sect: %u, new budget %lu", ++ next_rq != NULL ? blk_rq_sectors(next_rq) : 0, ++ bfqq->entity.budget); ++} ++ ++static unsigned long bfq_calc_max_budget(u64 peak_rate, u64 timeout) ++{ ++ unsigned long max_budget; ++ ++ /* ++ * The max_budget calculated when autotuning is equal to the ++ * amount of sectors transfered in timeout_sync at the ++ * estimated peak rate. ++ */ ++ max_budget = (unsigned long)(peak_rate * 1000 * ++ timeout >> BFQ_RATE_SHIFT); ++ ++ return max_budget; ++} ++ ++/* ++ * In addition to updating the peak rate, checks whether the process ++ * is "slow", and returns 1 if so. This slow flag is used, in addition ++ * to the budget timeout, to reduce the amount of service provided to ++ * seeky processes, and hence reduce their chances to lower the ++ * throughput. See the code for more details. ++ */ ++static int bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ int compensate, enum bfqq_expiration reason) ++{ ++ u64 bw, usecs, expected, timeout; ++ ktime_t delta; ++ int update = 0; ++ ++ if (!bfq_bfqq_sync(bfqq) || bfq_bfqq_budget_new(bfqq)) ++ return 0; ++ ++ if (compensate) ++ delta = bfqd->last_idling_start; ++ else ++ delta = ktime_get(); ++ delta = ktime_sub(delta, bfqd->last_budget_start); ++ usecs = ktime_to_us(delta); ++ ++ /* Don't trust short/unrealistic values. */ ++ if (usecs < 100 || usecs >= LONG_MAX) ++ return 0; ++ ++ /* ++ * Calculate the bandwidth for the last slice. We use a 64 bit ++ * value to store the peak rate, in sectors per usec in fixed ++ * point math. We do so to have enough precision in the estimate ++ * and to avoid overflows. ++ */ ++ bw = (u64)bfqq->entity.service << BFQ_RATE_SHIFT; ++ do_div(bw, (unsigned long)usecs); ++ ++ timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); ++ ++ /* ++ * Use only long (> 20ms) intervals to filter out spikes for ++ * the peak rate estimation. ++ */ ++ if (usecs > 20000) { ++ if (bw > bfqd->peak_rate || ++ (!BFQQ_SEEKY(bfqq) && ++ reason == BFQ_BFQQ_BUDGET_TIMEOUT)) { ++ bfq_log(bfqd, "measured bw =%llu", bw); ++ /* ++ * To smooth oscillations use a low-pass filter with ++ * alpha=7/8, i.e., ++ * new_rate = (7/8) * old_rate + (1/8) * bw ++ */ ++ do_div(bw, 8); ++ if (bw == 0) ++ return 0; ++ bfqd->peak_rate *= 7; ++ do_div(bfqd->peak_rate, 8); ++ bfqd->peak_rate += bw; ++ update = 1; ++ bfq_log(bfqd, "new peak_rate=%llu", bfqd->peak_rate); ++ } ++ ++ update |= bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES - 1; ++ ++ if (bfqd->peak_rate_samples < BFQ_PEAK_RATE_SAMPLES) ++ bfqd->peak_rate_samples++; ++ ++ if (bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES && ++ update) { ++ int dev_type = blk_queue_nonrot(bfqd->queue); ++ if (bfqd->bfq_user_max_budget == 0) { ++ bfqd->bfq_max_budget = ++ bfq_calc_max_budget(bfqd->peak_rate, ++ timeout); ++ bfq_log(bfqd, "new max_budget=%lu", ++ bfqd->bfq_max_budget); ++ } ++ if (bfqd->device_speed == BFQ_BFQD_FAST && ++ bfqd->peak_rate < device_speed_thresh[dev_type]) { ++ bfqd->device_speed = BFQ_BFQD_SLOW; ++ bfqd->RT_prod = R_slow[dev_type] * ++ T_slow[dev_type]; ++ } else if (bfqd->device_speed == BFQ_BFQD_SLOW && ++ bfqd->peak_rate > device_speed_thresh[dev_type]) { ++ bfqd->device_speed = BFQ_BFQD_FAST; ++ bfqd->RT_prod = R_fast[dev_type] * ++ T_fast[dev_type]; ++ } ++ } ++ } ++ ++ /* ++ * If the process has been served for a too short time ++ * interval to let its possible sequential accesses prevail on ++ * the initial seek time needed to move the disk head on the ++ * first sector it requested, then give the process a chance ++ * and for the moment return false. ++ */ ++ if (bfqq->entity.budget <= bfq_max_budget(bfqd) / 8) ++ return 0; ++ ++ /* ++ * A process is considered ``slow'' (i.e., seeky, so that we ++ * cannot treat it fairly in the service domain, as it would ++ * slow down too much the other processes) if, when a slice ++ * ends for whatever reason, it has received service at a ++ * rate that would not be high enough to complete the budget ++ * before the budget timeout expiration. ++ */ ++ expected = bw * 1000 * timeout >> BFQ_RATE_SHIFT; ++ ++ /* ++ * Caveat: processes doing IO in the slower disk zones will ++ * tend to be slow(er) even if not seeky. And the estimated ++ * peak rate will actually be an average over the disk ++ * surface. Hence, to not be too harsh with unlucky processes, ++ * we keep a budget/3 margin of safety before declaring a ++ * process slow. ++ */ ++ return expected > (4 * bfqq->entity.budget) / 3; ++} ++ ++/* ++ * To be deemed as soft real-time, an application must meet two ++ * requirements. First, the application must not require an average ++ * bandwidth higher than the approximate bandwidth required to playback or ++ * record a compressed high-definition video. ++ * The next function is invoked on the completion of the last request of a ++ * batch, to compute the next-start time instant, soft_rt_next_start, such ++ * that, if the next request of the application does not arrive before ++ * soft_rt_next_start, then the above requirement on the bandwidth is met. ++ * ++ * The second requirement is that the request pattern of the application is ++ * isochronous, i.e., that, after issuing a request or a batch of requests, ++ * the application stops issuing new requests until all its pending requests ++ * have been completed. After that, the application may issue a new batch, ++ * and so on. ++ * For this reason the next function is invoked to compute ++ * soft_rt_next_start only for applications that meet this requirement, ++ * whereas soft_rt_next_start is set to infinity for applications that do ++ * not. ++ * ++ * Unfortunately, even a greedy application may happen to behave in an ++ * isochronous way if the CPU load is high. In fact, the application may ++ * stop issuing requests while the CPUs are busy serving other processes, ++ * then restart, then stop again for a while, and so on. In addition, if ++ * the disk achieves a low enough throughput with the request pattern ++ * issued by the application (e.g., because the request pattern is random ++ * and/or the device is slow), then the application may meet the above ++ * bandwidth requirement too. To prevent such a greedy application to be ++ * deemed as soft real-time, a further rule is used in the computation of ++ * soft_rt_next_start: soft_rt_next_start must be higher than the current ++ * time plus the maximum time for which the arrival of a request is waited ++ * for when a sync queue becomes idle, namely bfqd->bfq_slice_idle. ++ * This filters out greedy applications, as the latter issue instead their ++ * next request as soon as possible after the last one has been completed ++ * (in contrast, when a batch of requests is completed, a soft real-time ++ * application spends some time processing data). ++ * ++ * Unfortunately, the last filter may easily generate false positives if ++ * only bfqd->bfq_slice_idle is used as a reference time interval and one ++ * or both the following cases occur: ++ * 1) HZ is so low that the duration of a jiffy is comparable to or higher ++ * than bfqd->bfq_slice_idle. This happens, e.g., on slow devices with ++ * HZ=100. ++ * 2) jiffies, instead of increasing at a constant rate, may stop increasing ++ * for a while, then suddenly 'jump' by several units to recover the lost ++ * increments. This seems to happen, e.g., inside virtual machines. ++ * To address this issue, we do not use as a reference time interval just ++ * bfqd->bfq_slice_idle, but bfqd->bfq_slice_idle plus a few jiffies. In ++ * particular we add the minimum number of jiffies for which the filter ++ * seems to be quite precise also in embedded systems and KVM/QEMU virtual ++ * machines. ++ */ ++static inline unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq) ++{ ++ return max(bfqq->last_idle_bklogged + ++ HZ * bfqq->service_from_backlogged / ++ bfqd->bfq_wr_max_softrt_rate, ++ jiffies + bfqq->bfqd->bfq_slice_idle + 4); ++} ++ ++/* ++ * Return the largest-possible time instant such that, for as long as possible, ++ * the current time will be lower than this time instant according to the macro ++ * time_is_before_jiffies(). ++ */ ++static inline unsigned long bfq_infinity_from_now(unsigned long now) ++{ ++ return now + ULONG_MAX / 2; ++} ++ ++/** ++ * bfq_bfqq_expire - expire a queue. ++ * @bfqd: device owning the queue. ++ * @bfqq: the queue to expire. ++ * @compensate: if true, compensate for the time spent idling. ++ * @reason: the reason causing the expiration. ++ * ++ * ++ * If the process associated to the queue is slow (i.e., seeky), or in ++ * case of budget timeout, or, finally, if it is async, we ++ * artificially charge it an entire budget (independently of the ++ * actual service it received). As a consequence, the queue will get ++ * higher timestamps than the correct ones upon reactivation, and ++ * hence it will be rescheduled as if it had received more service ++ * than what it actually received. In the end, this class of processes ++ * will receive less service in proportion to how slowly they consume ++ * their budgets (and hence how seriously they tend to lower the ++ * throughput). ++ * ++ * In contrast, when a queue expires because it has been idling for ++ * too much or because it exhausted its budget, we do not touch the ++ * amount of service it has received. Hence when the queue will be ++ * reactivated and its timestamps updated, the latter will be in sync ++ * with the actual service received by the queue until expiration. ++ * ++ * Charging a full budget to the first type of queues and the exact ++ * service to the others has the effect of using the WF2Q+ policy to ++ * schedule the former on a timeslice basis, without violating the ++ * service domain guarantees of the latter. ++ */ ++static void bfq_bfqq_expire(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ int compensate, ++ enum bfqq_expiration reason) ++{ ++ int slow; ++ BUG_ON(bfqq != bfqd->in_service_queue); ++ ++ /* Update disk peak rate for autotuning and check whether the ++ * process is slow (see bfq_update_peak_rate). ++ */ ++ slow = bfq_update_peak_rate(bfqd, bfqq, compensate, reason); ++ ++ /* ++ * As above explained, 'punish' slow (i.e., seeky), timed-out ++ * and async queues, to favor sequential sync workloads. ++ * ++ * Processes doing I/O in the slower disk zones will tend to be ++ * slow(er) even if not seeky. Hence, since the estimated peak ++ * rate is actually an average over the disk surface, these ++ * processes may timeout just for bad luck. To avoid punishing ++ * them we do not charge a full budget to a process that ++ * succeeded in consuming at least 2/3 of its budget. ++ */ ++ if (slow || (reason == BFQ_BFQQ_BUDGET_TIMEOUT && ++ bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3)) ++ bfq_bfqq_charge_full_budget(bfqq); ++ ++ bfqq->service_from_backlogged += bfqq->entity.service; ++ ++ if (BFQQ_SEEKY(bfqq) && reason == BFQ_BFQQ_BUDGET_TIMEOUT && ++ !bfq_bfqq_constantly_seeky(bfqq)) { ++ bfq_mark_bfqq_constantly_seeky(bfqq); ++ if (!blk_queue_nonrot(bfqd->queue)) ++ bfqd->const_seeky_busy_in_flight_queues++; ++ } ++ ++ if (reason == BFQ_BFQQ_TOO_IDLE && ++ bfqq->entity.service <= 2 * bfqq->entity.budget / 10 ) ++ bfq_clear_bfqq_IO_bound(bfqq); ++ ++ if (bfqd->low_latency && bfqq->wr_coeff == 1) ++ bfqq->last_wr_start_finish = jiffies; ++ ++ if (bfqd->low_latency && bfqd->bfq_wr_max_softrt_rate > 0 && ++ RB_EMPTY_ROOT(&bfqq->sort_list)) { ++ /* ++ * If we get here, and there are no outstanding requests, ++ * then the request pattern is isochronous (see the comments ++ * to the function bfq_bfqq_softrt_next_start()). Hence we ++ * can compute soft_rt_next_start. If, instead, the queue ++ * still has outstanding requests, then we have to wait ++ * for the completion of all the outstanding requests to ++ * discover whether the request pattern is actually ++ * isochronous. ++ */ ++ if (bfqq->dispatched == 0) ++ bfqq->soft_rt_next_start = ++ bfq_bfqq_softrt_next_start(bfqd, bfqq); ++ else { ++ /* ++ * The application is still waiting for the ++ * completion of one or more requests: ++ * prevent it from possibly being incorrectly ++ * deemed as soft real-time by setting its ++ * soft_rt_next_start to infinity. In fact, ++ * without this assignment, the application ++ * would be incorrectly deemed as soft ++ * real-time if: ++ * 1) it issued a new request before the ++ * completion of all its in-flight ++ * requests, and ++ * 2) at that time, its soft_rt_next_start ++ * happened to be in the past. ++ */ ++ bfqq->soft_rt_next_start = ++ bfq_infinity_from_now(jiffies); ++ /* ++ * Schedule an update of soft_rt_next_start to when ++ * the task may be discovered to be isochronous. ++ */ ++ bfq_mark_bfqq_softrt_update(bfqq); ++ } ++ } ++ ++ bfq_log_bfqq(bfqd, bfqq, ++ "expire (%d, slow %d, num_disp %d, idle_win %d)", reason, ++ slow, bfqq->dispatched, bfq_bfqq_idle_window(bfqq)); ++ ++ /* ++ * Increase, decrease or leave budget unchanged according to ++ * reason. ++ */ ++ __bfq_bfqq_recalc_budget(bfqd, bfqq, reason); ++ __bfq_bfqq_expire(bfqd, bfqq); ++} ++ ++/* ++ * Budget timeout is not implemented through a dedicated timer, but ++ * just checked on request arrivals and completions, as well as on ++ * idle timer expirations. ++ */ ++static int bfq_bfqq_budget_timeout(struct bfq_queue *bfqq) ++{ ++ if (bfq_bfqq_budget_new(bfqq) || ++ time_before(jiffies, bfqq->budget_timeout)) ++ return 0; ++ return 1; ++} ++ ++/* ++ * If we expire a queue that is waiting for the arrival of a new ++ * request, we may prevent the fictitious timestamp back-shifting that ++ * allows the guarantees of the queue to be preserved (see [1] for ++ * this tricky aspect). Hence we return true only if this condition ++ * does not hold, or if the queue is slow enough to deserve only to be ++ * kicked off for preserving a high throughput. ++*/ ++static inline int bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) ++{ ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "may_budget_timeout: wait_request %d left %d timeout %d", ++ bfq_bfqq_wait_request(bfqq), ++ bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3, ++ bfq_bfqq_budget_timeout(bfqq)); ++ ++ return (!bfq_bfqq_wait_request(bfqq) || ++ bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3) ++ && ++ bfq_bfqq_budget_timeout(bfqq); ++} ++ ++/* ++ * Device idling is allowed only for the queues for which this function ++ * returns true. For this reason, the return value of this function plays a ++ * critical role for both throughput boosting and service guarantees. The ++ * return value is computed through a logical expression. In this rather ++ * long comment, we try to briefly describe all the details and motivations ++ * behind the components of this logical expression. ++ * ++ * First, the expression may be true only for sync queues. Besides, if ++ * bfqq is also being weight-raised, then the expression always evaluates ++ * to true, as device idling is instrumental for preserving low-latency ++ * guarantees (see [1]). Otherwise, the expression evaluates to true only ++ * if bfqq has a non-null idle window and at least one of the following ++ * two conditions holds. The first condition is that the device is not ++ * performing NCQ, because idling the device most certainly boosts the ++ * throughput if this condition holds and bfqq has been granted a non-null ++ * idle window. The second compound condition is made of the logical AND of ++ * two components. ++ * ++ * The first component is true only if there is no weight-raised busy ++ * queue. This guarantees that the device is not idled for a sync non- ++ * weight-raised queue when there are busy weight-raised queues. The former ++ * is then expired immediately if empty. Combined with the timestamping ++ * rules of BFQ (see [1] for details), this causes sync non-weight-raised ++ * queues to get a lower number of requests served, and hence to ask for a ++ * lower number of requests from the request pool, before the busy weight- ++ * raised queues get served again. ++ * ++ * This is beneficial for the processes associated with weight-raised ++ * queues, when the request pool is saturated (e.g., in the presence of ++ * write hogs). In fact, if the processes associated with the other queues ++ * ask for requests at a lower rate, then weight-raised processes have a ++ * higher probability to get a request from the pool immediately (or at ++ * least soon) when they need one. Hence they have a higher probability to ++ * actually get a fraction of the disk throughput proportional to their ++ * high weight. This is especially true with NCQ-capable drives, which ++ * enqueue several requests in advance and further reorder internally- ++ * queued requests. ++ * ++ * In the end, mistreating non-weight-raised queues when there are busy ++ * weight-raised queues seems to mitigate starvation problems in the ++ * presence of heavy write workloads and NCQ, and hence to guarantee a ++ * higher application and system responsiveness in these hostile scenarios. ++ * ++ * If the first component of the compound condition is instead true, i.e., ++ * there is no weight-raised busy queue, then the second component of the ++ * compound condition takes into account service-guarantee and throughput ++ * issues related to NCQ (recall that the compound condition is evaluated ++ * only if the device is detected as supporting NCQ). ++ * ++ * As for service guarantees, allowing the drive to enqueue more than one ++ * request at a time, and hence delegating de facto final scheduling ++ * decisions to the drive's internal scheduler, causes loss of control on ++ * the actual request service order. In this respect, when the drive is ++ * allowed to enqueue more than one request at a time, the service ++ * distribution enforced by the drive's internal scheduler is likely to ++ * coincide with the desired device-throughput distribution only in the ++ * following, perfectly symmetric, scenario: ++ * 1) all active queues have the same weight, ++ * 2) all active groups at the same level in the groups tree have the same ++ * weight, ++ * 3) all active groups at the same level in the groups tree have the same ++ * number of children. ++ * ++ * Even in such a scenario, sequential I/O may still receive a preferential ++ * treatment, but this is not likely to be a big issue with flash-based ++ * devices, because of their non-dramatic loss of throughput with random ++ * I/O. Things do differ with HDDs, for which additional care is taken, as ++ * explained after completing the discussion for flash-based devices. ++ * ++ * Unfortunately, keeping the necessary state for evaluating exactly the ++ * above symmetry conditions would be quite complex and time-consuming. ++ * Therefore BFQ evaluates instead the following stronger sub-conditions, ++ * for which it is much easier to maintain the needed state: ++ * 1) all active queues have the same weight, ++ * 2) all active groups have the same weight, ++ * 3) all active groups have at most one active child each. ++ * In particular, the last two conditions are always true if hierarchical ++ * support and the cgroups interface are not enabled, hence no state needs ++ * to be maintained in this case. ++ * ++ * According to the above considerations, the second component of the ++ * compound condition evaluates to true if any of the above symmetry ++ * sub-condition does not hold, or the device is not flash-based. Therefore, ++ * if also the first component is true, then idling is allowed for a sync ++ * queue. These are the only sub-conditions considered if the device is ++ * flash-based, as, for such a device, it is sensible to force idling only ++ * for service-guarantee issues. In fact, as for throughput, idling ++ * NCQ-capable flash-based devices would not boost the throughput even ++ * with sequential I/O; rather it would lower the throughput in proportion ++ * to how fast the device is. In the end, (only) if all the three ++ * sub-conditions hold and the device is flash-based, the compound ++ * condition evaluates to false and therefore no idling is performed. ++ * ++ * As already said, things change with a rotational device, where idling ++ * boosts the throughput with sequential I/O (even with NCQ). Hence, for ++ * such a device the second component of the compound condition evaluates ++ * to true also if the following additional sub-condition does not hold: ++ * the queue is constantly seeky. Unfortunately, this different behavior ++ * with respect to flash-based devices causes an additional asymmetry: if ++ * some sync queues enjoy idling and some other sync queues do not, then ++ * the latter get a low share of the device throughput, simply because the ++ * former get many requests served after being set as in service, whereas ++ * the latter do not. As a consequence, to guarantee the desired throughput ++ * distribution, on HDDs the compound expression evaluates to true (and ++ * hence device idling is performed) also if the following last symmetry ++ * condition does not hold: no other queue is benefiting from idling. Also ++ * this last condition is actually replaced with a simpler-to-maintain and ++ * stronger condition: there is no busy queue which is not constantly seeky ++ * (and hence may also benefit from idling). ++ * ++ * To sum up, when all the required symmetry and throughput-boosting ++ * sub-conditions hold, the second component of the compound condition ++ * evaluates to false, and hence no idling is performed. This helps to ++ * keep the drives' internal queues full on NCQ-capable devices, and hence ++ * to boost the throughput, without causing 'almost' any loss of service ++ * guarantees. The 'almost' follows from the fact that, if the internal ++ * queue of one such device is filled while all the sub-conditions hold, ++ * but at some point in time some sub-condition stops to hold, then it may ++ * become impossible to let requests be served in the new desired order ++ * until all the requests already queued in the device have been served. ++ */ ++static inline bool bfq_bfqq_must_not_expire(struct bfq_queue *bfqq) ++{ ++ struct bfq_data *bfqd = bfqq->bfqd; ++#ifdef CONFIG_CGROUP_BFQIO ++#define symmetric_scenario (!bfqd->active_numerous_groups && \ ++ !bfq_differentiated_weights(bfqd)) ++#else ++#define symmetric_scenario (!bfq_differentiated_weights(bfqd)) ++#endif ++#define cond_for_seeky_on_ncq_hdd (bfq_bfqq_constantly_seeky(bfqq) && \ ++ bfqd->busy_in_flight_queues == \ ++ bfqd->const_seeky_busy_in_flight_queues) ++/* ++ * Condition for expiring a non-weight-raised queue (and hence not idling ++ * the device). ++ */ ++#define cond_for_expiring_non_wr (bfqd->hw_tag && \ ++ (bfqd->wr_busy_queues > 0 || \ ++ (symmetric_scenario && \ ++ (blk_queue_nonrot(bfqd->queue) || \ ++ cond_for_seeky_on_ncq_hdd)))) ++ ++ return bfq_bfqq_sync(bfqq) && ++ (bfq_bfqq_IO_bound(bfqq) || bfqq->wr_coeff > 1) && ++ (bfqq->wr_coeff > 1 || ++ (bfq_bfqq_idle_window(bfqq) && ++ !cond_for_expiring_non_wr) ++ ); ++} ++ ++/* ++ * If the in-service queue is empty but sync, and the function ++ * bfq_bfqq_must_not_expire returns true, then: ++ * 1) the queue must remain in service and cannot be expired, and ++ * 2) the disk must be idled to wait for the possible arrival of a new ++ * request for the queue. ++ * See the comments to the function bfq_bfqq_must_not_expire for the reasons ++ * why performing device idling is the best choice to boost the throughput ++ * and preserve service guarantees when bfq_bfqq_must_not_expire itself ++ * returns true. ++ */ ++static inline bool bfq_bfqq_must_idle(struct bfq_queue *bfqq) ++{ ++ struct bfq_data *bfqd = bfqq->bfqd; ++ ++ return RB_EMPTY_ROOT(&bfqq->sort_list) && bfqd->bfq_slice_idle != 0 && ++ bfq_bfqq_must_not_expire(bfqq); ++} ++ ++/* ++ * Select a queue for service. If we have a current queue in service, ++ * check whether to continue servicing it, or retrieve and set a new one. ++ */ ++static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) ++{ ++ struct bfq_queue *bfqq; ++ struct request *next_rq; ++ enum bfqq_expiration reason = BFQ_BFQQ_BUDGET_TIMEOUT; ++ ++ bfqq = bfqd->in_service_queue; ++ if (bfqq == NULL) ++ goto new_queue; ++ ++ bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue"); ++ ++ if (bfq_may_expire_for_budg_timeout(bfqq) && ++ !timer_pending(&bfqd->idle_slice_timer) && ++ !bfq_bfqq_must_idle(bfqq)) ++ goto expire; ++ ++ next_rq = bfqq->next_rq; ++ /* ++ * If bfqq has requests queued and it has enough budget left to ++ * serve them, keep the queue, otherwise expire it. ++ */ ++ if (next_rq != NULL) { ++ if (bfq_serv_to_charge(next_rq, bfqq) > ++ bfq_bfqq_budget_left(bfqq)) { ++ reason = BFQ_BFQQ_BUDGET_EXHAUSTED; ++ goto expire; ++ } else { ++ /* ++ * The idle timer may be pending because we may ++ * not disable disk idling even when a new request ++ * arrives. ++ */ ++ if (timer_pending(&bfqd->idle_slice_timer)) { ++ /* ++ * If we get here: 1) at least a new request ++ * has arrived but we have not disabled the ++ * timer because the request was too small, ++ * 2) then the block layer has unplugged ++ * the device, causing the dispatch to be ++ * invoked. ++ * ++ * Since the device is unplugged, now the ++ * requests are probably large enough to ++ * provide a reasonable throughput. ++ * So we disable idling. ++ */ ++ bfq_clear_bfqq_wait_request(bfqq); ++ del_timer(&bfqd->idle_slice_timer); ++ } ++ goto keep_queue; ++ } ++ } ++ ++ /* ++ * No requests pending. If the in-service queue still has requests ++ * in flight (possibly waiting for a completion) or is idling for a ++ * new request, then keep it. ++ */ ++ if (timer_pending(&bfqd->idle_slice_timer) || ++ (bfqq->dispatched != 0 && bfq_bfqq_must_not_expire(bfqq))) { ++ bfqq = NULL; ++ goto keep_queue; ++ } ++ ++ reason = BFQ_BFQQ_NO_MORE_REQUESTS; ++expire: ++ bfq_bfqq_expire(bfqd, bfqq, 0, reason); ++new_queue: ++ bfqq = bfq_set_in_service_queue(bfqd); ++ bfq_log(bfqd, "select_queue: new queue %d returned", ++ bfqq != NULL ? bfqq->pid : 0); ++keep_queue: ++ return bfqq; ++} ++ ++static void bfq_update_wr_data(struct bfq_data *bfqd, struct bfq_queue *bfqq) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ if (bfqq->wr_coeff > 1) { /* queue is being weight-raised */ ++ bfq_log_bfqq(bfqd, bfqq, ++ "raising period dur %u/%u msec, old coeff %u, w %d(%d)", ++ jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish), ++ jiffies_to_msecs(bfqq->wr_cur_max_time), ++ bfqq->wr_coeff, ++ bfqq->entity.weight, bfqq->entity.orig_weight); ++ ++ BUG_ON(bfqq != bfqd->in_service_queue && entity->weight != ++ entity->orig_weight * bfqq->wr_coeff); ++ if (entity->ioprio_changed) ++ bfq_log_bfqq(bfqd, bfqq, "WARN: pending prio change"); ++ ++ /* ++ * If too much time has elapsed from the beginning ++ * of this weight-raising period, or the queue has ++ * exceeded the acceptable number of cooperations, ++ * stop it. ++ */ ++ if (bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh || ++ time_is_before_jiffies(bfqq->last_wr_start_finish + ++ bfqq->wr_cur_max_time)) { ++ bfqq->last_wr_start_finish = jiffies; ++ bfq_log_bfqq(bfqd, bfqq, ++ "wrais ending at %lu, rais_max_time %u", ++ bfqq->last_wr_start_finish, ++ jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ bfq_bfqq_end_wr(bfqq); ++ } ++ } ++ /* Update weight both if it must be raised and if it must be lowered */ ++ if ((entity->weight > entity->orig_weight) != (bfqq->wr_coeff > 1)) ++ __bfq_entity_update_weight_prio( ++ bfq_entity_service_tree(entity), ++ entity); ++} ++ ++/* ++ * Dispatch one request from bfqq, moving it to the request queue ++ * dispatch list. ++ */ ++static int bfq_dispatch_request(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq) ++{ ++ int dispatched = 0; ++ struct request *rq; ++ unsigned long service_to_charge; ++ ++ BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); ++ ++ /* Follow expired path, else get first next available. */ ++ rq = bfq_check_fifo(bfqq); ++ if (rq == NULL) ++ rq = bfqq->next_rq; ++ service_to_charge = bfq_serv_to_charge(rq, bfqq); ++ ++ if (service_to_charge > bfq_bfqq_budget_left(bfqq)) { ++ /* ++ * This may happen if the next rq is chosen in fifo order ++ * instead of sector order. The budget is properly ++ * dimensioned to be always sufficient to serve the next ++ * request only if it is chosen in sector order. The reason ++ * is that it would be quite inefficient and little useful ++ * to always make sure that the budget is large enough to ++ * serve even the possible next rq in fifo order. ++ * In fact, requests are seldom served in fifo order. ++ * ++ * Expire the queue for budget exhaustion, and make sure ++ * that the next act_budget is enough to serve the next ++ * request, even if it comes from the fifo expired path. ++ */ ++ bfqq->next_rq = rq; ++ /* ++ * Since this dispatch is failed, make sure that ++ * a new one will be performed ++ */ ++ if (!bfqd->rq_in_driver) ++ bfq_schedule_dispatch(bfqd); ++ goto expire; ++ } ++ ++ /* Finally, insert request into driver dispatch list. */ ++ bfq_bfqq_served(bfqq, service_to_charge); ++ bfq_dispatch_insert(bfqd->queue, rq); ++ ++ bfq_update_wr_data(bfqd, bfqq); ++ ++ bfq_log_bfqq(bfqd, bfqq, ++ "dispatched %u sec req (%llu), budg left %lu", ++ blk_rq_sectors(rq), ++ (long long unsigned)blk_rq_pos(rq), ++ bfq_bfqq_budget_left(bfqq)); ++ ++ dispatched++; ++ ++ if (bfqd->in_service_bic == NULL) { ++ atomic_long_inc(&RQ_BIC(rq)->icq.ioc->refcount); ++ bfqd->in_service_bic = RQ_BIC(rq); ++ } ++ ++ if (bfqd->busy_queues > 1 && ((!bfq_bfqq_sync(bfqq) && ++ dispatched >= bfqd->bfq_max_budget_async_rq) || ++ bfq_class_idle(bfqq))) ++ goto expire; ++ ++ return dispatched; ++ ++expire: ++ bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_EXHAUSTED); ++ return dispatched; ++} ++ ++static int __bfq_forced_dispatch_bfqq(struct bfq_queue *bfqq) ++{ ++ int dispatched = 0; ++ ++ while (bfqq->next_rq != NULL) { ++ bfq_dispatch_insert(bfqq->bfqd->queue, bfqq->next_rq); ++ dispatched++; ++ } ++ ++ BUG_ON(!list_empty(&bfqq->fifo)); ++ return dispatched; ++} ++ ++/* ++ * Drain our current requests. ++ * Used for barriers and when switching io schedulers on-the-fly. ++ */ ++static int bfq_forced_dispatch(struct bfq_data *bfqd) ++{ ++ struct bfq_queue *bfqq, *n; ++ struct bfq_service_tree *st; ++ int dispatched = 0; ++ ++ bfqq = bfqd->in_service_queue; ++ if (bfqq != NULL) ++ __bfq_bfqq_expire(bfqd, bfqq); ++ ++ /* ++ * Loop through classes, and be careful to leave the scheduler ++ * in a consistent state, as feedback mechanisms and vtime ++ * updates cannot be disabled during the process. ++ */ ++ list_for_each_entry_safe(bfqq, n, &bfqd->active_list, bfqq_list) { ++ st = bfq_entity_service_tree(&bfqq->entity); ++ ++ dispatched += __bfq_forced_dispatch_bfqq(bfqq); ++ bfqq->max_budget = bfq_max_budget(bfqd); ++ ++ bfq_forget_idle(st); ++ } ++ ++ BUG_ON(bfqd->busy_queues != 0); ++ ++ return dispatched; ++} ++ ++static int bfq_dispatch_requests(struct request_queue *q, int force) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ struct bfq_queue *bfqq; ++ int max_dispatch; ++ ++ bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues); ++ if (bfqd->busy_queues == 0) ++ return 0; ++ ++ if (unlikely(force)) ++ return bfq_forced_dispatch(bfqd); ++ ++ bfqq = bfq_select_queue(bfqd); ++ if (bfqq == NULL) ++ return 0; ++ ++ max_dispatch = bfqd->bfq_quantum; ++ if (bfq_class_idle(bfqq)) ++ max_dispatch = 1; ++ ++ if (!bfq_bfqq_sync(bfqq)) ++ max_dispatch = bfqd->bfq_max_budget_async_rq; ++ ++ if (bfqq->dispatched >= max_dispatch) { ++ if (bfqd->busy_queues > 1) ++ return 0; ++ if (bfqq->dispatched >= 4 * max_dispatch) ++ return 0; ++ } ++ ++ if (bfqd->sync_flight != 0 && !bfq_bfqq_sync(bfqq)) ++ return 0; ++ ++ bfq_clear_bfqq_wait_request(bfqq); ++ BUG_ON(timer_pending(&bfqd->idle_slice_timer)); ++ ++ if (!bfq_dispatch_request(bfqd, bfqq)) ++ return 0; ++ ++ bfq_log_bfqq(bfqd, bfqq, "dispatched one request of %d (max_disp %d)", ++ bfqq->pid, max_dispatch); ++ ++ return 1; ++} ++ ++/* ++ * Task holds one reference to the queue, dropped when task exits. Each rq ++ * in-flight on this queue also holds a reference, dropped when rq is freed. ++ * ++ * Queue lock must be held here. ++ */ ++static void bfq_put_queue(struct bfq_queue *bfqq) ++{ ++ struct bfq_data *bfqd = bfqq->bfqd; ++ ++ BUG_ON(atomic_read(&bfqq->ref) <= 0); ++ ++ bfq_log_bfqq(bfqd, bfqq, "put_queue: %p %d", bfqq, ++ atomic_read(&bfqq->ref)); ++ if (!atomic_dec_and_test(&bfqq->ref)) ++ return; ++ ++ BUG_ON(rb_first(&bfqq->sort_list) != NULL); ++ BUG_ON(bfqq->allocated[READ] + bfqq->allocated[WRITE] != 0); ++ BUG_ON(bfqq->entity.tree != NULL); ++ BUG_ON(bfq_bfqq_busy(bfqq)); ++ BUG_ON(bfqd->in_service_queue == bfqq); ++ ++ bfq_log_bfqq(bfqd, bfqq, "put_queue: %p freed", bfqq); ++ ++ kmem_cache_free(bfq_pool, bfqq); ++} ++ ++static void bfq_put_cooperator(struct bfq_queue *bfqq) ++{ ++ struct bfq_queue *__bfqq, *next; ++ ++ /* ++ * If this queue was scheduled to merge with another queue, be ++ * sure to drop the reference taken on that queue (and others in ++ * the merge chain). See bfq_setup_merge and bfq_merge_bfqqs. ++ */ ++ __bfqq = bfqq->new_bfqq; ++ while (__bfqq) { ++ if (__bfqq == bfqq) ++ break; ++ next = __bfqq->new_bfqq; ++ bfq_put_queue(__bfqq); ++ __bfqq = next; ++ } ++} ++ ++static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) ++{ ++ if (bfqq == bfqd->in_service_queue) { ++ __bfq_bfqq_expire(bfqd, bfqq); ++ bfq_schedule_dispatch(bfqd); ++ } ++ ++ bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, ++ atomic_read(&bfqq->ref)); ++ ++ bfq_put_cooperator(bfqq); ++ ++ bfq_put_queue(bfqq); ++} ++ ++static inline void bfq_init_icq(struct io_cq *icq) ++{ ++ struct bfq_io_cq *bic = icq_to_bic(icq); ++ ++ bic->ttime.last_end_request = jiffies; ++ /* ++ * A newly created bic indicates that the process has just ++ * started doing I/O, and is probably mapping into memory its ++ * executable and libraries: it definitely needs weight raising. ++ * There is however the possibility that the process performs, ++ * for a while, I/O close to some other process. EQM intercepts ++ * this behavior and may merge the queue corresponding to the ++ * process with some other queue, BEFORE the weight of the queue ++ * is raised. Merged queues are not weight-raised (they are assumed ++ * to belong to processes that benefit only from high throughput). ++ * If the merge is basically the consequence of an accident, then ++ * the queue will be split soon and will get back its old weight. ++ * It is then important to write down somewhere that this queue ++ * does need weight raising, even if it did not make it to get its ++ * weight raised before being merged. To this purpose, we overload ++ * the field raising_time_left and assign 1 to it, to mark the queue ++ * as needing weight raising. ++ */ ++ bic->wr_time_left = 1; ++} ++ ++static void bfq_exit_icq(struct io_cq *icq) ++{ ++ struct bfq_io_cq *bic = icq_to_bic(icq); ++ struct bfq_data *bfqd = bic_to_bfqd(bic); ++ ++ if (bic->bfqq[BLK_RW_ASYNC]) { ++ bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_ASYNC]); ++ bic->bfqq[BLK_RW_ASYNC] = NULL; ++ } ++ ++ if (bic->bfqq[BLK_RW_SYNC]) { ++ /* ++ * If the bic is using a shared queue, put the reference ++ * taken on the io_context when the bic started using a ++ * shared bfq_queue. ++ */ ++ if (bfq_bfqq_coop(bic->bfqq[BLK_RW_SYNC])) ++ put_io_context(icq->ioc); ++ bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_SYNC]); ++ bic->bfqq[BLK_RW_SYNC] = NULL; ++ } ++} ++ ++/* ++ * Update the entity prio values; note that the new values will not ++ * be used until the next (re)activation. ++ */ ++static void bfq_init_prio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) ++{ ++ struct task_struct *tsk = current; ++ int ioprio_class; ++ ++ if (!bfq_bfqq_prio_changed(bfqq)) ++ return; ++ ++ ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio); ++ switch (ioprio_class) { ++ default: ++ dev_err(bfqq->bfqd->queue->backing_dev_info.dev, ++ "bfq: bad prio %x\n", ioprio_class); ++ case IOPRIO_CLASS_NONE: ++ /* ++ * No prio set, inherit CPU scheduling settings. ++ */ ++ bfqq->entity.new_ioprio = task_nice_ioprio(tsk); ++ bfqq->entity.new_ioprio_class = task_nice_ioclass(tsk); ++ break; ++ case IOPRIO_CLASS_RT: ++ bfqq->entity.new_ioprio = IOPRIO_PRIO_DATA(bic->ioprio); ++ bfqq->entity.new_ioprio_class = IOPRIO_CLASS_RT; ++ break; ++ case IOPRIO_CLASS_BE: ++ bfqq->entity.new_ioprio = IOPRIO_PRIO_DATA(bic->ioprio); ++ bfqq->entity.new_ioprio_class = IOPRIO_CLASS_BE; ++ break; ++ case IOPRIO_CLASS_IDLE: ++ bfqq->entity.new_ioprio_class = IOPRIO_CLASS_IDLE; ++ bfqq->entity.new_ioprio = 7; ++ bfq_clear_bfqq_idle_window(bfqq); ++ break; ++ } ++ ++ bfqq->entity.ioprio_changed = 1; ++ ++ bfq_clear_bfqq_prio_changed(bfqq); ++} ++ ++static void bfq_changed_ioprio(struct bfq_io_cq *bic) ++{ ++ struct bfq_data *bfqd; ++ struct bfq_queue *bfqq, *new_bfqq; ++ struct bfq_group *bfqg; ++ unsigned long uninitialized_var(flags); ++ int ioprio = bic->icq.ioc->ioprio; ++ ++ bfqd = bfq_get_bfqd_locked(&(bic->icq.q->elevator->elevator_data), ++ &flags); ++ /* ++ * This condition may trigger on a newly created bic, be sure to ++ * drop the lock before returning. ++ */ ++ if (unlikely(bfqd == NULL) || likely(bic->ioprio == ioprio)) ++ goto out; ++ ++ bfqq = bic->bfqq[BLK_RW_ASYNC]; ++ if (bfqq != NULL) { ++ bfqg = container_of(bfqq->entity.sched_data, struct bfq_group, ++ sched_data); ++ new_bfqq = bfq_get_queue(bfqd, bfqg, BLK_RW_ASYNC, bic, ++ GFP_ATOMIC); ++ if (new_bfqq != NULL) { ++ bic->bfqq[BLK_RW_ASYNC] = new_bfqq; ++ bfq_log_bfqq(bfqd, bfqq, ++ "changed_ioprio: bfqq %p %d", ++ bfqq, atomic_read(&bfqq->ref)); ++ bfq_put_queue(bfqq); ++ } ++ } ++ ++ bfqq = bic->bfqq[BLK_RW_SYNC]; ++ if (bfqq != NULL) ++ bfq_mark_bfqq_prio_changed(bfqq); ++ ++ bic->ioprio = ioprio; ++ ++out: ++ bfq_put_bfqd_unlock(bfqd, &flags); ++} ++ ++static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ pid_t pid, int is_sync) ++{ ++ RB_CLEAR_NODE(&bfqq->entity.rb_node); ++ INIT_LIST_HEAD(&bfqq->fifo); ++ ++ atomic_set(&bfqq->ref, 0); ++ bfqq->bfqd = bfqd; ++ ++ bfq_mark_bfqq_prio_changed(bfqq); ++ ++ if (is_sync) { ++ if (!bfq_class_idle(bfqq)) ++ bfq_mark_bfqq_idle_window(bfqq); ++ bfq_mark_bfqq_sync(bfqq); ++ } ++ bfq_mark_bfqq_IO_bound(bfqq); ++ ++ /* Tentative initial value to trade off between thr and lat */ ++ bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3; ++ bfqq->pid = pid; ++ ++ bfqq->wr_coeff = 1; ++ bfqq->last_wr_start_finish = 0; ++ /* ++ * Set to the value for which bfqq will not be deemed as ++ * soft rt when it becomes backlogged. ++ */ ++ bfqq->soft_rt_next_start = bfq_infinity_from_now(jiffies); ++} ++ ++static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, ++ struct bfq_group *bfqg, ++ int is_sync, ++ struct bfq_io_cq *bic, ++ gfp_t gfp_mask) ++{ ++ struct bfq_queue *bfqq, *new_bfqq = NULL; ++ ++retry: ++ /* bic always exists here */ ++ bfqq = bic_to_bfqq(bic, is_sync); ++ ++ /* ++ * Always try a new alloc if we fall back to the OOM bfqq ++ * originally, since it should just be a temporary situation. ++ */ ++ if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { ++ bfqq = NULL; ++ if (new_bfqq != NULL) { ++ bfqq = new_bfqq; ++ new_bfqq = NULL; ++ } else if (gfp_mask & __GFP_WAIT) { ++ spin_unlock_irq(bfqd->queue->queue_lock); ++ new_bfqq = kmem_cache_alloc_node(bfq_pool, ++ gfp_mask | __GFP_ZERO, ++ bfqd->queue->node); ++ spin_lock_irq(bfqd->queue->queue_lock); ++ if (new_bfqq != NULL) ++ goto retry; ++ } else { ++ bfqq = kmem_cache_alloc_node(bfq_pool, ++ gfp_mask | __GFP_ZERO, ++ bfqd->queue->node); ++ } ++ ++ if (bfqq != NULL) { ++ bfq_init_bfqq(bfqd, bfqq, current->pid, is_sync); ++ bfq_log_bfqq(bfqd, bfqq, "allocated"); ++ } else { ++ bfqq = &bfqd->oom_bfqq; ++ bfq_log_bfqq(bfqd, bfqq, "using oom bfqq"); ++ } ++ ++ bfq_init_prio_data(bfqq, bic); ++ bfq_init_entity(&bfqq->entity, bfqg); ++ } ++ ++ if (new_bfqq != NULL) ++ kmem_cache_free(bfq_pool, new_bfqq); ++ ++ return bfqq; ++} ++ ++static struct bfq_queue **bfq_async_queue_prio(struct bfq_data *bfqd, ++ struct bfq_group *bfqg, ++ int ioprio_class, int ioprio) ++{ ++ switch (ioprio_class) { ++ case IOPRIO_CLASS_RT: ++ return &bfqg->async_bfqq[0][ioprio]; ++ case IOPRIO_CLASS_NONE: ++ ioprio = IOPRIO_NORM; ++ /* fall through */ ++ case IOPRIO_CLASS_BE: ++ return &bfqg->async_bfqq[1][ioprio]; ++ case IOPRIO_CLASS_IDLE: ++ return &bfqg->async_idle_bfqq; ++ default: ++ BUG(); ++ } ++} ++ ++static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, ++ struct bfq_group *bfqg, int is_sync, ++ struct bfq_io_cq *bic, gfp_t gfp_mask) ++{ ++ const int ioprio = IOPRIO_PRIO_DATA(bic->ioprio); ++ const int ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio); ++ struct bfq_queue **async_bfqq = NULL; ++ struct bfq_queue *bfqq = NULL; ++ ++ if (!is_sync) { ++ async_bfqq = bfq_async_queue_prio(bfqd, bfqg, ioprio_class, ++ ioprio); ++ bfqq = *async_bfqq; ++ } ++ ++ if (bfqq == NULL) ++ bfqq = bfq_find_alloc_queue(bfqd, bfqg, is_sync, bic, gfp_mask); ++ ++ /* ++ * Pin the queue now that it's allocated, scheduler exit will ++ * prune it. ++ */ ++ if (!is_sync && *async_bfqq == NULL) { ++ atomic_inc(&bfqq->ref); ++ bfq_log_bfqq(bfqd, bfqq, "get_queue, bfqq not in async: %p, %d", ++ bfqq, atomic_read(&bfqq->ref)); ++ *async_bfqq = bfqq; ++ } ++ ++ atomic_inc(&bfqq->ref); ++ bfq_log_bfqq(bfqd, bfqq, "get_queue, at end: %p, %d", bfqq, ++ atomic_read(&bfqq->ref)); ++ return bfqq; ++} ++ ++static void bfq_update_io_thinktime(struct bfq_data *bfqd, ++ struct bfq_io_cq *bic) ++{ ++ unsigned long elapsed = jiffies - bic->ttime.last_end_request; ++ unsigned long ttime = min(elapsed, 2UL * bfqd->bfq_slice_idle); ++ ++ bic->ttime.ttime_samples = (7*bic->ttime.ttime_samples + 256) / 8; ++ bic->ttime.ttime_total = (7*bic->ttime.ttime_total + 256*ttime) / 8; ++ bic->ttime.ttime_mean = (bic->ttime.ttime_total + 128) / ++ bic->ttime.ttime_samples; ++} ++ ++static void bfq_update_io_seektime(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ struct request *rq) ++{ ++ sector_t sdist; ++ u64 total; ++ ++ if (bfqq->last_request_pos < blk_rq_pos(rq)) ++ sdist = blk_rq_pos(rq) - bfqq->last_request_pos; ++ else ++ sdist = bfqq->last_request_pos - blk_rq_pos(rq); ++ ++ /* ++ * Don't allow the seek distance to get too large from the ++ * odd fragment, pagein, etc. ++ */ ++ if (bfqq->seek_samples == 0) /* first request, not really a seek */ ++ sdist = 0; ++ else if (bfqq->seek_samples <= 60) /* second & third seek */ ++ sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*1024); ++ else ++ sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*64); ++ ++ bfqq->seek_samples = (7*bfqq->seek_samples + 256) / 8; ++ bfqq->seek_total = (7*bfqq->seek_total + (u64)256*sdist) / 8; ++ total = bfqq->seek_total + (bfqq->seek_samples/2); ++ do_div(total, bfqq->seek_samples); ++ bfqq->seek_mean = (sector_t)total; ++ ++ bfq_log_bfqq(bfqd, bfqq, "dist=%llu mean=%llu", (u64)sdist, ++ (u64)bfqq->seek_mean); ++} ++ ++/* ++ * Disable idle window if the process thinks too long or seeks so much that ++ * it doesn't matter. ++ */ ++static void bfq_update_idle_window(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ struct bfq_io_cq *bic) ++{ ++ int enable_idle; ++ ++ /* Don't idle for async or idle io prio class. */ ++ if (!bfq_bfqq_sync(bfqq) || bfq_class_idle(bfqq)) ++ return; ++ ++ /* Idle window just restored, statistics are meaningless. */ ++ if (bfq_bfqq_just_split(bfqq)) ++ return; ++ ++ enable_idle = bfq_bfqq_idle_window(bfqq); ++ ++ if (atomic_read(&bic->icq.ioc->active_ref) == 0 || ++ bfqd->bfq_slice_idle == 0 || ++ (bfqd->hw_tag && BFQQ_SEEKY(bfqq) && ++ bfqq->wr_coeff == 1)) ++ enable_idle = 0; ++ else if (bfq_sample_valid(bic->ttime.ttime_samples)) { ++ if (bic->ttime.ttime_mean > bfqd->bfq_slice_idle && ++ bfqq->wr_coeff == 1) ++ enable_idle = 0; ++ else ++ enable_idle = 1; ++ } ++ bfq_log_bfqq(bfqd, bfqq, "update_idle_window: enable_idle %d", ++ enable_idle); ++ ++ if (enable_idle) ++ bfq_mark_bfqq_idle_window(bfqq); ++ else ++ bfq_clear_bfqq_idle_window(bfqq); ++} ++ ++/* ++ * Called when a new fs request (rq) is added to bfqq. Check if there's ++ * something we should do about it. ++ */ ++static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ struct request *rq) ++{ ++ struct bfq_io_cq *bic = RQ_BIC(rq); ++ ++ if (rq->cmd_flags & REQ_META) ++ bfqq->meta_pending++; ++ ++ bfq_update_io_thinktime(bfqd, bic); ++ bfq_update_io_seektime(bfqd, bfqq, rq); ++ if (!BFQQ_SEEKY(bfqq) && bfq_bfqq_constantly_seeky(bfqq)) { ++ bfq_clear_bfqq_constantly_seeky(bfqq); ++ if (!blk_queue_nonrot(bfqd->queue)) { ++ BUG_ON(!bfqd->const_seeky_busy_in_flight_queues); ++ bfqd->const_seeky_busy_in_flight_queues--; ++ } ++ } ++ if (bfqq->entity.service > bfq_max_budget(bfqd) / 8 || ++ !BFQQ_SEEKY(bfqq)) ++ bfq_update_idle_window(bfqd, bfqq, bic); ++ bfq_clear_bfqq_just_split(bfqq); ++ ++ bfq_log_bfqq(bfqd, bfqq, ++ "rq_enqueued: idle_window=%d (seeky %d, mean %llu)", ++ bfq_bfqq_idle_window(bfqq), BFQQ_SEEKY(bfqq), ++ (long long unsigned)bfqq->seek_mean); ++ ++ bfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq); ++ ++ if (bfqq == bfqd->in_service_queue && bfq_bfqq_wait_request(bfqq)) { ++ int small_req = bfqq->queued[rq_is_sync(rq)] == 1 && ++ blk_rq_sectors(rq) < 32; ++ int budget_timeout = bfq_bfqq_budget_timeout(bfqq); ++ ++ /* ++ * There is just this request queued: if the request ++ * is small and the queue is not to be expired, then ++ * just exit. ++ * ++ * In this way, if the disk is being idled to wait for ++ * a new request from the in-service queue, we avoid ++ * unplugging the device and committing the disk to serve ++ * just a small request. On the contrary, we wait for ++ * the block layer to decide when to unplug the device: ++ * hopefully, new requests will be merged to this one ++ * quickly, then the device will be unplugged and ++ * larger requests will be dispatched. ++ */ ++ if (small_req && !budget_timeout) ++ return; ++ ++ /* ++ * A large enough request arrived, or the queue is to ++ * be expired: in both cases disk idling is to be ++ * stopped, so clear wait_request flag and reset ++ * timer. ++ */ ++ bfq_clear_bfqq_wait_request(bfqq); ++ del_timer(&bfqd->idle_slice_timer); ++ ++ /* ++ * The queue is not empty, because a new request just ++ * arrived. Hence we can safely expire the queue, in ++ * case of budget timeout, without risking that the ++ * timestamps of the queue are not updated correctly. ++ * See [1] for more details. ++ */ ++ if (budget_timeout) ++ bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); ++ ++ /* ++ * Let the request rip immediately, or let a new queue be ++ * selected if bfqq has just been expired. ++ */ ++ __blk_run_queue(bfqd->queue); ++ } ++} ++ ++static void bfq_insert_request(struct request_queue *q, struct request *rq) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ struct bfq_queue *bfqq = RQ_BFQQ(rq), *new_bfqq; ++ ++ assert_spin_locked(bfqd->queue->queue_lock); ++ ++ /* ++ * An unplug may trigger a requeue of a request from the device ++ * driver: make sure we are in process context while trying to ++ * merge two bfq_queues. ++ */ ++ if (!in_interrupt()) { ++ new_bfqq = bfq_setup_cooperator(bfqd, bfqq, rq, true); ++ if (new_bfqq != NULL) { ++ if (bic_to_bfqq(RQ_BIC(rq), 1) != bfqq) ++ new_bfqq = bic_to_bfqq(RQ_BIC(rq), 1); ++ /* ++ * Release the request's reference to the old bfqq ++ * and make sure one is taken to the shared queue. ++ */ ++ new_bfqq->allocated[rq_data_dir(rq)]++; ++ bfqq->allocated[rq_data_dir(rq)]--; ++ atomic_inc(&new_bfqq->ref); ++ bfq_put_queue(bfqq); ++ if (bic_to_bfqq(RQ_BIC(rq), 1) == bfqq) ++ bfq_merge_bfqqs(bfqd, RQ_BIC(rq), ++ bfqq, new_bfqq); ++ rq->elv.priv[1] = new_bfqq; ++ bfqq = new_bfqq; ++ } else ++ bfq_bfqq_increase_failed_cooperations(bfqq); ++ } ++ ++ bfq_init_prio_data(bfqq, RQ_BIC(rq)); ++ ++ bfq_add_request(rq); ++ ++ /* ++ * Here a newly-created bfq_queue has already started a weight-raising ++ * period: clear raising_time_left to prevent bfq_bfqq_save_state() ++ * from assigning it a full weight-raising period. See the detailed ++ * comments about this field in bfq_init_icq(). ++ */ ++ if (bfqq->bic != NULL) ++ bfqq->bic->wr_time_left = 0; ++ rq_set_fifo_time(rq, jiffies + bfqd->bfq_fifo_expire[rq_is_sync(rq)]); ++ list_add_tail(&rq->queuelist, &bfqq->fifo); ++ ++ bfq_rq_enqueued(bfqd, bfqq, rq); ++} ++ ++static void bfq_update_hw_tag(struct bfq_data *bfqd) ++{ ++ bfqd->max_rq_in_driver = max(bfqd->max_rq_in_driver, ++ bfqd->rq_in_driver); ++ ++ if (bfqd->hw_tag == 1) ++ return; ++ ++ /* ++ * This sample is valid if the number of outstanding requests ++ * is large enough to allow a queueing behavior. Note that the ++ * sum is not exact, as it's not taking into account deactivated ++ * requests. ++ */ ++ if (bfqd->rq_in_driver + bfqd->queued < BFQ_HW_QUEUE_THRESHOLD) ++ return; ++ ++ if (bfqd->hw_tag_samples++ < BFQ_HW_QUEUE_SAMPLES) ++ return; ++ ++ bfqd->hw_tag = bfqd->max_rq_in_driver > BFQ_HW_QUEUE_THRESHOLD; ++ bfqd->max_rq_in_driver = 0; ++ bfqd->hw_tag_samples = 0; ++} ++ ++static void bfq_completed_request(struct request_queue *q, struct request *rq) ++{ ++ struct bfq_queue *bfqq = RQ_BFQQ(rq); ++ struct bfq_data *bfqd = bfqq->bfqd; ++ bool sync = bfq_bfqq_sync(bfqq); ++ ++ bfq_log_bfqq(bfqd, bfqq, "completed one req with %u sects left (%d)", ++ blk_rq_sectors(rq), sync); ++ ++ bfq_update_hw_tag(bfqd); ++ ++ BUG_ON(!bfqd->rq_in_driver); ++ BUG_ON(!bfqq->dispatched); ++ bfqd->rq_in_driver--; ++ bfqq->dispatched--; ++ ++ if (!bfqq->dispatched && !bfq_bfqq_busy(bfqq)) { ++ bfq_weights_tree_remove(bfqd, &bfqq->entity, ++ &bfqd->queue_weights_tree); ++ if (!blk_queue_nonrot(bfqd->queue)) { ++ BUG_ON(!bfqd->busy_in_flight_queues); ++ bfqd->busy_in_flight_queues--; ++ if (bfq_bfqq_constantly_seeky(bfqq)) { ++ BUG_ON(!bfqd-> ++ const_seeky_busy_in_flight_queues); ++ bfqd->const_seeky_busy_in_flight_queues--; ++ } ++ } ++ } ++ ++ if (sync) { ++ bfqd->sync_flight--; ++ RQ_BIC(rq)->ttime.last_end_request = jiffies; ++ } ++ ++ /* ++ * If we are waiting to discover whether the request pattern of the ++ * task associated with the queue is actually isochronous, and ++ * both requisites for this condition to hold are satisfied, then ++ * compute soft_rt_next_start (see the comments to the function ++ * bfq_bfqq_softrt_next_start()). ++ */ ++ if (bfq_bfqq_softrt_update(bfqq) && bfqq->dispatched == 0 && ++ RB_EMPTY_ROOT(&bfqq->sort_list)) ++ bfqq->soft_rt_next_start = ++ bfq_bfqq_softrt_next_start(bfqd, bfqq); ++ ++ /* ++ * If this is the in-service queue, check if it needs to be expired, ++ * or if we want to idle in case it has no pending requests. ++ */ ++ if (bfqd->in_service_queue == bfqq) { ++ if (bfq_bfqq_budget_new(bfqq)) ++ bfq_set_budget_timeout(bfqd); ++ ++ if (bfq_bfqq_must_idle(bfqq)) { ++ bfq_arm_slice_timer(bfqd); ++ goto out; ++ } else if (bfq_may_expire_for_budg_timeout(bfqq)) ++ bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); ++ else if (RB_EMPTY_ROOT(&bfqq->sort_list) && ++ (bfqq->dispatched == 0 || ++ !bfq_bfqq_must_not_expire(bfqq))) ++ bfq_bfqq_expire(bfqd, bfqq, 0, ++ BFQ_BFQQ_NO_MORE_REQUESTS); ++ } ++ ++ if (!bfqd->rq_in_driver) ++ bfq_schedule_dispatch(bfqd); ++ ++out: ++ return; ++} ++ ++static inline int __bfq_may_queue(struct bfq_queue *bfqq) ++{ ++ if (bfq_bfqq_wait_request(bfqq) && bfq_bfqq_must_alloc(bfqq)) { ++ bfq_clear_bfqq_must_alloc(bfqq); ++ return ELV_MQUEUE_MUST; ++ } ++ ++ return ELV_MQUEUE_MAY; ++} ++ ++static int bfq_may_queue(struct request_queue *q, int rw) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ struct task_struct *tsk = current; ++ struct bfq_io_cq *bic; ++ struct bfq_queue *bfqq; ++ ++ /* ++ * Don't force setup of a queue from here, as a call to may_queue ++ * does not necessarily imply that a request actually will be ++ * queued. So just lookup a possibly existing queue, or return ++ * 'may queue' if that fails. ++ */ ++ bic = bfq_bic_lookup(bfqd, tsk->io_context); ++ if (bic == NULL) ++ return ELV_MQUEUE_MAY; ++ ++ bfqq = bic_to_bfqq(bic, rw_is_sync(rw)); ++ if (bfqq != NULL) { ++ bfq_init_prio_data(bfqq, bic); ++ ++ return __bfq_may_queue(bfqq); ++ } ++ ++ return ELV_MQUEUE_MAY; ++} ++ ++/* ++ * Queue lock held here. ++ */ ++static void bfq_put_request(struct request *rq) ++{ ++ struct bfq_queue *bfqq = RQ_BFQQ(rq); ++ ++ if (bfqq != NULL) { ++ const int rw = rq_data_dir(rq); ++ ++ BUG_ON(!bfqq->allocated[rw]); ++ bfqq->allocated[rw]--; ++ ++ rq->elv.priv[0] = NULL; ++ rq->elv.priv[1] = NULL; ++ ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "put_request %p, %d", ++ bfqq, atomic_read(&bfqq->ref)); ++ bfq_put_queue(bfqq); ++ } ++} ++ ++/* ++ * Returns NULL if a new bfqq should be allocated, or the old bfqq if this ++ * was the last process referring to said bfqq. ++ */ ++static struct bfq_queue * ++bfq_split_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq) ++{ ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "splitting queue"); ++ ++ put_io_context(bic->icq.ioc); ++ ++ if (bfqq_process_refs(bfqq) == 1) { ++ bfqq->pid = current->pid; ++ bfq_clear_bfqq_coop(bfqq); ++ bfq_clear_bfqq_split_coop(bfqq); ++ return bfqq; ++ } ++ ++ bic_set_bfqq(bic, NULL, 1); ++ ++ bfq_put_cooperator(bfqq); ++ ++ bfq_put_queue(bfqq); ++ return NULL; ++} ++ ++/* ++ * Allocate bfq data structures associated with this request. ++ */ ++static int bfq_set_request(struct request_queue *q, struct request *rq, ++ struct bio *bio, gfp_t gfp_mask) ++{ ++ struct bfq_data *bfqd = q->elevator->elevator_data; ++ struct bfq_io_cq *bic = icq_to_bic(rq->elv.icq); ++ const int rw = rq_data_dir(rq); ++ const int is_sync = rq_is_sync(rq); ++ struct bfq_queue *bfqq; ++ struct bfq_group *bfqg; ++ unsigned long flags; ++ bool split = false; ++ ++ might_sleep_if(gfp_mask & __GFP_WAIT); ++ ++ bfq_changed_ioprio(bic); ++ ++ spin_lock_irqsave(q->queue_lock, flags); ++ ++ if (bic == NULL) ++ goto queue_fail; ++ ++ bfqg = bfq_bic_update_cgroup(bic); ++ ++new_queue: ++ bfqq = bic_to_bfqq(bic, is_sync); ++ if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { ++ bfqq = bfq_get_queue(bfqd, bfqg, is_sync, bic, gfp_mask); ++ bic_set_bfqq(bic, bfqq, is_sync); ++ } else { ++ /* If the queue was seeky for too long, break it apart. */ ++ if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq)) { ++ bfq_log_bfqq(bfqd, bfqq, "breaking apart bfqq"); ++ bfqq = bfq_split_bfqq(bic, bfqq); ++ split = true; ++ if (!bfqq) ++ goto new_queue; ++ } ++ } ++ ++ bfqq->allocated[rw]++; ++ atomic_inc(&bfqq->ref); ++ bfq_log_bfqq(bfqd, bfqq, "set_request: bfqq %p, %d", bfqq, ++ atomic_read(&bfqq->ref)); ++ ++ rq->elv.priv[0] = bic; ++ rq->elv.priv[1] = bfqq; ++ ++ /* ++ * If a bfq_queue has only one process reference, it is owned ++ * by only one bfq_io_cq: we can set the bic field of the ++ * bfq_queue to the address of that structure. Also, if the ++ * queue has just been split, mark a flag so that the ++ * information is available to the other scheduler hooks. ++ */ ++ if (bfqq_process_refs(bfqq) == 1) { ++ bfqq->bic = bic; ++ if (split) { ++ bfq_mark_bfqq_just_split(bfqq); ++ /* ++ * If the queue has just been split from a shared ++ * queue, restore the idle window and the possible ++ * weight raising period. ++ */ ++ bfq_bfqq_resume_state(bfqq, bic); ++ } ++ } ++ ++ spin_unlock_irqrestore(q->queue_lock, flags); ++ ++ return 0; ++ ++queue_fail: ++ bfq_schedule_dispatch(bfqd); ++ spin_unlock_irqrestore(q->queue_lock, flags); ++ ++ return 1; ++} ++ ++static void bfq_kick_queue(struct work_struct *work) ++{ ++ struct bfq_data *bfqd = ++ container_of(work, struct bfq_data, unplug_work); ++ struct request_queue *q = bfqd->queue; ++ ++ spin_lock_irq(q->queue_lock); ++ __blk_run_queue(q); ++ spin_unlock_irq(q->queue_lock); ++} ++ ++/* ++ * Handler of the expiration of the timer running if the in-service queue ++ * is idling inside its time slice. ++ */ ++static void bfq_idle_slice_timer(unsigned long data) ++{ ++ struct bfq_data *bfqd = (struct bfq_data *)data; ++ struct bfq_queue *bfqq; ++ unsigned long flags; ++ enum bfqq_expiration reason; ++ ++ spin_lock_irqsave(bfqd->queue->queue_lock, flags); ++ ++ bfqq = bfqd->in_service_queue; ++ /* ++ * Theoretical race here: the in-service queue can be NULL or ++ * different from the queue that was idling if the timer handler ++ * spins on the queue_lock and a new request arrives for the ++ * current queue and there is a full dispatch cycle that changes ++ * the in-service queue. This can hardly happen, but in the worst ++ * case we just expire a queue too early. ++ */ ++ if (bfqq != NULL) { ++ bfq_log_bfqq(bfqd, bfqq, "slice_timer expired"); ++ if (bfq_bfqq_budget_timeout(bfqq)) ++ /* ++ * Also here the queue can be safely expired ++ * for budget timeout without wasting ++ * guarantees ++ */ ++ reason = BFQ_BFQQ_BUDGET_TIMEOUT; ++ else if (bfqq->queued[0] == 0 && bfqq->queued[1] == 0) ++ /* ++ * The queue may not be empty upon timer expiration, ++ * because we may not disable the timer when the ++ * first request of the in-service queue arrives ++ * during disk idling. ++ */ ++ reason = BFQ_BFQQ_TOO_IDLE; ++ else ++ goto schedule_dispatch; ++ ++ bfq_bfqq_expire(bfqd, bfqq, 1, reason); ++ } ++ ++schedule_dispatch: ++ bfq_schedule_dispatch(bfqd); ++ ++ spin_unlock_irqrestore(bfqd->queue->queue_lock, flags); ++} ++ ++static void bfq_shutdown_timer_wq(struct bfq_data *bfqd) ++{ ++ del_timer_sync(&bfqd->idle_slice_timer); ++ cancel_work_sync(&bfqd->unplug_work); ++} ++ ++static inline void __bfq_put_async_bfqq(struct bfq_data *bfqd, ++ struct bfq_queue **bfqq_ptr) ++{ ++ struct bfq_group *root_group = bfqd->root_group; ++ struct bfq_queue *bfqq = *bfqq_ptr; ++ ++ bfq_log(bfqd, "put_async_bfqq: %p", bfqq); ++ if (bfqq != NULL) { ++ bfq_bfqq_move(bfqd, bfqq, &bfqq->entity, root_group); ++ bfq_log_bfqq(bfqd, bfqq, "put_async_bfqq: putting %p, %d", ++ bfqq, atomic_read(&bfqq->ref)); ++ bfq_put_queue(bfqq); ++ *bfqq_ptr = NULL; ++ } ++} ++ ++/* ++ * Release all the bfqg references to its async queues. If we are ++ * deallocating the group these queues may still contain requests, so ++ * we reparent them to the root cgroup (i.e., the only one that will ++ * exist for sure until all the requests on a device are gone). ++ */ ++static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg) ++{ ++ int i, j; ++ ++ for (i = 0; i < 2; i++) ++ for (j = 0; j < IOPRIO_BE_NR; j++) ++ __bfq_put_async_bfqq(bfqd, &bfqg->async_bfqq[i][j]); ++ ++ __bfq_put_async_bfqq(bfqd, &bfqg->async_idle_bfqq); ++} ++ ++static void bfq_exit_queue(struct elevator_queue *e) ++{ ++ struct bfq_data *bfqd = e->elevator_data; ++ struct request_queue *q = bfqd->queue; ++ struct bfq_queue *bfqq, *n; ++ ++ bfq_shutdown_timer_wq(bfqd); ++ ++ spin_lock_irq(q->queue_lock); ++ ++ BUG_ON(bfqd->in_service_queue != NULL); ++ list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list) ++ bfq_deactivate_bfqq(bfqd, bfqq, 0); ++ ++ bfq_disconnect_groups(bfqd); ++ spin_unlock_irq(q->queue_lock); ++ ++ bfq_shutdown_timer_wq(bfqd); ++ ++ synchronize_rcu(); ++ ++ BUG_ON(timer_pending(&bfqd->idle_slice_timer)); ++ ++ bfq_free_root_group(bfqd); ++ kfree(bfqd); ++} ++ ++static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) ++{ ++ struct bfq_group *bfqg; ++ struct bfq_data *bfqd; ++ struct elevator_queue *eq; ++ ++ eq = elevator_alloc(q, e); ++ if (eq == NULL) ++ return -ENOMEM; ++ ++ bfqd = kzalloc_node(sizeof(*bfqd), GFP_KERNEL, q->node); ++ if (bfqd == NULL) { ++ kobject_put(&eq->kobj); ++ return -ENOMEM; ++ } ++ eq->elevator_data = bfqd; ++ ++ /* ++ * Our fallback bfqq if bfq_find_alloc_queue() runs into OOM issues. ++ * Grab a permanent reference to it, so that the normal code flow ++ * will not attempt to free it. ++ */ ++ bfq_init_bfqq(bfqd, &bfqd->oom_bfqq, 1, 0); ++ atomic_inc(&bfqd->oom_bfqq.ref); ++ ++ bfqd->queue = q; ++ ++ spin_lock_irq(q->queue_lock); ++ q->elevator = eq; ++ spin_unlock_irq(q->queue_lock); ++ ++ bfqg = bfq_alloc_root_group(bfqd, q->node); ++ if (bfqg == NULL) { ++ kfree(bfqd); ++ kobject_put(&eq->kobj); ++ return -ENOMEM; ++ } ++ ++ bfqd->root_group = bfqg; ++#ifdef CONFIG_CGROUP_BFQIO ++ bfqd->active_numerous_groups = 0; ++#endif ++ ++ init_timer(&bfqd->idle_slice_timer); ++ bfqd->idle_slice_timer.function = bfq_idle_slice_timer; ++ bfqd->idle_slice_timer.data = (unsigned long)bfqd; ++ ++ bfqd->rq_pos_tree = RB_ROOT; ++ bfqd->queue_weights_tree = RB_ROOT; ++ bfqd->group_weights_tree = RB_ROOT; ++ ++ INIT_WORK(&bfqd->unplug_work, bfq_kick_queue); ++ ++ INIT_LIST_HEAD(&bfqd->active_list); ++ INIT_LIST_HEAD(&bfqd->idle_list); ++ ++ bfqd->hw_tag = -1; ++ ++ bfqd->bfq_max_budget = bfq_default_max_budget; ++ ++ bfqd->bfq_quantum = bfq_quantum; ++ bfqd->bfq_fifo_expire[0] = bfq_fifo_expire[0]; ++ bfqd->bfq_fifo_expire[1] = bfq_fifo_expire[1]; ++ bfqd->bfq_back_max = bfq_back_max; ++ bfqd->bfq_back_penalty = bfq_back_penalty; ++ bfqd->bfq_slice_idle = bfq_slice_idle; ++ bfqd->bfq_class_idle_last_service = 0; ++ bfqd->bfq_max_budget_async_rq = bfq_max_budget_async_rq; ++ bfqd->bfq_timeout[BLK_RW_ASYNC] = bfq_timeout_async; ++ bfqd->bfq_timeout[BLK_RW_SYNC] = bfq_timeout_sync; ++ ++ bfqd->bfq_coop_thresh = 2; ++ bfqd->bfq_failed_cooperations = 7000; ++ bfqd->bfq_requests_within_timer = 120; ++ ++ bfqd->low_latency = true; ++ ++ bfqd->bfq_wr_coeff = 20; ++ bfqd->bfq_wr_rt_max_time = msecs_to_jiffies(300); ++ bfqd->bfq_wr_max_time = 0; ++ bfqd->bfq_wr_min_idle_time = msecs_to_jiffies(2000); ++ bfqd->bfq_wr_min_inter_arr_async = msecs_to_jiffies(500); ++ bfqd->bfq_wr_max_softrt_rate = 7000; /* ++ * Approximate rate required ++ * to playback or record a ++ * high-definition compressed ++ * video. ++ */ ++ bfqd->wr_busy_queues = 0; ++ bfqd->busy_in_flight_queues = 0; ++ bfqd->const_seeky_busy_in_flight_queues = 0; ++ ++ /* ++ * Begin by assuming, optimistically, that the device peak rate is ++ * equal to the highest reference rate. ++ */ ++ bfqd->RT_prod = R_fast[blk_queue_nonrot(bfqd->queue)] * ++ T_fast[blk_queue_nonrot(bfqd->queue)]; ++ bfqd->peak_rate = R_fast[blk_queue_nonrot(bfqd->queue)]; ++ bfqd->device_speed = BFQ_BFQD_FAST; ++ ++ return 0; ++} ++ ++static void bfq_slab_kill(void) ++{ ++ if (bfq_pool != NULL) ++ kmem_cache_destroy(bfq_pool); ++} ++ ++static int __init bfq_slab_setup(void) ++{ ++ bfq_pool = KMEM_CACHE(bfq_queue, 0); ++ if (bfq_pool == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++static ssize_t bfq_var_show(unsigned int var, char *page) ++{ ++ return sprintf(page, "%d\n", var); ++} ++ ++static ssize_t bfq_var_store(unsigned long *var, const char *page, ++ size_t count) ++{ ++ unsigned long new_val; ++ int ret = kstrtoul(page, 10, &new_val); ++ ++ if (ret == 0) ++ *var = new_val; ++ ++ return count; ++} ++ ++static ssize_t bfq_wr_max_time_show(struct elevator_queue *e, char *page) ++{ ++ struct bfq_data *bfqd = e->elevator_data; ++ return sprintf(page, "%d\n", bfqd->bfq_wr_max_time > 0 ? ++ jiffies_to_msecs(bfqd->bfq_wr_max_time) : ++ jiffies_to_msecs(bfq_wr_duration(bfqd))); ++} ++ ++static ssize_t bfq_weights_show(struct elevator_queue *e, char *page) ++{ ++ struct bfq_queue *bfqq; ++ struct bfq_data *bfqd = e->elevator_data; ++ ssize_t num_char = 0; ++ ++ num_char += sprintf(page + num_char, "Tot reqs queued %d\n\n", ++ bfqd->queued); ++ ++ spin_lock_irq(bfqd->queue->queue_lock); ++ ++ num_char += sprintf(page + num_char, "Active:\n"); ++ list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) { ++ num_char += sprintf(page + num_char, ++ "pid%d: weight %hu, nr_queued %d %d, dur %d/%u\n", ++ bfqq->pid, ++ bfqq->entity.weight, ++ bfqq->queued[0], ++ bfqq->queued[1], ++ jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish), ++ jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ } ++ ++ num_char += sprintf(page + num_char, "Idle:\n"); ++ list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) { ++ num_char += sprintf(page + num_char, ++ "pid%d: weight %hu, dur %d/%u\n", ++ bfqq->pid, ++ bfqq->entity.weight, ++ jiffies_to_msecs(jiffies - ++ bfqq->last_wr_start_finish), ++ jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ } ++ ++ spin_unlock_irq(bfqd->queue->queue_lock); ++ ++ return num_char; ++} ++ ++#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ ++static ssize_t __FUNC(struct elevator_queue *e, char *page) \ ++{ \ ++ struct bfq_data *bfqd = e->elevator_data; \ ++ unsigned int __data = __VAR; \ ++ if (__CONV) \ ++ __data = jiffies_to_msecs(__data); \ ++ return bfq_var_show(__data, (page)); \ ++} ++SHOW_FUNCTION(bfq_quantum_show, bfqd->bfq_quantum, 0); ++SHOW_FUNCTION(bfq_fifo_expire_sync_show, bfqd->bfq_fifo_expire[1], 1); ++SHOW_FUNCTION(bfq_fifo_expire_async_show, bfqd->bfq_fifo_expire[0], 1); ++SHOW_FUNCTION(bfq_back_seek_max_show, bfqd->bfq_back_max, 0); ++SHOW_FUNCTION(bfq_back_seek_penalty_show, bfqd->bfq_back_penalty, 0); ++SHOW_FUNCTION(bfq_slice_idle_show, bfqd->bfq_slice_idle, 1); ++SHOW_FUNCTION(bfq_max_budget_show, bfqd->bfq_user_max_budget, 0); ++SHOW_FUNCTION(bfq_max_budget_async_rq_show, ++ bfqd->bfq_max_budget_async_rq, 0); ++SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout[BLK_RW_SYNC], 1); ++SHOW_FUNCTION(bfq_timeout_async_show, bfqd->bfq_timeout[BLK_RW_ASYNC], 1); ++SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0); ++SHOW_FUNCTION(bfq_wr_coeff_show, bfqd->bfq_wr_coeff, 0); ++SHOW_FUNCTION(bfq_wr_rt_max_time_show, bfqd->bfq_wr_rt_max_time, 1); ++SHOW_FUNCTION(bfq_wr_min_idle_time_show, bfqd->bfq_wr_min_idle_time, 1); ++SHOW_FUNCTION(bfq_wr_min_inter_arr_async_show, bfqd->bfq_wr_min_inter_arr_async, ++ 1); ++SHOW_FUNCTION(bfq_wr_max_softrt_rate_show, bfqd->bfq_wr_max_softrt_rate, 0); ++#undef SHOW_FUNCTION ++ ++#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ ++static ssize_t \ ++__FUNC(struct elevator_queue *e, const char *page, size_t count) \ ++{ \ ++ struct bfq_data *bfqd = e->elevator_data; \ ++ unsigned long uninitialized_var(__data); \ ++ int ret = bfq_var_store(&__data, (page), count); \ ++ if (__data < (MIN)) \ ++ __data = (MIN); \ ++ else if (__data > (MAX)) \ ++ __data = (MAX); \ ++ if (__CONV) \ ++ *(__PTR) = msecs_to_jiffies(__data); \ ++ else \ ++ *(__PTR) = __data; \ ++ return ret; \ ++} ++STORE_FUNCTION(bfq_quantum_store, &bfqd->bfq_quantum, 1, INT_MAX, 0); ++STORE_FUNCTION(bfq_fifo_expire_sync_store, &bfqd->bfq_fifo_expire[1], 1, ++ INT_MAX, 1); ++STORE_FUNCTION(bfq_fifo_expire_async_store, &bfqd->bfq_fifo_expire[0], 1, ++ INT_MAX, 1); ++STORE_FUNCTION(bfq_back_seek_max_store, &bfqd->bfq_back_max, 0, INT_MAX, 0); ++STORE_FUNCTION(bfq_back_seek_penalty_store, &bfqd->bfq_back_penalty, 1, ++ INT_MAX, 0); ++STORE_FUNCTION(bfq_slice_idle_store, &bfqd->bfq_slice_idle, 0, INT_MAX, 1); ++STORE_FUNCTION(bfq_max_budget_async_rq_store, &bfqd->bfq_max_budget_async_rq, ++ 1, INT_MAX, 0); ++STORE_FUNCTION(bfq_timeout_async_store, &bfqd->bfq_timeout[BLK_RW_ASYNC], 0, ++ INT_MAX, 1); ++STORE_FUNCTION(bfq_wr_coeff_store, &bfqd->bfq_wr_coeff, 1, INT_MAX, 0); ++STORE_FUNCTION(bfq_wr_max_time_store, &bfqd->bfq_wr_max_time, 0, INT_MAX, 1); ++STORE_FUNCTION(bfq_wr_rt_max_time_store, &bfqd->bfq_wr_rt_max_time, 0, INT_MAX, ++ 1); ++STORE_FUNCTION(bfq_wr_min_idle_time_store, &bfqd->bfq_wr_min_idle_time, 0, ++ INT_MAX, 1); ++STORE_FUNCTION(bfq_wr_min_inter_arr_async_store, ++ &bfqd->bfq_wr_min_inter_arr_async, 0, INT_MAX, 1); ++STORE_FUNCTION(bfq_wr_max_softrt_rate_store, &bfqd->bfq_wr_max_softrt_rate, 0, ++ INT_MAX, 0); ++#undef STORE_FUNCTION ++ ++/* do nothing for the moment */ ++static ssize_t bfq_weights_store(struct elevator_queue *e, ++ const char *page, size_t count) ++{ ++ return count; ++} ++ ++static inline unsigned long bfq_estimated_max_budget(struct bfq_data *bfqd) ++{ ++ u64 timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); ++ ++ if (bfqd->peak_rate_samples >= BFQ_PEAK_RATE_SAMPLES) ++ return bfq_calc_max_budget(bfqd->peak_rate, timeout); ++ else ++ return bfq_default_max_budget; ++} ++ ++static ssize_t bfq_max_budget_store(struct elevator_queue *e, ++ const char *page, size_t count) ++{ ++ struct bfq_data *bfqd = e->elevator_data; ++ unsigned long uninitialized_var(__data); ++ int ret = bfq_var_store(&__data, (page), count); ++ ++ if (__data == 0) ++ bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); ++ else { ++ if (__data > INT_MAX) ++ __data = INT_MAX; ++ bfqd->bfq_max_budget = __data; ++ } ++ ++ bfqd->bfq_user_max_budget = __data; ++ ++ return ret; ++} ++ ++static ssize_t bfq_timeout_sync_store(struct elevator_queue *e, ++ const char *page, size_t count) ++{ ++ struct bfq_data *bfqd = e->elevator_data; ++ unsigned long uninitialized_var(__data); ++ int ret = bfq_var_store(&__data, (page), count); ++ ++ if (__data < 1) ++ __data = 1; ++ else if (__data > INT_MAX) ++ __data = INT_MAX; ++ ++ bfqd->bfq_timeout[BLK_RW_SYNC] = msecs_to_jiffies(__data); ++ if (bfqd->bfq_user_max_budget == 0) ++ bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); ++ ++ return ret; ++} ++ ++static ssize_t bfq_low_latency_store(struct elevator_queue *e, ++ const char *page, size_t count) ++{ ++ struct bfq_data *bfqd = e->elevator_data; ++ unsigned long uninitialized_var(__data); ++ int ret = bfq_var_store(&__data, (page), count); ++ ++ if (__data > 1) ++ __data = 1; ++ if (__data == 0 && bfqd->low_latency != 0) ++ bfq_end_wr(bfqd); ++ bfqd->low_latency = __data; ++ ++ return ret; ++} ++ ++#define BFQ_ATTR(name) \ ++ __ATTR(name, S_IRUGO|S_IWUSR, bfq_##name##_show, bfq_##name##_store) ++ ++static struct elv_fs_entry bfq_attrs[] = { ++ BFQ_ATTR(quantum), ++ BFQ_ATTR(fifo_expire_sync), ++ BFQ_ATTR(fifo_expire_async), ++ BFQ_ATTR(back_seek_max), ++ BFQ_ATTR(back_seek_penalty), ++ BFQ_ATTR(slice_idle), ++ BFQ_ATTR(max_budget), ++ BFQ_ATTR(max_budget_async_rq), ++ BFQ_ATTR(timeout_sync), ++ BFQ_ATTR(timeout_async), ++ BFQ_ATTR(low_latency), ++ BFQ_ATTR(wr_coeff), ++ BFQ_ATTR(wr_max_time), ++ BFQ_ATTR(wr_rt_max_time), ++ BFQ_ATTR(wr_min_idle_time), ++ BFQ_ATTR(wr_min_inter_arr_async), ++ BFQ_ATTR(wr_max_softrt_rate), ++ BFQ_ATTR(weights), ++ __ATTR_NULL ++}; ++ ++static struct elevator_type iosched_bfq = { ++ .ops = { ++ .elevator_merge_fn = bfq_merge, ++ .elevator_merged_fn = bfq_merged_request, ++ .elevator_merge_req_fn = bfq_merged_requests, ++ .elevator_allow_merge_fn = bfq_allow_merge, ++ .elevator_dispatch_fn = bfq_dispatch_requests, ++ .elevator_add_req_fn = bfq_insert_request, ++ .elevator_activate_req_fn = bfq_activate_request, ++ .elevator_deactivate_req_fn = bfq_deactivate_request, ++ .elevator_completed_req_fn = bfq_completed_request, ++ .elevator_former_req_fn = elv_rb_former_request, ++ .elevator_latter_req_fn = elv_rb_latter_request, ++ .elevator_init_icq_fn = bfq_init_icq, ++ .elevator_exit_icq_fn = bfq_exit_icq, ++ .elevator_set_req_fn = bfq_set_request, ++ .elevator_put_req_fn = bfq_put_request, ++ .elevator_may_queue_fn = bfq_may_queue, ++ .elevator_init_fn = bfq_init_queue, ++ .elevator_exit_fn = bfq_exit_queue, ++ }, ++ .icq_size = sizeof(struct bfq_io_cq), ++ .icq_align = __alignof__(struct bfq_io_cq), ++ .elevator_attrs = bfq_attrs, ++ .elevator_name = "bfq", ++ .elevator_owner = THIS_MODULE, ++}; ++ ++static int __init bfq_init(void) ++{ ++ /* ++ * Can be 0 on HZ < 1000 setups. ++ */ ++ if (bfq_slice_idle == 0) ++ bfq_slice_idle = 1; ++ ++ if (bfq_timeout_async == 0) ++ bfq_timeout_async = 1; ++ ++ if (bfq_slab_setup()) ++ return -ENOMEM; ++ ++ /* ++ * Times to load large popular applications for the typical systems ++ * installed on the reference devices (see the comments before the ++ * definitions of the two arrays). ++ */ ++ T_slow[0] = msecs_to_jiffies(2600); ++ T_slow[1] = msecs_to_jiffies(1000); ++ T_fast[0] = msecs_to_jiffies(5500); ++ T_fast[1] = msecs_to_jiffies(2000); ++ ++ /* ++ * Thresholds that determine the switch between speed classes (see ++ * the comments before the definition of the array). ++ */ ++ device_speed_thresh[0] = (R_fast[0] + R_slow[0]) / 2; ++ device_speed_thresh[1] = (R_fast[1] + R_slow[1]) / 2; ++ ++ elv_register(&iosched_bfq); ++ pr_info("BFQ I/O-scheduler version: v7r5"); ++ ++ return 0; ++} ++ ++static void __exit bfq_exit(void) ++{ ++ elv_unregister(&iosched_bfq); ++ bfq_slab_kill(); ++} ++ ++module_init(bfq_init); ++module_exit(bfq_exit); ++ ++MODULE_AUTHOR("Fabio Checconi, Paolo Valente"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/block/bfq-sched.c linux-openelec/block/bfq-sched.c +--- linux-3.14.36/block/bfq-sched.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/block/bfq-sched.c 2015-05-06 12:05:43.000000000 -0500 +@@ -0,0 +1,1179 @@ ++/* ++ * BFQ: Hierarchical B-WF2Q+ scheduler. ++ * ++ * Based on ideas and code from CFQ: ++ * Copyright (C) 2003 Jens Axboe ++ * ++ * Copyright (C) 2008 Fabio Checconi ++ * Paolo Valente ++ * ++ * Copyright (C) 2010 Paolo Valente ++ */ ++ ++#ifdef CONFIG_CGROUP_BFQIO ++#define for_each_entity(entity) \ ++ for (; entity != NULL; entity = entity->parent) ++ ++#define for_each_entity_safe(entity, parent) \ ++ for (; entity && ({ parent = entity->parent; 1; }); entity = parent) ++ ++static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, ++ int extract, ++ struct bfq_data *bfqd); ++ ++static inline void bfq_update_budget(struct bfq_entity *next_in_service) ++{ ++ struct bfq_entity *bfqg_entity; ++ struct bfq_group *bfqg; ++ struct bfq_sched_data *group_sd; ++ ++ BUG_ON(next_in_service == NULL); ++ ++ group_sd = next_in_service->sched_data; ++ ++ bfqg = container_of(group_sd, struct bfq_group, sched_data); ++ /* ++ * bfq_group's my_entity field is not NULL only if the group ++ * is not the root group. We must not touch the root entity ++ * as it must never become an in-service entity. ++ */ ++ bfqg_entity = bfqg->my_entity; ++ if (bfqg_entity != NULL) ++ bfqg_entity->budget = next_in_service->budget; ++} ++ ++static int bfq_update_next_in_service(struct bfq_sched_data *sd) ++{ ++ struct bfq_entity *next_in_service; ++ ++ if (sd->in_service_entity != NULL) ++ /* will update/requeue at the end of service */ ++ return 0; ++ ++ /* ++ * NOTE: this can be improved in many ways, such as returning ++ * 1 (and thus propagating upwards the update) only when the ++ * budget changes, or caching the bfqq that will be scheduled ++ * next from this subtree. By now we worry more about ++ * correctness than about performance... ++ */ ++ next_in_service = bfq_lookup_next_entity(sd, 0, NULL); ++ sd->next_in_service = next_in_service; ++ ++ if (next_in_service != NULL) ++ bfq_update_budget(next_in_service); ++ ++ return 1; ++} ++ ++static inline void bfq_check_next_in_service(struct bfq_sched_data *sd, ++ struct bfq_entity *entity) ++{ ++ BUG_ON(sd->next_in_service != entity); ++} ++#else ++#define for_each_entity(entity) \ ++ for (; entity != NULL; entity = NULL) ++ ++#define for_each_entity_safe(entity, parent) \ ++ for (parent = NULL; entity != NULL; entity = parent) ++ ++static inline int bfq_update_next_in_service(struct bfq_sched_data *sd) ++{ ++ return 0; ++} ++ ++static inline void bfq_check_next_in_service(struct bfq_sched_data *sd, ++ struct bfq_entity *entity) ++{ ++} ++ ++static inline void bfq_update_budget(struct bfq_entity *next_in_service) ++{ ++} ++#endif ++ ++/* ++ * Shift for timestamp calculations. This actually limits the maximum ++ * service allowed in one timestamp delta (small shift values increase it), ++ * the maximum total weight that can be used for the queues in the system ++ * (big shift values increase it), and the period of virtual time ++ * wraparounds. ++ */ ++#define WFQ_SERVICE_SHIFT 22 ++ ++/** ++ * bfq_gt - compare two timestamps. ++ * @a: first ts. ++ * @b: second ts. ++ * ++ * Return @a > @b, dealing with wrapping correctly. ++ */ ++static inline int bfq_gt(u64 a, u64 b) ++{ ++ return (s64)(a - b) > 0; ++} ++ ++static inline struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = NULL; ++ ++ BUG_ON(entity == NULL); ++ ++ if (entity->my_sched_data == NULL) ++ bfqq = container_of(entity, struct bfq_queue, entity); ++ ++ return bfqq; ++} ++ ++ ++/** ++ * bfq_delta - map service into the virtual time domain. ++ * @service: amount of service. ++ * @weight: scale factor (weight of an entity or weight sum). ++ */ ++static inline u64 bfq_delta(unsigned long service, ++ unsigned long weight) ++{ ++ u64 d = (u64)service << WFQ_SERVICE_SHIFT; ++ ++ do_div(d, weight); ++ return d; ++} ++ ++/** ++ * bfq_calc_finish - assign the finish time to an entity. ++ * @entity: the entity to act upon. ++ * @service: the service to be charged to the entity. ++ */ ++static inline void bfq_calc_finish(struct bfq_entity *entity, ++ unsigned long service) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ ++ BUG_ON(entity->weight == 0); ++ ++ entity->finish = entity->start + ++ bfq_delta(service, entity->weight); ++ ++ if (bfqq != NULL) { ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "calc_finish: serv %lu, w %d", ++ service, entity->weight); ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "calc_finish: start %llu, finish %llu, delta %llu", ++ entity->start, entity->finish, ++ bfq_delta(service, entity->weight)); ++ } ++} ++ ++/** ++ * bfq_entity_of - get an entity from a node. ++ * @node: the node field of the entity. ++ * ++ * Convert a node pointer to the relative entity. This is used only ++ * to simplify the logic of some functions and not as the generic ++ * conversion mechanism because, e.g., in the tree walking functions, ++ * the check for a %NULL value would be redundant. ++ */ ++static inline struct bfq_entity *bfq_entity_of(struct rb_node *node) ++{ ++ struct bfq_entity *entity = NULL; ++ ++ if (node != NULL) ++ entity = rb_entry(node, struct bfq_entity, rb_node); ++ ++ return entity; ++} ++ ++/** ++ * bfq_extract - remove an entity from a tree. ++ * @root: the tree root. ++ * @entity: the entity to remove. ++ */ ++static inline void bfq_extract(struct rb_root *root, ++ struct bfq_entity *entity) ++{ ++ BUG_ON(entity->tree != root); ++ ++ entity->tree = NULL; ++ rb_erase(&entity->rb_node, root); ++} ++ ++/** ++ * bfq_idle_extract - extract an entity from the idle tree. ++ * @st: the service tree of the owning @entity. ++ * @entity: the entity being removed. ++ */ ++static void bfq_idle_extract(struct bfq_service_tree *st, ++ struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ struct rb_node *next; ++ ++ BUG_ON(entity->tree != &st->idle); ++ ++ if (entity == st->first_idle) { ++ next = rb_next(&entity->rb_node); ++ st->first_idle = bfq_entity_of(next); ++ } ++ ++ if (entity == st->last_idle) { ++ next = rb_prev(&entity->rb_node); ++ st->last_idle = bfq_entity_of(next); ++ } ++ ++ bfq_extract(&st->idle, entity); ++ ++ if (bfqq != NULL) ++ list_del(&bfqq->bfqq_list); ++} ++ ++/** ++ * bfq_insert - generic tree insertion. ++ * @root: tree root. ++ * @entity: entity to insert. ++ * ++ * This is used for the idle and the active tree, since they are both ++ * ordered by finish time. ++ */ ++static void bfq_insert(struct rb_root *root, struct bfq_entity *entity) ++{ ++ struct bfq_entity *entry; ++ struct rb_node **node = &root->rb_node; ++ struct rb_node *parent = NULL; ++ ++ BUG_ON(entity->tree != NULL); ++ ++ while (*node != NULL) { ++ parent = *node; ++ entry = rb_entry(parent, struct bfq_entity, rb_node); ++ ++ if (bfq_gt(entry->finish, entity->finish)) ++ node = &parent->rb_left; ++ else ++ node = &parent->rb_right; ++ } ++ ++ rb_link_node(&entity->rb_node, parent, node); ++ rb_insert_color(&entity->rb_node, root); ++ ++ entity->tree = root; ++} ++ ++/** ++ * bfq_update_min - update the min_start field of a entity. ++ * @entity: the entity to update. ++ * @node: one of its children. ++ * ++ * This function is called when @entity may store an invalid value for ++ * min_start due to updates to the active tree. The function assumes ++ * that the subtree rooted at @node (which may be its left or its right ++ * child) has a valid min_start value. ++ */ ++static inline void bfq_update_min(struct bfq_entity *entity, ++ struct rb_node *node) ++{ ++ struct bfq_entity *child; ++ ++ if (node != NULL) { ++ child = rb_entry(node, struct bfq_entity, rb_node); ++ if (bfq_gt(entity->min_start, child->min_start)) ++ entity->min_start = child->min_start; ++ } ++} ++ ++/** ++ * bfq_update_active_node - recalculate min_start. ++ * @node: the node to update. ++ * ++ * @node may have changed position or one of its children may have moved, ++ * this function updates its min_start value. The left and right subtrees ++ * are assumed to hold a correct min_start value. ++ */ ++static inline void bfq_update_active_node(struct rb_node *node) ++{ ++ struct bfq_entity *entity = rb_entry(node, struct bfq_entity, rb_node); ++ ++ entity->min_start = entity->start; ++ bfq_update_min(entity, node->rb_right); ++ bfq_update_min(entity, node->rb_left); ++} ++ ++/** ++ * bfq_update_active_tree - update min_start for the whole active tree. ++ * @node: the starting node. ++ * ++ * @node must be the deepest modified node after an update. This function ++ * updates its min_start using the values held by its children, assuming ++ * that they did not change, and then updates all the nodes that may have ++ * changed in the path to the root. The only nodes that may have changed ++ * are the ones in the path or their siblings. ++ */ ++static void bfq_update_active_tree(struct rb_node *node) ++{ ++ struct rb_node *parent; ++ ++up: ++ bfq_update_active_node(node); ++ ++ parent = rb_parent(node); ++ if (parent == NULL) ++ return; ++ ++ if (node == parent->rb_left && parent->rb_right != NULL) ++ bfq_update_active_node(parent->rb_right); ++ else if (parent->rb_left != NULL) ++ bfq_update_active_node(parent->rb_left); ++ ++ node = parent; ++ goto up; ++} ++ ++static void bfq_weights_tree_add(struct bfq_data *bfqd, ++ struct bfq_entity *entity, ++ struct rb_root *root); ++ ++static void bfq_weights_tree_remove(struct bfq_data *bfqd, ++ struct bfq_entity *entity, ++ struct rb_root *root); ++ ++ ++/** ++ * bfq_active_insert - insert an entity in the active tree of its ++ * group/device. ++ * @st: the service tree of the entity. ++ * @entity: the entity being inserted. ++ * ++ * The active tree is ordered by finish time, but an extra key is kept ++ * per each node, containing the minimum value for the start times of ++ * its children (and the node itself), so it's possible to search for ++ * the eligible node with the lowest finish time in logarithmic time. ++ */ ++static void bfq_active_insert(struct bfq_service_tree *st, ++ struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ struct rb_node *node = &entity->rb_node; ++#ifdef CONFIG_CGROUP_BFQIO ++ struct bfq_sched_data *sd = NULL; ++ struct bfq_group *bfqg = NULL; ++ struct bfq_data *bfqd = NULL; ++#endif ++ ++ bfq_insert(&st->active, entity); ++ ++ if (node->rb_left != NULL) ++ node = node->rb_left; ++ else if (node->rb_right != NULL) ++ node = node->rb_right; ++ ++ bfq_update_active_tree(node); ++ ++#ifdef CONFIG_CGROUP_BFQIO ++ sd = entity->sched_data; ++ bfqg = container_of(sd, struct bfq_group, sched_data); ++ BUG_ON(!bfqg); ++ bfqd = (struct bfq_data *)bfqg->bfqd; ++#endif ++ if (bfqq != NULL) ++ list_add(&bfqq->bfqq_list, &bfqq->bfqd->active_list); ++#ifdef CONFIG_CGROUP_BFQIO ++ else { /* bfq_group */ ++ BUG_ON(!bfqd); ++ bfq_weights_tree_add(bfqd, entity, &bfqd->group_weights_tree); ++ } ++ if (bfqg != bfqd->root_group) { ++ BUG_ON(!bfqg); ++ BUG_ON(!bfqd); ++ bfqg->active_entities++; ++ if (bfqg->active_entities == 2) ++ bfqd->active_numerous_groups++; ++ } ++#endif ++} ++ ++/** ++ * bfq_ioprio_to_weight - calc a weight from an ioprio. ++ * @ioprio: the ioprio value to convert. ++ */ ++static inline unsigned short bfq_ioprio_to_weight(int ioprio) ++{ ++ BUG_ON(ioprio < 0 || ioprio >= IOPRIO_BE_NR); ++ return IOPRIO_BE_NR - ioprio; ++} ++ ++/** ++ * bfq_weight_to_ioprio - calc an ioprio from a weight. ++ * @weight: the weight value to convert. ++ * ++ * To preserve as mush as possible the old only-ioprio user interface, ++ * 0 is used as an escape ioprio value for weights (numerically) equal or ++ * larger than IOPRIO_BE_NR ++ */ ++static inline unsigned short bfq_weight_to_ioprio(int weight) ++{ ++ BUG_ON(weight < BFQ_MIN_WEIGHT || weight > BFQ_MAX_WEIGHT); ++ return IOPRIO_BE_NR - weight < 0 ? 0 : IOPRIO_BE_NR - weight; ++} ++ ++static inline void bfq_get_entity(struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ ++ if (bfqq != NULL) { ++ atomic_inc(&bfqq->ref); ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "get_entity: %p %d", ++ bfqq, atomic_read(&bfqq->ref)); ++ } ++} ++ ++/** ++ * bfq_find_deepest - find the deepest node that an extraction can modify. ++ * @node: the node being removed. ++ * ++ * Do the first step of an extraction in an rb tree, looking for the ++ * node that will replace @node, and returning the deepest node that ++ * the following modifications to the tree can touch. If @node is the ++ * last node in the tree return %NULL. ++ */ ++static struct rb_node *bfq_find_deepest(struct rb_node *node) ++{ ++ struct rb_node *deepest; ++ ++ if (node->rb_right == NULL && node->rb_left == NULL) ++ deepest = rb_parent(node); ++ else if (node->rb_right == NULL) ++ deepest = node->rb_left; ++ else if (node->rb_left == NULL) ++ deepest = node->rb_right; ++ else { ++ deepest = rb_next(node); ++ if (deepest->rb_right != NULL) ++ deepest = deepest->rb_right; ++ else if (rb_parent(deepest) != node) ++ deepest = rb_parent(deepest); ++ } ++ ++ return deepest; ++} ++ ++/** ++ * bfq_active_extract - remove an entity from the active tree. ++ * @st: the service_tree containing the tree. ++ * @entity: the entity being removed. ++ */ ++static void bfq_active_extract(struct bfq_service_tree *st, ++ struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ struct rb_node *node; ++#ifdef CONFIG_CGROUP_BFQIO ++ struct bfq_sched_data *sd = NULL; ++ struct bfq_group *bfqg = NULL; ++ struct bfq_data *bfqd = NULL; ++#endif ++ ++ node = bfq_find_deepest(&entity->rb_node); ++ bfq_extract(&st->active, entity); ++ ++ if (node != NULL) ++ bfq_update_active_tree(node); ++ ++#ifdef CONFIG_CGROUP_BFQIO ++ sd = entity->sched_data; ++ bfqg = container_of(sd, struct bfq_group, sched_data); ++ BUG_ON(!bfqg); ++ bfqd = (struct bfq_data *)bfqg->bfqd; ++#endif ++ if (bfqq != NULL) ++ list_del(&bfqq->bfqq_list); ++#ifdef CONFIG_CGROUP_BFQIO ++ else { /* bfq_group */ ++ BUG_ON(!bfqd); ++ bfq_weights_tree_remove(bfqd, entity, ++ &bfqd->group_weights_tree); ++ } ++ if (bfqg != bfqd->root_group) { ++ BUG_ON(!bfqg); ++ BUG_ON(!bfqd); ++ BUG_ON(!bfqg->active_entities); ++ bfqg->active_entities--; ++ if (bfqg->active_entities == 1) { ++ BUG_ON(!bfqd->active_numerous_groups); ++ bfqd->active_numerous_groups--; ++ } ++ } ++#endif ++} ++ ++/** ++ * bfq_idle_insert - insert an entity into the idle tree. ++ * @st: the service tree containing the tree. ++ * @entity: the entity to insert. ++ */ ++static void bfq_idle_insert(struct bfq_service_tree *st, ++ struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ struct bfq_entity *first_idle = st->first_idle; ++ struct bfq_entity *last_idle = st->last_idle; ++ ++ if (first_idle == NULL || bfq_gt(first_idle->finish, entity->finish)) ++ st->first_idle = entity; ++ if (last_idle == NULL || bfq_gt(entity->finish, last_idle->finish)) ++ st->last_idle = entity; ++ ++ bfq_insert(&st->idle, entity); ++ ++ if (bfqq != NULL) ++ list_add(&bfqq->bfqq_list, &bfqq->bfqd->idle_list); ++} ++ ++/** ++ * bfq_forget_entity - remove an entity from the wfq trees. ++ * @st: the service tree. ++ * @entity: the entity being removed. ++ * ++ * Update the device status and forget everything about @entity, putting ++ * the device reference to it, if it is a queue. Entities belonging to ++ * groups are not refcounted. ++ */ ++static void bfq_forget_entity(struct bfq_service_tree *st, ++ struct bfq_entity *entity) ++{ ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ struct bfq_sched_data *sd; ++ ++ BUG_ON(!entity->on_st); ++ ++ entity->on_st = 0; ++ st->wsum -= entity->weight; ++ if (bfqq != NULL) { ++ sd = entity->sched_data; ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "forget_entity: %p %d", ++ bfqq, atomic_read(&bfqq->ref)); ++ bfq_put_queue(bfqq); ++ } ++} ++ ++/** ++ * bfq_put_idle_entity - release the idle tree ref of an entity. ++ * @st: service tree for the entity. ++ * @entity: the entity being released. ++ */ ++static void bfq_put_idle_entity(struct bfq_service_tree *st, ++ struct bfq_entity *entity) ++{ ++ bfq_idle_extract(st, entity); ++ bfq_forget_entity(st, entity); ++} ++ ++/** ++ * bfq_forget_idle - update the idle tree if necessary. ++ * @st: the service tree to act upon. ++ * ++ * To preserve the global O(log N) complexity we only remove one entry here; ++ * as the idle tree will not grow indefinitely this can be done safely. ++ */ ++static void bfq_forget_idle(struct bfq_service_tree *st) ++{ ++ struct bfq_entity *first_idle = st->first_idle; ++ struct bfq_entity *last_idle = st->last_idle; ++ ++ if (RB_EMPTY_ROOT(&st->active) && last_idle != NULL && ++ !bfq_gt(last_idle->finish, st->vtime)) { ++ /* ++ * Forget the whole idle tree, increasing the vtime past ++ * the last finish time of idle entities. ++ */ ++ st->vtime = last_idle->finish; ++ } ++ ++ if (first_idle != NULL && !bfq_gt(first_idle->finish, st->vtime)) ++ bfq_put_idle_entity(st, first_idle); ++} ++ ++static struct bfq_service_tree * ++__bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, ++ struct bfq_entity *entity) ++{ ++ struct bfq_service_tree *new_st = old_st; ++ ++ if (entity->ioprio_changed) { ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ unsigned short prev_weight, new_weight; ++ struct bfq_data *bfqd = NULL; ++ struct rb_root *root; ++#ifdef CONFIG_CGROUP_BFQIO ++ struct bfq_sched_data *sd; ++ struct bfq_group *bfqg; ++#endif ++ ++ if (bfqq != NULL) ++ bfqd = bfqq->bfqd; ++#ifdef CONFIG_CGROUP_BFQIO ++ else { ++ sd = entity->my_sched_data; ++ bfqg = container_of(sd, struct bfq_group, sched_data); ++ BUG_ON(!bfqg); ++ bfqd = (struct bfq_data *)bfqg->bfqd; ++ BUG_ON(!bfqd); ++ } ++#endif ++ ++ BUG_ON(old_st->wsum < entity->weight); ++ old_st->wsum -= entity->weight; ++ ++ if (entity->new_weight != entity->orig_weight) { ++ entity->orig_weight = entity->new_weight; ++ entity->ioprio = ++ bfq_weight_to_ioprio(entity->orig_weight); ++ } else if (entity->new_ioprio != entity->ioprio) { ++ entity->ioprio = entity->new_ioprio; ++ entity->orig_weight = ++ bfq_ioprio_to_weight(entity->ioprio); ++ } else ++ entity->new_weight = entity->orig_weight = ++ bfq_ioprio_to_weight(entity->ioprio); ++ ++ entity->ioprio_class = entity->new_ioprio_class; ++ entity->ioprio_changed = 0; ++ ++ /* ++ * NOTE: here we may be changing the weight too early, ++ * this will cause unfairness. The correct approach ++ * would have required additional complexity to defer ++ * weight changes to the proper time instants (i.e., ++ * when entity->finish <= old_st->vtime). ++ */ ++ new_st = bfq_entity_service_tree(entity); ++ ++ prev_weight = entity->weight; ++ new_weight = entity->orig_weight * ++ (bfqq != NULL ? bfqq->wr_coeff : 1); ++ /* ++ * If the weight of the entity changes, remove the entity ++ * from its old weight counter (if there is a counter ++ * associated with the entity), and add it to the counter ++ * associated with its new weight. ++ */ ++ if (prev_weight != new_weight) { ++ root = bfqq ? &bfqd->queue_weights_tree : ++ &bfqd->group_weights_tree; ++ bfq_weights_tree_remove(bfqd, entity, root); ++ } ++ entity->weight = new_weight; ++ /* ++ * Add the entity to its weights tree only if it is ++ * not associated with a weight-raised queue. ++ */ ++ if (prev_weight != new_weight && ++ (bfqq ? bfqq->wr_coeff == 1 : 1)) ++ /* If we get here, root has been initialized. */ ++ bfq_weights_tree_add(bfqd, entity, root); ++ ++ new_st->wsum += entity->weight; ++ ++ if (new_st != old_st) ++ entity->start = new_st->vtime; ++ } ++ ++ return new_st; ++} ++ ++/** ++ * bfq_bfqq_served - update the scheduler status after selection for ++ * service. ++ * @bfqq: the queue being served. ++ * @served: bytes to transfer. ++ * ++ * NOTE: this can be optimized, as the timestamps of upper level entities ++ * are synchronized every time a new bfqq is selected for service. By now, ++ * we keep it to better check consistency. ++ */ ++static void bfq_bfqq_served(struct bfq_queue *bfqq, unsigned long served) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ struct bfq_service_tree *st; ++ ++ for_each_entity(entity) { ++ st = bfq_entity_service_tree(entity); ++ ++ entity->service += served; ++ BUG_ON(entity->service > entity->budget); ++ BUG_ON(st->wsum == 0); ++ ++ st->vtime += bfq_delta(served, st->wsum); ++ bfq_forget_idle(st); ++ } ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %lu secs", served); ++} ++ ++/** ++ * bfq_bfqq_charge_full_budget - set the service to the entity budget. ++ * @bfqq: the queue that needs a service update. ++ * ++ * When it's not possible to be fair in the service domain, because ++ * a queue is not consuming its budget fast enough (the meaning of ++ * fast depends on the timeout parameter), we charge it a full ++ * budget. In this way we should obtain a sort of time-domain ++ * fairness among all the seeky/slow queues. ++ */ ++static inline void bfq_bfqq_charge_full_budget(struct bfq_queue *bfqq) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "charge_full_budget"); ++ ++ bfq_bfqq_served(bfqq, entity->budget - entity->service); ++} ++ ++/** ++ * __bfq_activate_entity - activate an entity. ++ * @entity: the entity being activated. ++ * ++ * Called whenever an entity is activated, i.e., it is not active and one ++ * of its children receives a new request, or has to be reactivated due to ++ * budget exhaustion. It uses the current budget of the entity (and the ++ * service received if @entity is active) of the queue to calculate its ++ * timestamps. ++ */ ++static void __bfq_activate_entity(struct bfq_entity *entity) ++{ ++ struct bfq_sched_data *sd = entity->sched_data; ++ struct bfq_service_tree *st = bfq_entity_service_tree(entity); ++ ++ if (entity == sd->in_service_entity) { ++ BUG_ON(entity->tree != NULL); ++ /* ++ * If we are requeueing the current entity we have ++ * to take care of not charging to it service it has ++ * not received. ++ */ ++ bfq_calc_finish(entity, entity->service); ++ entity->start = entity->finish; ++ sd->in_service_entity = NULL; ++ } else if (entity->tree == &st->active) { ++ /* ++ * Requeueing an entity due to a change of some ++ * next_in_service entity below it. We reuse the ++ * old start time. ++ */ ++ bfq_active_extract(st, entity); ++ } else if (entity->tree == &st->idle) { ++ /* ++ * Must be on the idle tree, bfq_idle_extract() will ++ * check for that. ++ */ ++ bfq_idle_extract(st, entity); ++ entity->start = bfq_gt(st->vtime, entity->finish) ? ++ st->vtime : entity->finish; ++ } else { ++ /* ++ * The finish time of the entity may be invalid, and ++ * it is in the past for sure, otherwise the queue ++ * would have been on the idle tree. ++ */ ++ entity->start = st->vtime; ++ st->wsum += entity->weight; ++ bfq_get_entity(entity); ++ ++ BUG_ON(entity->on_st); ++ entity->on_st = 1; ++ } ++ ++ st = __bfq_entity_update_weight_prio(st, entity); ++ bfq_calc_finish(entity, entity->budget); ++ bfq_active_insert(st, entity); ++} ++ ++/** ++ * bfq_activate_entity - activate an entity and its ancestors if necessary. ++ * @entity: the entity to activate. ++ * ++ * Activate @entity and all the entities on the path from it to the root. ++ */ ++static void bfq_activate_entity(struct bfq_entity *entity) ++{ ++ struct bfq_sched_data *sd; ++ ++ for_each_entity(entity) { ++ __bfq_activate_entity(entity); ++ ++ sd = entity->sched_data; ++ if (!bfq_update_next_in_service(sd)) ++ /* ++ * No need to propagate the activation to the ++ * upper entities, as they will be updated when ++ * the in-service entity is rescheduled. ++ */ ++ break; ++ } ++} ++ ++/** ++ * __bfq_deactivate_entity - deactivate an entity from its service tree. ++ * @entity: the entity to deactivate. ++ * @requeue: if false, the entity will not be put into the idle tree. ++ * ++ * Deactivate an entity, independently from its previous state. If the ++ * entity was not on a service tree just return, otherwise if it is on ++ * any scheduler tree, extract it from that tree, and if necessary ++ * and if the caller did not specify @requeue, put it on the idle tree. ++ * ++ * Return %1 if the caller should update the entity hierarchy, i.e., ++ * if the entity was in service or if it was the next_in_service for ++ * its sched_data; return %0 otherwise. ++ */ ++static int __bfq_deactivate_entity(struct bfq_entity *entity, int requeue) ++{ ++ struct bfq_sched_data *sd = entity->sched_data; ++ struct bfq_service_tree *st = bfq_entity_service_tree(entity); ++ int was_in_service = entity == sd->in_service_entity; ++ int ret = 0; ++ ++ if (!entity->on_st) ++ return 0; ++ ++ BUG_ON(was_in_service && entity->tree != NULL); ++ ++ if (was_in_service) { ++ bfq_calc_finish(entity, entity->service); ++ sd->in_service_entity = NULL; ++ } else if (entity->tree == &st->active) ++ bfq_active_extract(st, entity); ++ else if (entity->tree == &st->idle) ++ bfq_idle_extract(st, entity); ++ else if (entity->tree != NULL) ++ BUG(); ++ ++ if (was_in_service || sd->next_in_service == entity) ++ ret = bfq_update_next_in_service(sd); ++ ++ if (!requeue || !bfq_gt(entity->finish, st->vtime)) ++ bfq_forget_entity(st, entity); ++ else ++ bfq_idle_insert(st, entity); ++ ++ BUG_ON(sd->in_service_entity == entity); ++ BUG_ON(sd->next_in_service == entity); ++ ++ return ret; ++} ++ ++/** ++ * bfq_deactivate_entity - deactivate an entity. ++ * @entity: the entity to deactivate. ++ * @requeue: true if the entity can be put on the idle tree ++ */ ++static void bfq_deactivate_entity(struct bfq_entity *entity, int requeue) ++{ ++ struct bfq_sched_data *sd; ++ struct bfq_entity *parent; ++ ++ for_each_entity_safe(entity, parent) { ++ sd = entity->sched_data; ++ ++ if (!__bfq_deactivate_entity(entity, requeue)) ++ /* ++ * The parent entity is still backlogged, and ++ * we don't need to update it as it is still ++ * in service. ++ */ ++ break; ++ ++ if (sd->next_in_service != NULL) ++ /* ++ * The parent entity is still backlogged and ++ * the budgets on the path towards the root ++ * need to be updated. ++ */ ++ goto update; ++ ++ /* ++ * If we reach there the parent is no more backlogged and ++ * we want to propagate the dequeue upwards. ++ */ ++ requeue = 1; ++ } ++ ++ return; ++ ++update: ++ entity = parent; ++ for_each_entity(entity) { ++ __bfq_activate_entity(entity); ++ ++ sd = entity->sched_data; ++ if (!bfq_update_next_in_service(sd)) ++ break; ++ } ++} ++ ++/** ++ * bfq_update_vtime - update vtime if necessary. ++ * @st: the service tree to act upon. ++ * ++ * If necessary update the service tree vtime to have at least one ++ * eligible entity, skipping to its start time. Assumes that the ++ * active tree of the device is not empty. ++ * ++ * NOTE: this hierarchical implementation updates vtimes quite often, ++ * we may end up with reactivated processes getting timestamps after a ++ * vtime skip done because we needed a ->first_active entity on some ++ * intermediate node. ++ */ ++static void bfq_update_vtime(struct bfq_service_tree *st) ++{ ++ struct bfq_entity *entry; ++ struct rb_node *node = st->active.rb_node; ++ ++ entry = rb_entry(node, struct bfq_entity, rb_node); ++ if (bfq_gt(entry->min_start, st->vtime)) { ++ st->vtime = entry->min_start; ++ bfq_forget_idle(st); ++ } ++} ++ ++/** ++ * bfq_first_active_entity - find the eligible entity with ++ * the smallest finish time ++ * @st: the service tree to select from. ++ * ++ * This function searches the first schedulable entity, starting from the ++ * root of the tree and going on the left every time on this side there is ++ * a subtree with at least one eligible (start >= vtime) entity. The path on ++ * the right is followed only if a) the left subtree contains no eligible ++ * entities and b) no eligible entity has been found yet. ++ */ ++static struct bfq_entity *bfq_first_active_entity(struct bfq_service_tree *st) ++{ ++ struct bfq_entity *entry, *first = NULL; ++ struct rb_node *node = st->active.rb_node; ++ ++ while (node != NULL) { ++ entry = rb_entry(node, struct bfq_entity, rb_node); ++left: ++ if (!bfq_gt(entry->start, st->vtime)) ++ first = entry; ++ ++ BUG_ON(bfq_gt(entry->min_start, st->vtime)); ++ ++ if (node->rb_left != NULL) { ++ entry = rb_entry(node->rb_left, ++ struct bfq_entity, rb_node); ++ if (!bfq_gt(entry->min_start, st->vtime)) { ++ node = node->rb_left; ++ goto left; ++ } ++ } ++ if (first != NULL) ++ break; ++ node = node->rb_right; ++ } ++ ++ BUG_ON(first == NULL && !RB_EMPTY_ROOT(&st->active)); ++ return first; ++} ++ ++/** ++ * __bfq_lookup_next_entity - return the first eligible entity in @st. ++ * @st: the service tree. ++ * ++ * Update the virtual time in @st and return the first eligible entity ++ * it contains. ++ */ ++static struct bfq_entity *__bfq_lookup_next_entity(struct bfq_service_tree *st, ++ bool force) ++{ ++ struct bfq_entity *entity, *new_next_in_service = NULL; ++ ++ if (RB_EMPTY_ROOT(&st->active)) ++ return NULL; ++ ++ bfq_update_vtime(st); ++ entity = bfq_first_active_entity(st); ++ BUG_ON(bfq_gt(entity->start, st->vtime)); ++ ++ /* ++ * If the chosen entity does not match with the sched_data's ++ * next_in_service and we are forcedly serving the IDLE priority ++ * class tree, bubble up budget update. ++ */ ++ if (unlikely(force && entity != entity->sched_data->next_in_service)) { ++ new_next_in_service = entity; ++ for_each_entity(new_next_in_service) ++ bfq_update_budget(new_next_in_service); ++ } ++ ++ return entity; ++} ++ ++/** ++ * bfq_lookup_next_entity - return the first eligible entity in @sd. ++ * @sd: the sched_data. ++ * @extract: if true the returned entity will be also extracted from @sd. ++ * ++ * NOTE: since we cache the next_in_service entity at each level of the ++ * hierarchy, the complexity of the lookup can be decreased with ++ * absolutely no effort just returning the cached next_in_service value; ++ * we prefer to do full lookups to test the consistency of * the data ++ * structures. ++ */ ++static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, ++ int extract, ++ struct bfq_data *bfqd) ++{ ++ struct bfq_service_tree *st = sd->service_tree; ++ struct bfq_entity *entity; ++ int i = 0; ++ ++ BUG_ON(sd->in_service_entity != NULL); ++ ++ if (bfqd != NULL && ++ jiffies - bfqd->bfq_class_idle_last_service > BFQ_CL_IDLE_TIMEOUT) { ++ entity = __bfq_lookup_next_entity(st + BFQ_IOPRIO_CLASSES - 1, ++ true); ++ if (entity != NULL) { ++ i = BFQ_IOPRIO_CLASSES - 1; ++ bfqd->bfq_class_idle_last_service = jiffies; ++ sd->next_in_service = entity; ++ } ++ } ++ for (; i < BFQ_IOPRIO_CLASSES; i++) { ++ entity = __bfq_lookup_next_entity(st + i, false); ++ if (entity != NULL) { ++ if (extract) { ++ bfq_check_next_in_service(sd, entity); ++ bfq_active_extract(st + i, entity); ++ sd->in_service_entity = entity; ++ sd->next_in_service = NULL; ++ } ++ break; ++ } ++ } ++ ++ return entity; ++} ++ ++/* ++ * Get next queue for service. ++ */ ++static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) ++{ ++ struct bfq_entity *entity = NULL; ++ struct bfq_sched_data *sd; ++ struct bfq_queue *bfqq; ++ ++ BUG_ON(bfqd->in_service_queue != NULL); ++ ++ if (bfqd->busy_queues == 0) ++ return NULL; ++ ++ sd = &bfqd->root_group->sched_data; ++ for (; sd != NULL; sd = entity->my_sched_data) { ++ entity = bfq_lookup_next_entity(sd, 1, bfqd); ++ BUG_ON(entity == NULL); ++ entity->service = 0; ++ } ++ ++ bfqq = bfq_entity_to_bfqq(entity); ++ BUG_ON(bfqq == NULL); ++ ++ return bfqq; ++} ++ ++static void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd) ++{ ++ if (bfqd->in_service_bic != NULL) { ++ put_io_context(bfqd->in_service_bic->icq.ioc); ++ bfqd->in_service_bic = NULL; ++ } ++ ++ bfqd->in_service_queue = NULL; ++ del_timer(&bfqd->idle_slice_timer); ++} ++ ++static void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ int requeue) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ ++ if (bfqq == bfqd->in_service_queue) ++ __bfq_bfqd_reset_in_service(bfqd); ++ ++ bfq_deactivate_entity(entity, requeue); ++} ++ ++static void bfq_activate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ ++ bfq_activate_entity(entity); ++} ++ ++/* ++ * Called when the bfqq no longer has requests pending, remove it from ++ * the service tree. ++ */ ++static void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ int requeue) ++{ ++ BUG_ON(!bfq_bfqq_busy(bfqq)); ++ BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); ++ ++ bfq_log_bfqq(bfqd, bfqq, "del from busy"); ++ ++ bfq_clear_bfqq_busy(bfqq); ++ ++ BUG_ON(bfqd->busy_queues == 0); ++ bfqd->busy_queues--; ++ ++ if (!bfqq->dispatched) { ++ bfq_weights_tree_remove(bfqd, &bfqq->entity, ++ &bfqd->queue_weights_tree); ++ if (!blk_queue_nonrot(bfqd->queue)) { ++ BUG_ON(!bfqd->busy_in_flight_queues); ++ bfqd->busy_in_flight_queues--; ++ if (bfq_bfqq_constantly_seeky(bfqq)) { ++ BUG_ON(!bfqd-> ++ const_seeky_busy_in_flight_queues); ++ bfqd->const_seeky_busy_in_flight_queues--; ++ } ++ } ++ } ++ if (bfqq->wr_coeff > 1) ++ bfqd->wr_busy_queues--; ++ ++ bfq_deactivate_bfqq(bfqd, bfqq, requeue); ++} ++ ++/* ++ * Called when an inactive queue receives a new request. ++ */ ++static void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq) ++{ ++ BUG_ON(bfq_bfqq_busy(bfqq)); ++ BUG_ON(bfqq == bfqd->in_service_queue); ++ ++ bfq_log_bfqq(bfqd, bfqq, "add to busy"); ++ ++ bfq_activate_bfqq(bfqd, bfqq); ++ ++ bfq_mark_bfqq_busy(bfqq); ++ bfqd->busy_queues++; ++ ++ if (!bfqq->dispatched) { ++ if (bfqq->wr_coeff == 1) ++ bfq_weights_tree_add(bfqd, &bfqq->entity, ++ &bfqd->queue_weights_tree); ++ if (!blk_queue_nonrot(bfqd->queue)) { ++ bfqd->busy_in_flight_queues++; ++ if (bfq_bfqq_constantly_seeky(bfqq)) ++ bfqd->const_seeky_busy_in_flight_queues++; ++ } ++ } ++ if (bfqq->wr_coeff > 1) ++ bfqd->wr_busy_queues++; ++} +diff -Nur linux-3.14.36/block/blk-core.c linux-openelec/block/blk-core.c +--- linux-3.14.36/block/blk-core.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/block/blk-core.c 2015-05-06 12:05:43.000000000 -0500 +@@ -1928,7 +1928,7 @@ + * in some cases below, so export this function. + * Request stacking drivers like request-based dm may change the queue + * limits while requests are in the queue (e.g. dm's table swapping). +- * Such request stacking drivers should check those requests agaist ++ * Such request stacking drivers should check those requests against + * the new queue limits again when they dispatch those requests, + * although such checkings are also done against the old queue limits + * when submitting requests. +diff -Nur linux-3.14.36/block/blk-map.c linux-openelec/block/blk-map.c +--- linux-3.14.36/block/blk-map.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/block/blk-map.c 2015-05-06 12:05:43.000000000 -0500 +@@ -285,7 +285,7 @@ + * + * Description: + * Data will be mapped directly if possible. Otherwise a bounce +- * buffer is used. Can be called multple times to append multple ++ * buffer is used. Can be called multiple times to append multiple + * buffers. + */ + int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, +diff -Nur linux-3.14.36/block/Kconfig.iosched linux-openelec/block/Kconfig.iosched +--- linux-3.14.36/block/Kconfig.iosched 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/block/Kconfig.iosched 2015-05-06 12:05:43.000000000 -0500 +@@ -39,6 +39,27 @@ + ---help--- + Enable group IO scheduling in CFQ. + ++config IOSCHED_BFQ ++ tristate "BFQ I/O scheduler" ++ default n ++ ---help--- ++ The BFQ I/O scheduler tries to distribute bandwidth among ++ all processes according to their weights. ++ It aims at distributing the bandwidth as desired, independently of ++ the disk parameters and with any workload. It also tries to ++ guarantee low latency to interactive and soft real-time ++ applications. If compiled built-in (saying Y here), BFQ can ++ be configured to support hierarchical scheduling. ++ ++config CGROUP_BFQIO ++ bool "BFQ hierarchical scheduling support" ++ depends on CGROUPS && IOSCHED_BFQ=y ++ default n ++ ---help--- ++ Enable hierarchical scheduling in BFQ, using the cgroups ++ filesystem interface. The name of the subsystem will be ++ bfqio. ++ + choice + prompt "Default I/O scheduler" + default DEFAULT_CFQ +@@ -52,6 +73,16 @@ + config DEFAULT_CFQ + bool "CFQ" if IOSCHED_CFQ=y + ++ config DEFAULT_BFQ ++ bool "BFQ" if IOSCHED_BFQ=y ++ help ++ Selects BFQ as the default I/O scheduler which will be ++ used by default for all block devices. ++ The BFQ I/O scheduler aims at distributing the bandwidth ++ as desired, independently of the disk parameters and with ++ any workload. It also tries to guarantee low latency to ++ interactive and soft real-time applications. ++ + config DEFAULT_NOOP + bool "No-op" + +@@ -61,6 +92,7 @@ + string + default "deadline" if DEFAULT_DEADLINE + default "cfq" if DEFAULT_CFQ ++ default "bfq" if DEFAULT_BFQ + default "noop" if DEFAULT_NOOP + + endmenu +diff -Nur linux-3.14.36/block/Makefile linux-openelec/block/Makefile +--- linux-3.14.36/block/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/block/Makefile 2015-05-06 12:05:43.000000000 -0500 +@@ -16,6 +16,7 @@ + obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o + obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o + obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o ++obj-$(CONFIG_IOSCHED_BFQ) += bfq-iosched.o + + obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o + obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o +diff -Nur linux-3.14.36/crypto/blkcipher.c linux-openelec/crypto/blkcipher.c +--- linux-3.14.36/crypto/blkcipher.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/crypto/blkcipher.c 2015-05-06 12:05:43.000000000 -0500 +@@ -70,14 +70,12 @@ + return max(start, end_page); + } + +-static inline unsigned int blkcipher_done_slow(struct crypto_blkcipher *tfm, +- struct blkcipher_walk *walk, ++static inline unsigned int blkcipher_done_slow(struct blkcipher_walk *walk, + unsigned int bsize) + { + u8 *addr; +- unsigned int alignmask = crypto_blkcipher_alignmask(tfm); + +- addr = (u8 *)ALIGN((unsigned long)walk->buffer, alignmask + 1); ++ addr = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1); + addr = blkcipher_get_spot(addr, bsize); + scatterwalk_copychunks(addr, &walk->out, bsize, 1); + return bsize; +@@ -105,7 +103,6 @@ + int blkcipher_walk_done(struct blkcipher_desc *desc, + struct blkcipher_walk *walk, int err) + { +- struct crypto_blkcipher *tfm = desc->tfm; + unsigned int nbytes = 0; + + if (likely(err >= 0)) { +@@ -117,7 +114,7 @@ + err = -EINVAL; + goto err; + } else +- n = blkcipher_done_slow(tfm, walk, n); ++ n = blkcipher_done_slow(walk, n); + + nbytes = walk->total - n; + err = 0; +@@ -136,7 +133,7 @@ + } + + if (walk->iv != desc->info) +- memcpy(desc->info, walk->iv, crypto_blkcipher_ivsize(tfm)); ++ memcpy(desc->info, walk->iv, walk->ivsize); + if (walk->buffer != walk->page) + kfree(walk->buffer); + if (walk->page) +@@ -226,22 +223,20 @@ + static int blkcipher_walk_next(struct blkcipher_desc *desc, + struct blkcipher_walk *walk) + { +- struct crypto_blkcipher *tfm = desc->tfm; +- unsigned int alignmask = crypto_blkcipher_alignmask(tfm); + unsigned int bsize; + unsigned int n; + int err; + + n = walk->total; +- if (unlikely(n < crypto_blkcipher_blocksize(tfm))) { ++ if (unlikely(n < walk->cipher_blocksize)) { + desc->flags |= CRYPTO_TFM_RES_BAD_BLOCK_LEN; + return blkcipher_walk_done(desc, walk, -EINVAL); + } + + walk->flags &= ~(BLKCIPHER_WALK_SLOW | BLKCIPHER_WALK_COPY | + BLKCIPHER_WALK_DIFF); +- if (!scatterwalk_aligned(&walk->in, alignmask) || +- !scatterwalk_aligned(&walk->out, alignmask)) { ++ if (!scatterwalk_aligned(&walk->in, walk->alignmask) || ++ !scatterwalk_aligned(&walk->out, walk->alignmask)) { + walk->flags |= BLKCIPHER_WALK_COPY; + if (!walk->page) { + walk->page = (void *)__get_free_page(GFP_ATOMIC); +@@ -250,12 +245,12 @@ + } + } + +- bsize = min(walk->blocksize, n); ++ bsize = min(walk->walk_blocksize, n); + n = scatterwalk_clamp(&walk->in, n); + n = scatterwalk_clamp(&walk->out, n); + + if (unlikely(n < bsize)) { +- err = blkcipher_next_slow(desc, walk, bsize, alignmask); ++ err = blkcipher_next_slow(desc, walk, bsize, walk->alignmask); + goto set_phys_lowmem; + } + +@@ -277,28 +272,26 @@ + return err; + } + +-static inline int blkcipher_copy_iv(struct blkcipher_walk *walk, +- struct crypto_blkcipher *tfm, +- unsigned int alignmask) +-{ +- unsigned bs = walk->blocksize; +- unsigned int ivsize = crypto_blkcipher_ivsize(tfm); +- unsigned aligned_bs = ALIGN(bs, alignmask + 1); +- unsigned int size = aligned_bs * 2 + ivsize + max(aligned_bs, ivsize) - +- (alignmask + 1); ++static inline int blkcipher_copy_iv(struct blkcipher_walk *walk) ++{ ++ unsigned bs = walk->walk_blocksize; ++ unsigned aligned_bs = ALIGN(bs, walk->alignmask + 1); ++ unsigned int size = aligned_bs * 2 + ++ walk->ivsize + max(aligned_bs, walk->ivsize) - ++ (walk->alignmask + 1); + u8 *iv; + +- size += alignmask & ~(crypto_tfm_ctx_alignment() - 1); ++ size += walk->alignmask & ~(crypto_tfm_ctx_alignment() - 1); + walk->buffer = kmalloc(size, GFP_ATOMIC); + if (!walk->buffer) + return -ENOMEM; + +- iv = (u8 *)ALIGN((unsigned long)walk->buffer, alignmask + 1); ++ iv = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1); + iv = blkcipher_get_spot(iv, bs) + aligned_bs; + iv = blkcipher_get_spot(iv, bs) + aligned_bs; +- iv = blkcipher_get_spot(iv, ivsize); ++ iv = blkcipher_get_spot(iv, walk->ivsize); + +- walk->iv = memcpy(iv, walk->iv, ivsize); ++ walk->iv = memcpy(iv, walk->iv, walk->ivsize); + return 0; + } + +@@ -306,7 +299,10 @@ + struct blkcipher_walk *walk) + { + walk->flags &= ~BLKCIPHER_WALK_PHYS; +- walk->blocksize = crypto_blkcipher_blocksize(desc->tfm); ++ walk->walk_blocksize = crypto_blkcipher_blocksize(desc->tfm); ++ walk->cipher_blocksize = walk->walk_blocksize; ++ walk->ivsize = crypto_blkcipher_ivsize(desc->tfm); ++ walk->alignmask = crypto_blkcipher_alignmask(desc->tfm); + return blkcipher_walk_first(desc, walk); + } + EXPORT_SYMBOL_GPL(blkcipher_walk_virt); +@@ -315,7 +311,10 @@ + struct blkcipher_walk *walk) + { + walk->flags |= BLKCIPHER_WALK_PHYS; +- walk->blocksize = crypto_blkcipher_blocksize(desc->tfm); ++ walk->walk_blocksize = crypto_blkcipher_blocksize(desc->tfm); ++ walk->cipher_blocksize = walk->walk_blocksize; ++ walk->ivsize = crypto_blkcipher_ivsize(desc->tfm); ++ walk->alignmask = crypto_blkcipher_alignmask(desc->tfm); + return blkcipher_walk_first(desc, walk); + } + EXPORT_SYMBOL_GPL(blkcipher_walk_phys); +@@ -323,9 +322,6 @@ + static int blkcipher_walk_first(struct blkcipher_desc *desc, + struct blkcipher_walk *walk) + { +- struct crypto_blkcipher *tfm = desc->tfm; +- unsigned int alignmask = crypto_blkcipher_alignmask(tfm); +- + if (WARN_ON_ONCE(in_irq())) + return -EDEADLK; + +@@ -335,8 +331,8 @@ + + walk->buffer = NULL; + walk->iv = desc->info; +- if (unlikely(((unsigned long)walk->iv & alignmask))) { +- int err = blkcipher_copy_iv(walk, tfm, alignmask); ++ if (unlikely(((unsigned long)walk->iv & walk->alignmask))) { ++ int err = blkcipher_copy_iv(walk); + if (err) + return err; + } +@@ -353,11 +349,28 @@ + unsigned int blocksize) + { + walk->flags &= ~BLKCIPHER_WALK_PHYS; +- walk->blocksize = blocksize; ++ walk->walk_blocksize = blocksize; ++ walk->cipher_blocksize = crypto_blkcipher_blocksize(desc->tfm); ++ walk->ivsize = crypto_blkcipher_ivsize(desc->tfm); ++ walk->alignmask = crypto_blkcipher_alignmask(desc->tfm); + return blkcipher_walk_first(desc, walk); + } + EXPORT_SYMBOL_GPL(blkcipher_walk_virt_block); + ++int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc, ++ struct blkcipher_walk *walk, ++ struct crypto_aead *tfm, ++ unsigned int blocksize) ++{ ++ walk->flags &= ~BLKCIPHER_WALK_PHYS; ++ walk->walk_blocksize = blocksize; ++ walk->cipher_blocksize = crypto_aead_blocksize(tfm); ++ walk->ivsize = crypto_aead_ivsize(tfm); ++ walk->alignmask = crypto_aead_alignmask(tfm); ++ return blkcipher_walk_first(desc, walk); ++} ++EXPORT_SYMBOL_GPL(blkcipher_aead_walk_virt_block); ++ + static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) + { +diff -Nur linux-3.14.36/crypto/tcrypt.c linux-openelec/crypto/tcrypt.c +--- linux-3.14.36/crypto/tcrypt.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/crypto/tcrypt.c 2015-05-06 12:05:43.000000000 -0500 +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include "tcrypt.h" + #include "internal.h" + +@@ -447,6 +448,7 @@ + goto out; + } + ++ schedule(); + printk("test %u (%d bit key, %d byte blocks): ", i, + *keysize * 8, *b_size); + +@@ -713,6 +715,7 @@ + if (speed[i].klen) + crypto_hash_setkey(tfm, tvmem[0], speed[i].klen); + ++ schedule(); + printk(KERN_INFO "test%3u " + "(%5u byte blocks,%5u bytes per update,%4u updates): ", + i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen); +@@ -953,6 +956,7 @@ + break; + } + ++ schedule(); + pr_info("test%3u " + "(%5u byte blocks,%5u bytes per update,%4u updates): ", + i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen); +@@ -1118,6 +1122,7 @@ + goto out_free_req; + } + ++ schedule(); + pr_info("test %u (%d bit key, %d byte blocks): ", i, + *keysize * 8, *b_size); + +@@ -1199,6 +1204,7 @@ + printk("alg %s ", *name); + printk(crypto_has_alg(*name, 0, 0) ? + "found\n" : "not found\n"); ++ schedule(); + name++; + } + } +diff -Nur linux-3.14.36/Documentation/ABI/testing/sysfs-class-net-statistics linux-openelec/Documentation/ABI/testing/sysfs-class-net-statistics +--- linux-3.14.36/Documentation/ABI/testing/sysfs-class-net-statistics 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/ABI/testing/sysfs-class-net-statistics 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,201 @@ ++What: /sys/class//statistics/collisions ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of collisions seen by this network device. ++ This value might not be relevant with all MAC layers. ++ ++What: /sys/class//statistics/multicast ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of multicast packets received by this ++ network device. ++ ++What: /sys/class//statistics/rx_bytes ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of bytes received by this network device. ++ See the network driver for the exact meaning of when this ++ value is incremented. ++ ++What: /sys/class//statistics/rx_compressed ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of compressed packets received by this ++ network device. This value might only be relevant for interfaces ++ that support packet compression (e.g: PPP). ++ ++What: /sys/class//statistics/rx_crc_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets received with a CRC (FCS) error ++ by this network device. Note that the specific meaning might ++ depend on the MAC layer used by the interface. ++ ++What: /sys/class//statistics/rx_dropped ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets received by the network device ++ but dropped, that are not forwarded to the upper layers for ++ packet processing. See the network driver for the exact ++ meaning of this value. ++ ++What: /sys/class//statistics/rx_fifo_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of receive FIFO errors seen by this ++ network device. See the network driver for the exact ++ meaning of this value. ++ ++What: /sys/class//statistics/rx_frame_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of received frames with error, such as ++ alignment errors. Note that the specific meaning depends on ++ on the MAC layer protocol used. See the network driver for ++ the exact meaning of this value. ++ ++What: /sys/class//statistics/rx_length_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of received error packet with a length ++ error, oversized or undersized. See the network driver for the ++ exact meaning of this value. ++ ++What: /sys/class//statistics/rx_missed_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of received packets that have been missed ++ due to lack of capacity in the receive side. See the network ++ driver for the exact meaning of this value. ++ ++What: /sys/class//statistics/rx_over_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of received packets that are oversized ++ compared to what the network device is configured to accept ++ (e.g: larger than MTU). See the network driver for the exact ++ meaning of this value. ++ ++What: /sys/class//statistics/rx_packets ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the total number of good packets received by this ++ network device. ++ ++What: /sys/class//statistics/tx_aborted_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets that have been aborted ++ during transmission by a network device (e.g: because of ++ a medium collision). See the network driver for the exact ++ meaning of this value. ++ ++What: /sys/class//statistics/tx_bytes ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of bytes transmitted by a network ++ device. See the network driver for the exact meaning of this ++ value, in particular whether this accounts for all successfully ++ transmitted packets or all packets that have been queued for ++ transmission. ++ ++What: /sys/class//statistics/tx_carrier_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets that could not be transmitted ++ because of carrier errors (e.g: physical link down). See the ++ network driver for the exact meaning of this value. ++ ++What: /sys/class//statistics/tx_compressed ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of transmitted compressed packets. Note ++ this might only be relevant for devices that support ++ compression (e.g: PPP). ++ ++What: /sys/class//statistics/tx_dropped ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets dropped during transmission. ++ See the driver for the exact reasons as to why the packets were ++ dropped. ++ ++What: /sys/class//statistics/tx_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets in error during transmission by ++ a network device. See the driver for the exact reasons as to ++ why the packets were dropped. ++ ++What: /sys/class//statistics/tx_fifo_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets having caused a transmit ++ FIFO error. See the driver for the exact reasons as to why the ++ packets were dropped. ++ ++What: /sys/class//statistics/tx_heartbeat_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets transmitted that have been ++ reported as heartbeat errors. See the driver for the exact ++ reasons as to why the packets were dropped. ++ ++What: /sys/class//statistics/tx_packets ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets transmitted by a network ++ device. See the driver for whether this reports the number of all ++ attempted or successful transmissions. ++ ++What: /sys/class//statistics/tx_window_errors ++Date: April 2005 ++KernelVersion: 2.6.12 ++Contact: netdev@vger.kernel.org ++Description: ++ Indicates the number of packets not successfully transmitted ++ due to a window collision. The specific meaning depends on the ++ MAC layer used. On Ethernet this is usually used to report ++ late collisions errors. +diff -Nur linux-3.14.36/Documentation/arm64/booting.txt linux-openelec/Documentation/arm64/booting.txt +--- linux-3.14.36/Documentation/arm64/booting.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/arm64/booting.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -111,8 +111,14 @@ + - Caches, MMUs + The MMU must be off. + Instruction cache may be on or off. +- Data cache must be off and invalidated. +- External caches (if present) must be configured and disabled. ++ The address range corresponding to the loaded kernel image must be ++ cleaned to the PoC. In the presence of a system cache or other ++ coherent masters with caches enabled, this will typically require ++ cache maintenance by VA rather than set/way operations. ++ System caches which respect the architected cache maintenance by VA ++ operations must be configured and may be enabled. ++ System caches which do not respect architected cache maintenance by VA ++ operations (not recommended) must be configured and disabled. + + - Architected timers + CNTFRQ must be programmed with the timer frequency and CNTVOFF must +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt linux-openelec/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt +--- linux-3.14.36/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,64 @@ ++Freescale Busfreq driver ++ ++It is a generic driver that manages the frequency of the DDR, AHB and AXI buses in the iMX6x architecture. ++It works for both SMP and UP systems and for both DDR3 and LPDDR2 memory types. ++ ++Required properties are listed below: ++- compatible: should be "fsl,imx6_busfreq" ++- clocks: Lists the various clocks used by the busfreq driver ++- interrupts - Lists the interrupts used by the busfreq driver. This is needed only for SMP architecutre. ++- fsl,max_ddr_freq - The max ddr freq for this chip ++ ++Examples: ++For SOC imx6q.dtsi: ++ busfreq { /* BUSFREQ */ ++ compatible = "fsl,imx6_busfreq"; ++ clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>, ++ <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>; ++ clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", ++ "periph_pre", "periph_clk2", "periph_clk2_sel", "osc"; ++ interrupts = <0 107 0x04>, <0 112 0x4>, <0 113 0x4>, <0 114 0x4>; ++ interrupt-names = "irq_busfreq_0", "irq_busfreq_1", "irq_busfreq_2", "irq_busfreq_3"; ++ fsl,max_ddr_freq = <528000000>; ++ }; ++ ++The Freescale Busfreq driver supports the following setpoints for the DDR freq: ++enum bus_freq_mode { ++ BUS_FREQ_HIGH, -> The max freq the SOC supports ++ BUS_FREQ_MED, -> Medium setpoint (ex 400MHz for DDR3 when the max is 528MHz) ++ BUS_FREQ_AUDIO, -> Audio playback freq (50MHz) ++ BUS_FREQ_LOW, -> Low power IDLE freq (24MHz) ++}; ++ ++Currently the Freescale Busfreq driver implementation requires drivers to call the following APIs: ++1. request_bus_freq(enum bus_freq_mode): ++ The driver is requesting the system and ddr freq to be set to the requested value. The driver should call this ++ API before it even enables its clocks. ++ ++2. release_bus_freq(enum bus_freq_mode): ++ The driver no longer needs the system and ddr freq at the required value. The driver should call this API after ++ its work is done and it has disabled its clocks. ++ ++Examples: ++In the IPU driver, the requesting and releasing of the required bus frequency is tied into the runtime PM implementation: ++ ++int ipu_runtime_suspend(struct device *dev) ++{ ++ release_bus_freq(BUS_FREQ_HIGH); ++ dev_dbg(dev, "ipu busfreq high release.\n"); ++ ++ return 0; ++} ++ ++int ipu_runtime_resume(struct device *dev) ++{ ++ request_bus_freq(BUS_FREQ_HIGH); ++ dev_dbg(dev, "ipu busfreq high requst.\n"); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops ipu_pm_ops = { ++ SET_RUNTIME_PM_OPS(ipu_runtime_suspend, ipu_runtime_resume, NULL) ++ SET_SYSTEM_SLEEP_PM_OPS(ipu_suspend, ipu_resume) ++}; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/arm/imx/gpc.txt linux-openelec/Documentation/devicetree/bindings/arm/imx/gpc.txt +--- linux-3.14.36/Documentation/devicetree/bindings/arm/imx/gpc.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/arm/imx/gpc.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,20 @@ ++Freescale imx GPC bindings ++ ++Optional properties: ++- fsl,cpu_pupscr_sw2iso: for powering up CPU, number of 32K clock cycle PGC will wait before negating isolation signal. ++- fsl,cpu_pupscr_sw: for powering up CPU, number of 32K clock cycle PGC will wait before asserting isolation signal. ++- fsl,cpu_pdnscr_iso2sw: for powering down CPU, number of ipg clock cycle PGC will wait before negating isolation signal. ++- fsl,cpu_pdnscr_iso: for powering down CPU, number of ipg clock cycle PGC will wait before asserting isolation signal. ++ ++These properties are for adjusting the GPC PGC CPU power up/down setting, if there is no such property in dts, then default ++value in GPC PGC registers will be used. ++ ++ ++Example: ++ ++ &gpc { ++ fsl,cpu_pupscr_sw2iso = <0xf>; ++ fsl,cpu_pupscr_sw = <0xf>; ++ fsl,cpu_pdnscr_iso2sw = <0x1>; ++ fsl,cpu_pdnscr_iso = <0x1>; ++ }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/arm/pmu.txt linux-openelec/Documentation/devicetree/bindings/arm/pmu.txt +--- linux-3.14.36/Documentation/devicetree/bindings/arm/pmu.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/arm/pmu.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -17,6 +17,9 @@ + "arm,arm1176-pmu" + "arm,arm1136-pmu" + - interrupts : 1 combined interrupt or 1 per core. ++- cluster : a phandle to the cluster to which it belongs ++ If there are more than one cluster with same CPU type ++ then there should be separate PMU nodes per cluster. + + Example: + +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/ata/ahci-platform.txt linux-openelec/Documentation/devicetree/bindings/ata/ahci-platform.txt +--- linux-3.14.36/Documentation/devicetree/bindings/ata/ahci-platform.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/ata/ahci-platform.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -4,12 +4,19 @@ + Each SATA controller should have its own node. + + Required properties: +-- compatible : compatible list, contains "snps,spear-ahci" ++- compatible : compatible list, contains "snps,spear-ahci", ++ "fsl,imx53-ahci" or "fsl,imx6q-ahci" + - interrupts : + - reg : + + Optional properties: + - dma-coherent : Present if dma operations are coherent ++- clocks : a list of phandle + clock specifier pairs ++- target-supply : regulator for SATA target power ++ ++"fsl,imx53-ahci", "fsl,imx6q-ahci" required properties: ++- clocks : must contain the sata, sata_ref and ahb clocks ++- clock-names : must contain "ahb" for the ahb clock + + Example: + sata@ffe08000 { +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/clock/imx6q-clock.txt linux-openelec/Documentation/devicetree/bindings/clock/imx6q-clock.txt +--- linux-3.14.36/Documentation/devicetree/bindings/clock/imx6q-clock.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/clock/imx6q-clock.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -89,8 +89,6 @@ + gpu3d_shader 74 + ipu1_podf 75 + ipu2_podf 76 +- ldb_di0_podf 77 +- ldb_di1_podf 78 + ipu1_di0_pre 79 + ipu1_di1_pre 80 + ipu2_di0_pre 81 +@@ -220,6 +218,20 @@ + lvds2_sel 205 + lvds1_gate 206 + lvds2_gate 207 ++ gpt_3m 208 ++ video_27m 209 ++ ldb_di0_div_7 210 ++ ldb_di1_div_7 211 ++ ldb_di0_div_sel 212 ++ ldb_di1_div_sel 213 ++ caam_mem 214 ++ caam_aclk 215 ++ caam_ipg 216 ++ epit1 217 ++ epit2 218 ++ tzasc2 219 ++ lvds1_in 220 ++ lvds1_out 221 + + Examples: + +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt linux-openelec/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt +--- linux-3.14.36/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -47,6 +47,7 @@ + 20 ASRC + 21 ESAI + 22 SSI Dual FIFO (needs firmware ver >= 2) ++ 23 HDMI Audio + + The third cell specifies the transfer priority as below. + +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt linux-openelec/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt +--- linux-3.14.36/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,146 @@ ++* FSL IPUv3 Display/FB ++ ++The FSL IPUv3 is Image Processing Unit version 3, a part of video and graphics ++subsystem in an application processor. The goal of the IPU is to provide ++comprehensive support for the flow of data from an image sensor or/and to a ++display device. ++ ++Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC. ++Each IPU unit has two display interfaces. ++ ++For LDB/LVDS panel, there are two LVDS channels(LVDS0 and LVDS1) which can ++transfer video data, these two channels can be used as ++split/dual/single/separate mode. ++-split mode means display data from DI0 or DI1 will send to both channels ++ LVDS0+LVDS1. ++-dual mode means display data from DI0 or DI1 will be duplicated on LVDS0 ++ and LVDS1, it said, LVDS0 and LVDS1 has the same content. ++-single mode means only work for DI0/DI1->LVDS0 or DI0/DI1->LVDS1. ++-separate mode means you can make DI0/DI1->LVDS0 and DI0/DI1->LVDS1 work ++ at the same time. ++ "ldb=spl0/1" -- split mode on DI0/1 ++ "ldb=dul0/1" -- dual mode on DI0/1 ++ "ldb=sin0/1" -- single mode on LVDS0/1 ++ "ldb=sep0/1" -- separate mode begin from LVDS0/1 ++ ++Required properties for IPU: ++- bypass_reset :Bypass reset to avoid display channel being. ++ stopped by probe since it may start to work in bootloader: 0 or 1. ++- compatible : should be "fsl,imx6q-ipu". ++- reg : the register address range. ++- interrupts : the error and sync interrupts request. ++- clocks : the clock sources that it depends on. ++- clock-names: the related clock names. ++- resets : IPU reset specifier. See reset.txt and fsl,imx-src.txt in ++ Documentation/devicetree/bindings/reset/ for details. ++ ++Required properties for fb: ++- compatible : should be "fsl,mxc_sdc_fb". ++- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi". ++- mode_str : video mode string: "LDB-XGA" or "LDB-1080P60" for ldb, ++ "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel, ++ "1920x1080M@60" for hdmi. ++- default_bpp : default bits per pixel: 8/16/24/32 ++- int_clk : use internal clock as pixel clock: 0 or 1 ++- late_init : to avoid display channel being re-initialized ++ as we've probably setup the channel in bootloader: 0 or 1 ++- interface_pix_fmt : display interface pixel format as below: ++ RGB666 IPU_PIX_FMT_RGB666 ++ RGB565 IPU_PIX_FMT_RGB565 ++ RGB24 IPU_PIX_FMT_RGB24 ++ BGR24 IPU_PIX_FMT_BGR24 ++ GBR24 IPU_PIX_FMT_GBR24 ++ YUV444 IPU_PIX_FMT_YUV444 ++ LVDS666 IPU_PIX_FMT_LVDS666 ++ YUYV IPU_PIX_FMT_YUYV ++ UYVY IPU_PIX_FMT_UYVY ++ YVYV IPU_PIX_FMT_YVYU ++ VYUY IPU_PIX_FMT_VYUY ++ ++Required properties for display: ++- compatible : should be "fsl,lcd" for lcd panel, "fsl,imx6q-ldb" for ldb ++- reg : the register address range if necessary to have. ++- interrupts : the error and sync interrupts if necessary to have. ++- clocks : the clock sources that it depends on if necessary to have. ++- clock-names: the related clock names if necessary to have. ++- ipu_id : ipu id for the first display device: 0 or 1 ++- disp_id : display interface id for the first display interface: 0 or 1 ++- default_ifmt : save as above display interface pixel format for lcd ++- pinctrl-names : should be "default" ++- pinctrl-0 : should be pinctrl_ipu1_1 or pinctrl_ipu2_1, which depends on the ++ IPU connected. ++- sec_ipu_id : secondary ipu id for the second display device(ldb only): 0 or 1 ++- sec_disp_id : secondary display interface id for the second display ++ device(ldb only): 0 or 1 ++- ext_ref : reference resistor select for ldb only: 0 or 1 ++- mode : ldb mode as below: ++ spl0 LDB_SPL_DI0 ++ spl1 LDB_SPL_DI1 ++ dul0 LDB_DUL_DI0 ++ dul1 LDB_DUL_DI1 ++ sin0 LDB_SIN0 ++ sin1 LDB_SIN1 ++ sep0 LDB_SEP0 ++ sep1 LDB_SEP1 ++- gpr : the mux controller for the display engine's display interfaces and the display encoder ++ (only valid for mipi dsi now). ++- disp-power-on-supply : the regulator to control display panel's power. ++ (only valid for mipi dsi now). ++- resets : the gpio pin to reset the display device(only valid for mipi display panel now). ++- lcd_panel : the video mode name for the display device(only valid for mipi display panel now). ++- dev_id : the display engine's identity within the system, which intends to replace ipu_id ++ (only valid for mipi dsi now). ++ ++Example for IPU: ++ ipu1: ipu@02400000 { ++ compatible = "fsl,imx6q-ipu"; ++ reg = <0x02400000 0x400000>; ++ interrupts = <0 6 0x4 0 5 0x4>; ++ clocks = <&clks 130>, <&clks 131>, <&clks 132>, ++ <&clks 39>, <&clks 40>, ++ <&clks 135>, <&clks 136>; ++ clock-names = "bus", "di0", "di1", ++ "di0_sel", "di1_sel", ++ "ldb_di0", "ldb_di1"; ++ resets = <&src 2>; ++ bypass_reset = <0>; ++ }; ++ ++Example for fb: ++ fb0 { ++ compatible = "fsl,mxc_sdc_fb"; ++ disp_dev = "ldb"; ++ interface_pix_fmt = "RGB666"; ++ mode_str ="LDB-XGA"; ++ default_bpp = <16>; ++ int_clk = <0>; ++ late_init = <0>; ++ status = "okay"; ++ }; ++ ++Example for ldb display: ++ ldb@020e0000 { ++ ipu_id = <1>; ++ disp_id = <0>; ++ ext_ref = <1>; ++ mode = "sep0"; ++ sec_ipu_id = <1>; ++ sec_disp_id = <1>; ++ status = "okay"; ++ }; ++ ++Example for mipi dsi display: ++ mipi_dsi: mipi@021e0000 { ++ compatible = "fsl,imx6q-mipi-dsi"; ++ reg = <0x021e0000 0x4000>; ++ interrupts = <0 102 0x04>; ++ gpr = <&gpr>; ++ clocks = <&clks 138>, <&clks 204>; ++ clock-names = "mipi_pllref_clk", "mipi_cfg_clk"; ++ dev_id = <0>; ++ disp_id = <0>; ++ lcd_panel = "TRULY-WVGA"; ++ disp-power-on-supply = <®_mipi_dsi_pwr_on> ++ resets = <&mipi_dsi_reset>; ++ status = "okay"; ++ }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/leds/leds-pwm.txt linux-openelec/Documentation/devicetree/bindings/leds/leds-pwm.txt +--- linux-3.14.36/Documentation/devicetree/bindings/leds/leds-pwm.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/leds/leds-pwm.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -13,6 +13,8 @@ + For the pwms and pwm-names property please refer to: + Documentation/devicetree/bindings/pwm/pwm.txt + - max-brightness : Maximum brightness possible for the LED ++- active-low : (optional) For PWMs where the LED is wired to supply ++ rather than ground. + - label : (optional) + see Documentation/devicetree/bindings/leds/common.txt + - linux,default-trigger : (optional) +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/mailbox/mailbox.txt linux-openelec/Documentation/devicetree/bindings/mailbox/mailbox.txt +--- linux-3.14.36/Documentation/devicetree/bindings/mailbox/mailbox.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/mailbox/mailbox.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,33 @@ ++* Generic Mailbox Controller and client driver bindings ++ ++Generic binding to provide a way for Mailbox controller drivers to ++assign appropriate mailbox channel to client drivers. ++ ++* Mailbox Controller ++ ++Required property: ++- #mbox-cells: Must be at least 1. Number of cells in a mailbox ++ specifier. ++ ++Example: ++ mailbox: mailbox { ++ ... ++ #mbox-cells = <1>; ++ }; ++ ++ ++* Mailbox Client ++ ++Required property: ++- mbox: List of phandle and mailbox channel specifier. ++ ++- mbox-names: List of identifier strings for each mailbox channel ++ required by the client. ++ ++Example: ++ pwr_cntrl: power { ++ ... ++ mbox-names = "pwr-ctrl", "rpc"; ++ mbox = <&mailbox 0 ++ &mailbox 1>; ++ }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/mlb/mlb150.txt linux-openelec/Documentation/devicetree/bindings/mlb/mlb150.txt +--- linux-3.14.36/Documentation/devicetree/bindings/mlb/mlb150.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/mlb/mlb150.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,22 @@ ++* Freescale Media Local Bus Host Controller (MLB) for i.MX6Q/DL ++ ++The Media Local Bus Host Controller on Freescale i.MX family ++provides an interface for MOST network. ++ ++Required properties: ++- compatible : Should be "fsl,-mlb150" ++- reg : Should contain mlb registers location and length ++- interrupts : Should contain mlb interrupt ++- clocks: Should contain the mlb clock sources ++- clock-names: Should be the names of mlb clock sources ++- iram : phandle pointing to the SRAM device node ++ ++Examples: ++mlb@0218c000 { ++ compatible = "fsl,imx6q-mlb150"; ++ reg = <0x0218c000 0x4000>; ++ interrupts = <0 53 0x04 0 117 0x04 0 126 0x04>; ++ clocks = <&clks 139>, <&clks 175>; ++ clock-names = "mlb", "pll8_mlb"; ++ iram = <&ocram>; ++}; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/mmc/mmc.txt linux-openelec/Documentation/devicetree/bindings/mmc/mmc.txt +--- linux-3.14.36/Documentation/devicetree/bindings/mmc/mmc.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/mmc/mmc.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -5,6 +5,8 @@ + Interpreted by the OF core: + - reg: Registers location and length. + - interrupts: Interrupts used by the MMC controller. ++- clocks: Clocks needed for the host controller, if any. ++- clock-names: Goes with clocks above. + + Card detection: + If no property below is supplied, host native card detect is used. +@@ -30,6 +32,15 @@ + - cap-sdio-irq: enable SDIO IRQ signalling on this interface + - full-pwr-cycle: full power cycle of the card is supported + ++Card power and reset control: ++The following properties can be specified for cases where the MMC ++peripheral needs additional reset, regulator and clock lines. It is for ++example common for WiFi/BT adapters to have these separate from the main ++MMC bus: ++ - card-reset-gpios: Specify GPIOs for card reset (reset active low) ++ - card-external-vcc-supply: Regulator to drive (independent) card VCC ++ - clock with name "card_ext_clock": External clock provided to the card ++ + *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line + polarity properties, we have to fix the meaning of the "normal" and "inverted" + line levels. We choose to follow the SDHCI standard, which specifies both those +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt linux-openelec/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt +--- linux-3.14.36/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -71,6 +71,13 @@ + name for integer state ID 0, list entry 1 for state ID 1, and + so on. + ++pinctrl-assert-gpios: ++ List of phandles, each pointing at a GPIO which is used by some ++ board design to steer pins between two peripherals on the board. ++ It plays like a board level pin multiplexer to choose different ++ functions for given pins by pulling up/down the GPIOs. See ++ bindings/gpio/gpio.txt for details of how to specify GPIO. ++ + For example: + + /* For a client device requiring named states */ +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/reset/gpio-reset.txt linux-openelec/Documentation/devicetree/bindings/reset/gpio-reset.txt +--- linux-3.14.36/Documentation/devicetree/bindings/reset/gpio-reset.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/reset/gpio-reset.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,35 @@ ++GPIO reset controller ++===================== ++ ++A GPIO reset controller controls a single GPIO that is connected to the reset ++pin of a peripheral IC. Please also refer to reset.txt in this directory for ++common reset controller binding usage. ++ ++Required properties: ++- compatible: Should be "gpio-reset" ++- reset-gpios: A gpio used as reset line. The gpio specifier for this property ++ depends on the gpio controller that provides the gpio. ++- #reset-cells: 0, see below ++ ++Optional properties: ++- reset-delay-us: delay in microseconds. The gpio reset line will be asserted for ++ this duration to reset. ++- initially-in-reset: boolean. If not set, the initial state should be a ++ deasserted reset line. If this property exists, the ++ reset line should be kept in reset. ++ ++example: ++ ++sii902x_reset: gpio-reset { ++ compatible = "gpio-reset"; ++ reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; ++ reset-delay-us = <10000>; ++ initially-in-reset; ++ #reset-cells = <0>; ++}; ++ ++/* Device with nRESET pin connected to GPIO5_0 */ ++sii902x@39 { ++ /* ... */ ++ resets = <&sii902x_reset>; /* active-low GPIO5_0, 10 ms delay */ ++}; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/sound/cs42888.txt linux-openelec/Documentation/devicetree/bindings/sound/cs42888.txt +--- linux-3.14.36/Documentation/devicetree/bindings/sound/cs42888.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/sound/cs42888.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,29 @@ ++CS42888 audio CODEC ++ ++This device supports I2C only. ++ ++Required properties: ++ ++ - compatible: "cirrus,cs42888" ++ - reg: the I2C address of the device. ++ - clocks: Phandle to the clock node. ++ - clock-names: Contains name for each entry in clocks. ++ "codec_osc" : the external oscillator. ++ "esai" : the hckt clock from esai. ++ - -supply: Phandle to the regulator . ++ ++Note: cs42888 needs a regulators node and a clocks node. ++ ++Example: ++In this case, the clock is external oscillator. ++ ++codec: cs42888@48 { ++ compatible = "cirrus,cs42888"; ++ reg = <0x048>; ++ clocks = <&codec_osc 0>; ++ clock-names = "codec_osc"; ++ VA-supply = <®_audio>; ++ VD-supply = <®_audio>; ++ VLS-supply = <®_audio>; ++ VLC-supply = <®_audio>; ++}; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt linux-openelec/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt +--- linux-3.14.36/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,23 @@ ++* Freescale Asynchronous Sample Rate Converter (ASRC) ++ ++This document is for asrc p2p node. p2p is one of asrc mode. asrc p2p depend on ++MXC_ASRC. ++ ++Required properties: ++ - compatible: Should be "fsl,-asrc-p2p". ++ - fsl,output-rate: the output rate of asrc p2p. which can be <32000> to <192000>, ++ - fsl,output-width: the output width of asrc p2p. which can be <16>, <24>. ++ - fsl,asrc-dma-rx-events: The rx dma event of the asrc, corresponding ++ to 3 pair of asrc. ++ - fsl,asrc-dma-tx-events: The tx dma event of the esai, corresponding ++ to 3 pair of asrc. ++ ++Example: ++asrc_p2p: asrc_p2p { ++ compatible = "fsl,imx6q-asrc-p2p"; ++ fsl,output-rate = <48000>; ++ fsl,output-width = <16>; ++ fsl,asrc-dma-rx-events = <17 18 19>; ++ fsl,asrc-dma-tx-events = <20 21 22>; ++ status = "okay"; ++}; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt linux-openelec/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt +--- linux-3.14.36/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,25 @@ ++Freescale i.MX audio complex with CS42888 codec ++ ++Required properties: ++- compatible : "fsl,imx-audio-cs42888" ++- model : The user-visible name of this sound complex ++- esai-controller : The phandle of the i.MX SSI controller ++- audio-codec : The phandle of the CS42888 audio codec ++ ++Optional properties: ++- asrc-controller : The phandle of the i.MX ASRC controller ++- audio-routing : A list of the connections between audio components. ++ Each entry is a pair of strings, the first being the connection's sink, ++ the second being the connection's source. Valid names could be power ++ supplies, CS42888 pins, and the jacks on the board: ++ ++Example: ++ ++sound { ++ compatible = "fsl,imx6q-sabresd-wm8962", ++ "fsl,imx-audio-wm8962"; ++ model = "cs42888-audio"; ++ esai-controller = <&esai>; ++ asrc-controller = <&asrc_p2p>; ++ audio-codec = <&codec>; ++}; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt linux-openelec/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt +--- linux-3.14.36/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -24,6 +24,12 @@ + Note: The AUDMUX port numbering should start at 1, which is consistent with + hardware manual. + ++Optional properties: ++- hp-det-gpios : The gpio pin to detect plug in/out event that happens to ++ Headphone jack. ++- mic-det-gpios: The gpio pin to detect plug in/out event that happens to ++ Microphone jack. ++ + Example: + + sound { +@@ -43,4 +49,6 @@ + "DMICDAT", "DMIC"; + mux-int-port = <2>; + mux-ext-port = <3>; ++ hp-det-gpios = <&gpio7 8 1>; ++ mic-det-gpios = <&gpio1 9 1>; + }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/sound/wm8962.txt linux-openelec/Documentation/devicetree/bindings/sound/wm8962.txt +--- linux-3.14.36/Documentation/devicetree/bindings/sound/wm8962.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/sound/wm8962.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -13,6 +13,14 @@ + of R51 (Class D Control 2) gets set, indicating that the speaker is + in mono mode. + ++ - amic-mono: This is a boolean property. If present, indicating that the ++ analog micphone is hardware mono input, the driver would enable monomix ++ for it. ++ ++ - dmic-mono: This is a boolean property. If present, indicating that the ++ digital micphone is hardware mono input, the driver would enable monomix ++ for it. ++ + - mic-cfg : Default register value for R48 (Additional Control 4). + If absent, the default should be the register default. + +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt linux-openelec/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt +--- linux-3.14.36/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt 2015-07-24 18:03:30.212842002 -0500 +@@ -18,6 +18,8 @@ + - vbus-supply: regulator for vbus + - disable-over-current: disable over current detect + - external-vbus-divider: enables off-chip resistor divider for Vbus ++- clocks: phandle to the clock that drives the USB hub ++- clock-names: must be "phy" + + Examples: + usb@02184000 { /* USB OTG */ +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/usb/mxs-phy.txt linux-openelec/Documentation/devicetree/bindings/usb/mxs-phy.txt +--- linux-3.14.36/Documentation/devicetree/bindings/usb/mxs-phy.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/devicetree/bindings/usb/mxs-phy.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -1,13 +1,16 @@ + * Freescale MXS USB Phy Device + + Required properties: +-- compatible: Should be "fsl,imx23-usbphy" ++- compatible: "fsl,imx23-usbphy" for imx23 and imx28, "fsl,imx6q-usbphy" ++for imx6dq and imx6dl, "fsl,imx6sl-usbphy" for imx6sl + - reg: Should contain registers location and length + - interrupts: Should contain phy interrupt ++- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series + + Example: + usbphy1: usbphy@020c9000 { + compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; + reg = <0x020c9000 0x1000>; + interrupts = <0 44 0x04>; ++ fsl,anatop = <&anatop>; + }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt linux-openelec/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt +--- linux-3.14.36/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,61 @@ ++* Freescale CMOS Sensor Interface (CSI) V4L2 Capture ++ ++Required properties for CSI ++- compatible: "fsl,-csi". Supported chip includes imx6sl ++- reg: Address and length of the register set for CSI ++- interrupts: Should contain CSI interrupts ++ ++Required properties for v4l2_capture ++- compatible: should be "fsl,-csi-v4l2", supported socs include imx6sl ++ ++Required properties for sensor ++- compatible: "," ++ please check the supported sensor in the Supported Sensor fields. ++- reg: sensor I2C slave address ++- pinctrl-names: should be "default" for parallel sensor ++- pinctrl-0: should depend on the connection between sensor and i.MX ++ connection between sensor and i.MX could be only legacy parallel on i.MX6SL ++- clocks: should be the clock source provided to sensor. ++- clock-names: should be "csi_mclk" ++- AVDD-supply: set according to the board. ++- DVDD-supply: set according to the board. ++- pwn-gpios: set according to the board. ++- rst-gpios: set according to the board. ++- csi_id: csi id for v4l2 capture device ++ should be 0 for i.MX6SL ++- mclk: should the value of mclk clock send out the sensor. unit is Hz. ++- mclk_source: should be 0 for i.MX6SL ++ ++Supported Sensor ++- ovti, ov5640 ++ ++Example for CSI: ++ csi: csi@020e4000 { ++ compatible = "fsl,imx6sl-csi"; ++ reg = <0x020e4000 0x4000>; ++ interrupts = <0 7 0x04>; ++ status = "disabled"; ++ }; ++ ++Examples for v4l2_capture: ++ csi_v4l2_cap { ++ compatible = "fsl,imx6q-v4l2-capture"; ++ status = "okay"; ++ }; ++ ++Examples for sensors: ++ ov564x: ov564x@3c { ++ compatible = "ovti,ov564x"; ++ reg = <0x3c>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_csi_0>; ++ clocks = <&clks IMX6SL_CLK_CSI>; ++ clock-names = "csi_mclk"; ++ AVDD-supply = <&vgen6_reg>; /* 2.8v */ ++ DVDD-supply = <&vgen2_reg>; /* 1.5v*/ ++ pwn-gpios = <&gpio1 25 1>; ++ rst-gpios = <&gpio1 26 0>; ++ csi_id = <0>; ++ mclk = <24000000>; ++ mclk_source = <0>; ++ }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt linux-openelec/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt +--- linux-3.14.36/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,42 @@ ++* Freescale MIPI CSI2 Controller for i.MX6DQ/i.MX6SDL ++ ++Required properties for mipi csi2 controller: ++- compatible: should be "fsl,imx6q-mipi-csi2" ++- reg: contains mipi csi2 register base address and range ++- interrupts: where type is a interrupt type, num is the ++ interrupt number and flag is a field that level/trigger information for ++ the interrupt. ++- clocks: the clock sources that mipi csi2 depends on. ++- clock-names: the name is related to the clock source one by one. ++- status: should be set to "disable". ++ ++Required properties for mipi csi2 on specified board: ++- ipu_id: ipu id which mipi csi2 connected to. ++ should be 0 or 1 for i.MX6DQ; should be 0 for i.MX6SDL ++- csi_id: csi id which mipi csi2 connected to. ++ should be 0 or 1 for i.MX6DQ/i.MX6SDL ++- v_channel: virtual channel which send to MIPI CSI2 controller ++ should keep consistent with the input MIPI signal. ++- lanes: data lanes of input MIPI signal. The maximum data lanes is 4. ++ should keep consistent with the input MIPI signal. ++- status: should be set to "okay". ++ ++Examples: ++for SOC imx6qdl.dtsi: ++ mipi_csi@021dc000 { ++ compatible = "fsl,imx6q-mipi-csi2"; ++ reg = <0x021dc000 0x4000>; ++ interrupts = <0 100 0x04>, <0 101 0x04>; ++ clocks = <&clks 138>, <&clks 53>, <&clks 204>; ++ clock-names = "dphy_clk", "pixel_clk", "cfg_clk"; ++ status = "disabled"; ++ }; ++ ++for board imx6qdl-sabresd.dtsi: ++ mipi_csi@021dc000 { ++ status = "okay"; ++ ipu_id = <0>; ++ csi_id = <1>; ++ v_channel = <0>; ++ lanes = <2>; ++ }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/video/fsl,pxp.txt linux-openelec/Documentation/devicetree/bindings/video/fsl,pxp.txt +--- linux-3.14.36/Documentation/devicetree/bindings/video/fsl,pxp.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/video/fsl,pxp.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,30 @@ ++* Freescale PxP Controller for i.MX6DL, i.MX6SL ++ ++Required properties for PxP controller: ++- compatible: should be "fsl,-pxp-dma" ++- reg: contains pxp register base address and range ++- interrupts: where type is an interrupt type, num is the ++ interrupt number and flag is a field that level/trigger information for ++ the interrupt. ++- clocks: the clock sources that pxp depends on. ++- clock-names: the name is related to the clock source ++ ++Required properties for pxp on specified board: ++- status: should be set to "okay" if want to use PxP ++ ++Examples: ++for SOC imx6dl.dtsi: ++ pxp@020f0000 { ++ compatible = "fsl,imx6dl-pxp-dma"; ++ reg = <0x020f0000 0x4000>; ++ interrupts = <0 98 0x04>; ++ clocks = <&clks 133>; ++ clock-names = "pxp-axi"; ++ status = "disabled"; ++ }; ++ ++ ++for board imx6dl-sabresd.dts: ++ &pxp { ++ status = "okay"; ++ }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt linux-openelec/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt +--- linux-3.14.36/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,102 @@ ++* Freescale V4L2 Capture for i.MX6DQ/i.MX6SDL ++ ++Required board properties for IPUv3 capture: ++- clocks: should include the clock provided by i.MX6 to sensor ++- clock-names: sensor clock's name should be "ipux_csiy" ++ x should be 1 or 2 for i.MX6DQ; should be 1 for i.MX6SDL ++ y is 0 or 1 for i.MX6DQ/i.MX6SDL ++Note: other detailed information for IPUv3, please refer to ++Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt ++ ++Required properties for v4l2_capture ++- compatible: should be "fsl,imx6q-v4l2-capture" ++- ipu_id: ipu id for v4l2 capture device ++ should be 0 or 1 for i.MX6DQ; should be 0 for i.MX6SDL ++- csi_id: csi id for v4l2 capture device ++ should be 0 or 1 for i.MX6DQ/i.MX6SDL ++- mclk_source: should be 0 or 1. two mclk sources at most now ++- status: should be set to "okay" to enable this device ++ ++Required properties for sensor ++- compatible: "," ++ please check the supported sensor in the Supported Sensor fields. ++- reg: sensor I2C slave address ++- pinctrl-names: should be "default" for parallel sensor ++- pinctrl-0: should depend on the connection between sensor and i.MX ++ connection between sensor and i.MX could be MIPI-CSI2 or legacy parallel ++- clocks: should be the clock source provided to sensor. ++- clock-names: should be "csi_mclk" ++- DOVDD-supply: set according to the board. ++- AVDD-supply: set according to the board. ++- DVDD-supply: set according to the board. ++- pwn-gpios: set according to the board. ++- rst-gpios: set according to the board. ++- csi_id: csi id for v4l2 capture device ++ should be 0 or 1 for i.MX6DQ/i.MX6SDL. ++- mclk: should the value of mclk clock send out the sensor. unit is Hz. ++- mclk_source: should be 0 or 1 and should be the same as the setting in ++ v4l2_capture. ++- cvbs: 1 for CVBS input, 0 YPbPr input. This property is only needed for ++ adv7180 tv decoder. ++ ++Supported Sensor ++- ov5640 ++- ov5642 ++- ov5640_mipi ++- adv7180 ++ ++ ++Example for IPUv3 including capture settings on imx6q-sabresd.dts: ++ ipu1: ipu@02400000 { /* IPU1 */ ++ compatible = "fsl,imx6q-ipuv3"; ++ reg = <0x02400000 0x400000>; ++ interrupts = <0 5 0x04>, < 0 6 0x04>; ++ clocks = <&clks 130>, <&clks 131>, <&clks 132>, <&clks 39>, <&clks 40>, <&clks 169>; ++ clock-names = "ipu1", "ipu1_di0", "ipu1_di1", "ipu1_di0_sel", "ipu1_di1_sel", "ipu1_csi0"; ++ status = "disabled"; ++ }; ++ ++Examples for v4l2_capture: ++ v4l2_cap { ++ compatible = "fsl,imx6q-v4l2-capture"; ++ ipu_id = <0>; ++ csi_id = <0>; ++ mclk_source = <0>; ++ status = "okay"; ++ }; ++ ++Examples for sensors: ++ ov5642: ov5642@3c { ++ compatible = "ovti,ov5642"; ++ reg = <0x3c>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ipu1_2>; ++ clocks = <&clks 201>; ++ clock-names = "csi_mclk"; ++ DOVDD-supply = <&vgen4_reg>; /* 1.8v */ ++ AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3 */ ++ DVDD-supply = <&vgen2_reg>; /* 1.5v*/ ++ pwn-gpios = <&gpio1 16 1>; /* active low: SD1_DAT0 */ ++ rst-gpios = <&gpio1 17 0>; /* active high: SD1_DAT1 */ ++ csi_id = <0>; ++ mclk = <24000000>; ++ mclk_source = <0>; ++ }; ++ ++ adv7180: adv7180@21 { ++ compatible = "adv,adv7180"; ++ reg = <0x21>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ipu1_3>; ++ clocks = <&clks 201>; ++ clock-names = "csi_mclk"; ++ DOVDD-supply = <®_3p3v>; /* 3.3v, enabled via 2.8 VGEN6 */ ++ AVDD-supply = <®_3p3v>; /* 1.8v */ ++ DVDD-supply = <®_3p3v>; /* 1.8v */ ++ PVDD-supply = <®_3p3v>; /* 1.8v */ ++ pwn-gpios = <&max7310_b 2 0>; ++ csi_id = <0>; ++ mclk = <24000000>; ++ mclk_source = <0>; ++ cvbs = <1>; ++ }; +diff -Nur linux-3.14.36/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt linux-openelec/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt +--- linux-3.14.36/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,20 @@ ++Device-Tree bindings for hdmi video driver ++ ++Required properties: ++- compatible: value should be "fsl,imx6q-hdmi-video". ++- fsl,hdcp: define the property in dts, hdmi driver will initalize for hdcp, ++ otherwise hdcp function will not supported. ++- fsl,phy_reg_vlev: hdmi phy register,Voltage Level Control Register offset 0x0e, ++ adjust hdmi phy signal voltage level. ++- fsl,phy_reg_cksymtx: hdmi phy register, clock symbol and transmitter control ++ register offset 0x09, adjust hdmi signal pre-emphasis. ++ ++Example: ++ ++ hdmi_video { ++ compatible = "fsl,imx6q-hdmi-video"; ++ fsl,hdcp; ++ fsl,phy_reg_vlev = <0x0294>; ++ fsl,phy_reg_cksymtx = <0x800d>; ++ }; ++ +diff -Nur linux-3.14.36/Documentation/filesystems/hfsplus.txt linux-openelec/Documentation/filesystems/hfsplus.txt +--- linux-3.14.36/Documentation/filesystems/hfsplus.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/filesystems/hfsplus.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -56,4 +56,4 @@ + + kernel source: + +-Apple Technote 1150 http://developer.apple.com/technotes/tn/tn1150.html ++Apple Technote 1150 https://developer.apple.com/legacy/library/technotes/tn/tn1150.html +diff -Nur linux-3.14.36/Documentation/kernel-parameters.txt linux-openelec/Documentation/kernel-parameters.txt +--- linux-3.14.36/Documentation/kernel-parameters.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/kernel-parameters.txt 2015-07-24 18:03:29.364842002 -0500 +@@ -603,8 +603,11 @@ + Also note the kernel might malfunction if you disable + some critical bits. + +- cma=nn[MG] [ARM,KNL] +- Sets the size of kernel global memory area for contiguous ++ cma=nn[MG]@[start[MG][-end[MG]]] ++ [ARM,X86,KNL] ++ Sets the size of kernel global memory area for ++ contiguous memory allocations and optionally the ++ placement constraint by the physical address range of + memory allocations. For more information, see + include/linux/dma-contiguous.h + +diff -Nur linux-3.14.36/Documentation/kernel-parameters.txt.orig linux-openelec/Documentation/kernel-parameters.txt.orig +--- linux-3.14.36/Documentation/kernel-parameters.txt.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/Documentation/kernel-parameters.txt.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,3610 @@ ++ Kernel Parameters ++ ~~~~~~~~~~~~~~~~~ ++ ++The following is a consolidated list of the kernel parameters as implemented ++(mostly) by the __setup() macro and sorted into English Dictionary order ++(defined as ignoring all punctuation and sorting digits before letters in a ++case insensitive manner), and with descriptions where known. ++ ++Module parameters for loadable modules are specified only as the ++parameter name with optional '=' and value as appropriate, such as: ++ ++ modprobe usbcore blinkenlights=1 ++ ++Module parameters for modules that are built into the kernel image ++are specified on the kernel command line with the module name plus ++'.' plus parameter name, with '=' and value if appropriate, such as: ++ ++ usbcore.blinkenlights=1 ++ ++Hyphens (dashes) and underscores are equivalent in parameter names, so ++ log_buf_len=1M print-fatal-signals=1 ++can also be entered as ++ log-buf-len=1M print_fatal_signals=1 ++ ++ ++This document may not be entirely up to date and comprehensive. The command ++"modinfo -p ${modulename}" shows a current list of all parameters of a loadable ++module. Loadable modules, after being loaded into the running kernel, also ++reveal their parameters in /sys/module/${modulename}/parameters/. Some of these ++parameters may be changed at runtime by the command ++"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}". ++ ++The parameters listed below are only valid if certain kernel build options were ++enabled and if respective hardware is present. The text in square brackets at ++the beginning of each description states the restrictions within which a ++parameter is applicable: ++ ++ ACPI ACPI support is enabled. ++ AGP AGP (Accelerated Graphics Port) is enabled. ++ ALSA ALSA sound support is enabled. ++ APIC APIC support is enabled. ++ APM Advanced Power Management support is enabled. ++ ARM ARM architecture is enabled. ++ AVR32 AVR32 architecture is enabled. ++ AX25 Appropriate AX.25 support is enabled. ++ BLACKFIN Blackfin architecture is enabled. ++ CLK Common clock infrastructure is enabled. ++ CMA Contiguous Memory Area support is enabled. ++ DRM Direct Rendering Management support is enabled. ++ DYNAMIC_DEBUG Build in debug messages and enable them at runtime ++ EDD BIOS Enhanced Disk Drive Services (EDD) is enabled ++ EFI EFI Partitioning (GPT) is enabled ++ EIDE EIDE/ATAPI support is enabled. ++ EVM Extended Verification Module ++ FB The frame buffer device is enabled. ++ FTRACE Function tracing enabled. ++ GCOV GCOV profiling is enabled. ++ HW Appropriate hardware is enabled. ++ IA-64 IA-64 architecture is enabled. ++ IMA Integrity measurement architecture is enabled. ++ IOSCHED More than one I/O scheduler is enabled. ++ IP_PNP IP DHCP, BOOTP, or RARP is enabled. ++ IPV6 IPv6 support is enabled. ++ ISAPNP ISA PnP code is enabled. ++ ISDN Appropriate ISDN support is enabled. ++ JOY Appropriate joystick support is enabled. ++ KGDB Kernel debugger support is enabled. ++ KVM Kernel Virtual Machine support is enabled. ++ LIBATA Libata driver is enabled ++ LP Printer support is enabled. ++ LOOP Loopback device support is enabled. ++ M68k M68k architecture is enabled. ++ These options have more detailed description inside of ++ Documentation/m68k/kernel-options.txt. ++ MDA MDA console support is enabled. ++ MIPS MIPS architecture is enabled. ++ MOUSE Appropriate mouse support is enabled. ++ MSI Message Signaled Interrupts (PCI). ++ MTD MTD (Memory Technology Device) support is enabled. ++ NET Appropriate network support is enabled. ++ NUMA NUMA support is enabled. ++ NFS Appropriate NFS support is enabled. ++ OSS OSS sound support is enabled. ++ PV_OPS A paravirtualized kernel is enabled. ++ PARIDE The ParIDE (parallel port IDE) subsystem is enabled. ++ PARISC The PA-RISC architecture is enabled. ++ PCI PCI bus support is enabled. ++ PCIE PCI Express support is enabled. ++ PCMCIA The PCMCIA subsystem is enabled. ++ PNP Plug & Play support is enabled. ++ PPC PowerPC architecture is enabled. ++ PPT Parallel port support is enabled. ++ PS2 Appropriate PS/2 support is enabled. ++ RAM RAM disk support is enabled. ++ S390 S390 architecture is enabled. ++ SCSI Appropriate SCSI support is enabled. ++ A lot of drivers have their options described inside ++ the Documentation/scsi/ sub-directory. ++ SECURITY Different security models are enabled. ++ SELINUX SELinux support is enabled. ++ APPARMOR AppArmor support is enabled. ++ SERIAL Serial support is enabled. ++ SH SuperH architecture is enabled. ++ SMP The kernel is an SMP kernel. ++ SPARC Sparc architecture is enabled. ++ SWSUSP Software suspend (hibernation) is enabled. ++ SUSPEND System suspend states are enabled. ++ TPM TPM drivers are enabled. ++ TS Appropriate touchscreen support is enabled. ++ UMS USB Mass Storage support is enabled. ++ USB USB support is enabled. ++ USBHID USB Human Interface Device support is enabled. ++ V4L Video For Linux support is enabled. ++ VMMIO Driver for memory mapped virtio devices is enabled. ++ VGA The VGA console has been enabled. ++ VT Virtual terminal support is enabled. ++ WDT Watchdog support is enabled. ++ XT IBM PC/XT MFM hard disk support is enabled. ++ X86-32 X86-32, aka i386 architecture is enabled. ++ X86-64 X86-64 architecture is enabled. ++ More X86-64 boot options can be found in ++ Documentation/x86/x86_64/boot-options.txt . ++ X86 Either 32-bit or 64-bit x86 (same as X86-32+X86-64) ++ XEN Xen support is enabled ++ ++In addition, the following text indicates that the option: ++ ++ BUGS= Relates to possible processor bugs on the said processor. ++ KNL Is a kernel start-up parameter. ++ BOOT Is a boot loader parameter. ++ ++Parameters denoted with BOOT are actually interpreted by the boot ++loader, and have no meaning to the kernel directly. ++Do not modify the syntax of boot loader parameters without extreme ++need or coordination with . ++ ++There are also arch-specific kernel-parameters not documented here. ++See for example . ++ ++Note that ALL kernel parameters listed below are CASE SENSITIVE, and that ++a trailing = on the name of any parameter states that that parameter will ++be entered as an environment variable, whereas its absence indicates that ++it will appear as a kernel argument readable via /proc/cmdline by programs ++running once the system is up. ++ ++The number of kernel parameters is not limited, but the length of the ++complete command line (parameters including spaces etc.) is limited to ++a fixed number of characters. This limit depends on the architecture ++and is between 256 and 4096 characters. It is defined in the file ++./include/asm/setup.h as COMMAND_LINE_SIZE. ++ ++Finally, the [KMG] suffix is commonly described after a number of kernel ++parameter values. These 'K', 'M', and 'G' letters represent the _binary_ ++multipliers 'Kilo', 'Mega', and 'Giga', equalling 2^10, 2^20, and 2^30 ++bytes respectively. Such letter suffixes can also be entirely omitted. ++ ++ ++ acpi= [HW,ACPI,X86] ++ Advanced Configuration and Power Interface ++ Format: { force | off | strict | noirq | rsdt } ++ force -- enable ACPI if default was off ++ off -- disable ACPI if default was on ++ noirq -- do not use ACPI for IRQ routing ++ strict -- Be less tolerant of platforms that are not ++ strictly ACPI specification compliant. ++ rsdt -- prefer RSDT over (default) XSDT ++ copy_dsdt -- copy DSDT to memory ++ ++ See also Documentation/power/runtime_pm.txt, pci=noacpi ++ ++ acpi_rsdp= [ACPI,EFI,KEXEC] ++ Pass the RSDP address to the kernel, mostly used ++ on machines running EFI runtime service to boot the ++ second kernel for kdump. ++ ++ acpi_apic_instance= [ACPI, IOAPIC] ++ Format: ++ 2: use 2nd APIC table, if available ++ 1,0: use 1st APIC table ++ default: 0 ++ ++ acpi_backlight= [HW,ACPI] ++ acpi_backlight=vendor ++ acpi_backlight=video ++ If set to vendor, prefer vendor specific driver ++ (e.g. thinkpad_acpi, sony_acpi, etc.) instead ++ of the ACPI video.ko driver. ++ ++ acpi.debug_layer= [HW,ACPI,ACPI_DEBUG] ++ acpi.debug_level= [HW,ACPI,ACPI_DEBUG] ++ Format: ++ CONFIG_ACPI_DEBUG must be enabled to produce any ACPI ++ debug output. Bits in debug_layer correspond to a ++ _COMPONENT in an ACPI source file, e.g., ++ #define _COMPONENT ACPI_PCI_COMPONENT ++ Bits in debug_level correspond to a level in ++ ACPI_DEBUG_PRINT statements, e.g., ++ ACPI_DEBUG_PRINT((ACPI_DB_INFO, ... ++ The debug_level mask defaults to "info". See ++ Documentation/acpi/debug.txt for more information about ++ debug layers and levels. ++ ++ Enable processor driver info messages: ++ acpi.debug_layer=0x20000000 ++ Enable PCI/PCI interrupt routing info messages: ++ acpi.debug_layer=0x400000 ++ Enable AML "Debug" output, i.e., stores to the Debug ++ object while interpreting AML: ++ acpi.debug_layer=0xffffffff acpi.debug_level=0x2 ++ Enable all messages related to ACPI hardware: ++ acpi.debug_layer=0x2 acpi.debug_level=0xffffffff ++ ++ Some values produce so much output that the system is ++ unusable. The "log_buf_len" parameter may be useful ++ if you need to capture more output. ++ ++ acpi_irq_balance [HW,ACPI] ++ ACPI will balance active IRQs ++ default in APIC mode ++ ++ acpi_irq_nobalance [HW,ACPI] ++ ACPI will not move active IRQs (default) ++ default in PIC mode ++ ++ acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA ++ Format: ,... ++ ++ acpi_irq_pci= [HW,ACPI] If irq_balance, clear listed IRQs for ++ use by PCI ++ Format: ,... ++ ++ acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT ++ ++ acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS ++ Format: To spoof as Windows 98: ="Microsoft Windows" ++ ++ acpi_osi= [HW,ACPI] Modify list of supported OS interface strings ++ acpi_osi="string1" # add string1 ++ acpi_osi="!string2" # remove string2 ++ acpi_osi=!* # remove all strings ++ acpi_osi=! # disable all built-in OS vendor ++ strings ++ acpi_osi= # disable all strings ++ ++ 'acpi_osi=!' can be used in combination with single or ++ multiple 'acpi_osi="string1"' to support specific OS ++ vendor string(s). Note that such command can only ++ affect the default state of the OS vendor strings, thus ++ it cannot affect the default state of the feature group ++ strings and the current state of the OS vendor strings, ++ specifying it multiple times through kernel command line ++ is meaningless. This command is useful when one do not ++ care about the state of the feature group strings which ++ should be controlled by the OSPM. ++ Examples: ++ 1. 'acpi_osi=! acpi_osi="Windows 2000"' is equivalent ++ to 'acpi_osi="Windows 2000" acpi_osi=!', they all ++ can make '_OSI("Windows 2000")' TRUE. ++ ++ 'acpi_osi=' cannot be used in combination with other ++ 'acpi_osi=' command lines, the _OSI method will not ++ exist in the ACPI namespace. NOTE that such command can ++ only affect the _OSI support state, thus specifying it ++ multiple times through kernel command line is also ++ meaningless. ++ Examples: ++ 1. 'acpi_osi=' can make 'CondRefOf(_OSI, Local1)' ++ FALSE. ++ ++ 'acpi_osi=!*' can be used in combination with single or ++ multiple 'acpi_osi="string1"' to support specific ++ string(s). Note that such command can affect the ++ current state of both the OS vendor strings and the ++ feature group strings, thus specifying it multiple times ++ through kernel command line is meaningful. But it may ++ still not able to affect the final state of a string if ++ there are quirks related to this string. This command ++ is useful when one want to control the state of the ++ feature group strings to debug BIOS issues related to ++ the OSPM features. ++ Examples: ++ 1. 'acpi_osi="Module Device" acpi_osi=!*' can make ++ '_OSI("Module Device")' FALSE. ++ 2. 'acpi_osi=!* acpi_osi="Module Device"' can make ++ '_OSI("Module Device")' TRUE. ++ 3. 'acpi_osi=! acpi_osi=!* acpi_osi="Windows 2000"' is ++ equivalent to ++ 'acpi_osi=!* acpi_osi=! acpi_osi="Windows 2000"' ++ and ++ 'acpi_osi=!* acpi_osi="Windows 2000" acpi_osi=!', ++ they all will make '_OSI("Windows 2000")' TRUE. ++ ++ acpi_pm_good [X86] ++ Override the pmtimer bug detection: force the kernel ++ to assume that this machine's pmtimer latches its value ++ and always returns good values. ++ ++ acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode ++ Format: { level | edge | high | low } ++ ++ acpi_serialize [HW,ACPI] force serialization of AML methods ++ ++ acpi_skip_timer_override [HW,ACPI] ++ Recognize and ignore IRQ0/pin2 Interrupt Override. ++ For broken nForce2 BIOS resulting in XT-PIC timer. ++ ++ acpi_sleep= [HW,ACPI] Sleep options ++ Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, ++ old_ordering, nonvs, sci_force_enable } ++ See Documentation/power/video.txt for information on ++ s3_bios and s3_mode. ++ s3_beep is for debugging; it makes the PC's speaker beep ++ as soon as the kernel's real-mode entry point is called. ++ s4_nohwsig prevents ACPI hardware signature from being ++ used during resume from hibernation. ++ old_ordering causes the ACPI 1.0 ordering of the _PTS ++ control method, with respect to putting devices into ++ low power states, to be enforced (the ACPI 2.0 ordering ++ of _PTS is used by default). ++ nonvs prevents the kernel from saving/restoring the ++ ACPI NVS memory during suspend/hibernation and resume. ++ sci_force_enable causes the kernel to set SCI_EN directly ++ on resume from S1/S3 (which is against the ACPI spec, ++ but some broken systems don't work without it). ++ ++ acpi_use_timer_override [HW,ACPI] ++ Use timer override. For some broken Nvidia NF5 boards ++ that require a timer override, but don't have HPET ++ ++ acpi_enforce_resources= [ACPI] ++ { strict | lax | no } ++ Check for resource conflicts between native drivers ++ and ACPI OperationRegions (SystemIO and SystemMemory ++ only). IO ports and memory declared in ACPI might be ++ used by the ACPI subsystem in arbitrary AML code and ++ can interfere with legacy drivers. ++ strict (default): access to resources claimed by ACPI ++ is denied; legacy drivers trying to access reserved ++ resources will fail to bind to device using them. ++ lax: access to resources claimed by ACPI is allowed; ++ legacy drivers trying to access reserved resources ++ will bind successfully but a warning message is logged. ++ no: ACPI OperationRegions are not marked as reserved, ++ no further checks are performed. ++ ++ acpi_no_memhotplug [ACPI] Disable memory hotplug. Useful for kdump ++ kernels. ++ ++ add_efi_memmap [EFI; X86] Include EFI memory map in ++ kernel's map of available physical RAM. ++ ++ agp= [AGP] ++ { off | try_unsupported } ++ off: disable AGP support ++ try_unsupported: try to drive unsupported chipsets ++ (may crash computer or cause data corruption) ++ ++ ALSA [HW,ALSA] ++ See Documentation/sound/alsa/alsa-parameters.txt ++ ++ alignment= [KNL,ARM] ++ Allow the default userspace alignment fault handler ++ behaviour to be specified. Bit 0 enables warnings, ++ bit 1 enables fixups, and bit 2 sends a segfault. ++ ++ align_va_addr= [X86-64] ++ Align virtual addresses by clearing slice [14:12] when ++ allocating a VMA at process creation time. This option ++ gives you up to 3% performance improvement on AMD F15h ++ machines (where it is enabled by default) for a ++ CPU-intensive style benchmark, and it can vary highly in ++ a microbenchmark depending on workload and compiler. ++ ++ 32: only for 32-bit processes ++ 64: only for 64-bit processes ++ on: enable for both 32- and 64-bit processes ++ off: disable for both 32- and 64-bit processes ++ ++ alloc_snapshot [FTRACE] ++ Allocate the ftrace snapshot buffer on boot up when the ++ main buffer is allocated. This is handy if debugging ++ and you need to use tracing_snapshot() on boot up, and ++ do not want to use tracing_snapshot_alloc() as it needs ++ to be done where GFP_KERNEL allocations are allowed. ++ ++ amd_iommu= [HW,X86-64] ++ Pass parameters to the AMD IOMMU driver in the system. ++ Possible values are: ++ fullflush - enable flushing of IO/TLB entries when ++ they are unmapped. Otherwise they are ++ flushed before they will be reused, which ++ is a lot of faster ++ off - do not initialize any AMD IOMMU found in ++ the system ++ force_isolation - Force device isolation for all ++ devices. The IOMMU driver is not ++ allowed anymore to lift isolation ++ requirements as needed. This option ++ does not override iommu=pt ++ ++ amd_iommu_dump= [HW,X86-64] ++ Enable AMD IOMMU driver option to dump the ACPI table ++ for AMD IOMMU. With this option enabled, AMD IOMMU ++ driver will print ACPI tables for AMD IOMMU during ++ IOMMU initialization. ++ ++ amijoy.map= [HW,JOY] Amiga joystick support ++ Map of devices attached to JOY0DAT and JOY1DAT ++ Format: , ++ See also Documentation/input/joystick.txt ++ ++ analog.map= [HW,JOY] Analog joystick and gamepad support ++ Specifies type or capabilities of an analog joystick ++ connected to one of 16 gameports ++ Format: ,,.. ++ ++ apc= [HW,SPARC] ++ Power management functions (SPARCstation-4/5 + deriv.) ++ Format: noidle ++ Disable APC CPU standby support. SPARCstation-Fox does ++ not play well with APC CPU idle - disable it if you have ++ APC and your system crashes randomly. ++ ++ apic= [APIC,X86-32] Advanced Programmable Interrupt Controller ++ Change the output verbosity whilst booting ++ Format: { quiet (default) | verbose | debug } ++ Change the amount of debugging information output ++ when initialising the APIC and IO-APIC components. ++ ++ autoconf= [IPV6] ++ See Documentation/networking/ipv6.txt. ++ ++ show_lapic= [APIC,X86] Advanced Programmable Interrupt Controller ++ Limit apic dumping. The parameter defines the maximal ++ number of local apics being dumped. Also it is possible ++ to set it to "all" by meaning -- no limit here. ++ Format: { 1 (default) | 2 | ... | all }. ++ The parameter valid if only apic=debug or ++ apic=verbose is specified. ++ Example: apic=debug show_lapic=all ++ ++ apm= [APM] Advanced Power Management ++ See header of arch/x86/kernel/apm_32.c. ++ ++ arcrimi= [HW,NET] ARCnet - "RIM I" (entirely mem-mapped) cards ++ Format: ,, ++ ++ ataflop= [HW,M68k] ++ ++ atarimouse= [HW,MOUSE] Atari Mouse ++ ++ atkbd.extra= [HW] Enable extra LEDs and keys on IBM RapidAccess, ++ EzKey and similar keyboards ++ ++ atkbd.reset= [HW] Reset keyboard during initialization ++ ++ atkbd.set= [HW] Select keyboard code set ++ Format: (2 = AT (default), 3 = PS/2) ++ ++ atkbd.scroll= [HW] Enable scroll wheel on MS Office and similar ++ keyboards ++ ++ atkbd.softraw= [HW] Choose between synthetic and real raw mode ++ Format: (0 = real, 1 = synthetic (default)) ++ ++ atkbd.softrepeat= [HW] ++ Use software keyboard repeat ++ ++ audit= [KNL] Enable the audit sub-system ++ Format: { "0" | "1" } (0 = disabled, 1 = enabled) ++ 0 - kernel audit is disabled and can not be enabled ++ until the next reboot ++ unset - kernel audit is initialized but disabled and ++ will be fully enabled by the userspace auditd. ++ 1 - kernel audit is initialized and partially enabled, ++ storing at most audit_backlog_limit messages in ++ RAM until it is fully enabled by the userspace ++ auditd. ++ Default: unset ++ ++ audit_backlog_limit= [KNL] Set the audit queue size limit. ++ Format: (must be >=0) ++ Default: 64 ++ ++ baycom_epp= [HW,AX25] ++ Format: , ++ ++ baycom_par= [HW,AX25] BayCom Parallel Port AX.25 Modem ++ Format: , ++ See header of drivers/net/hamradio/baycom_par.c. ++ ++ baycom_ser_fdx= [HW,AX25] ++ BayCom Serial Port AX.25 Modem (Full Duplex Mode) ++ Format: ,,[,] ++ See header of drivers/net/hamradio/baycom_ser_fdx.c. ++ ++ baycom_ser_hdx= [HW,AX25] ++ BayCom Serial Port AX.25 Modem (Half Duplex Mode) ++ Format: ,, ++ See header of drivers/net/hamradio/baycom_ser_hdx.c. ++ ++ blkdevparts= Manual partition parsing of block device(s) for ++ embedded devices based on command line input. ++ See Documentation/block/cmdline-partition.txt ++ ++ boot_delay= Milliseconds to delay each printk during boot. ++ Values larger than 10 seconds (10000) are changed to ++ no delay (0). ++ Format: integer ++ ++ bootmem_debug [KNL] Enable bootmem allocator debug messages. ++ ++ bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards) ++ bttv.radio= Most important insmod options are available as ++ kernel args too. ++ bttv.pll= See Documentation/video4linux/bttv/Insmod-options ++ bttv.tuner= ++ ++ bulk_remove=off [PPC] This parameter disables the use of the pSeries ++ firmware feature for flushing multiple hpte entries ++ at a time. ++ ++ c101= [NET] Moxa C101 synchronous serial card ++ ++ cachesize= [BUGS=X86-32] Override level 2 CPU cache size detection. ++ Sometimes CPU hardware bugs make them report the cache ++ size incorrectly. The kernel will attempt work arounds ++ to fix known problems, but for some CPUs it is not ++ possible to determine what the correct size should be. ++ This option provides an override for these situations. ++ ++ ccw_timeout_log [S390] ++ See Documentation/s390/CommonIO for details. ++ ++ cgroup_disable= [KNL] Disable a particular controller ++ Format: {name of the controller(s) to disable} ++ The effects of cgroup_disable=foo are: ++ - foo isn't auto-mounted if you mount all cgroups in ++ a single hierarchy ++ - foo isn't visible as an individually mountable ++ subsystem ++ {Currently only "memory" controller deal with this and ++ cut the overhead, others just disable the usage. So ++ only cgroup_disable=memory is actually worthy} ++ ++ checkreqprot [SELINUX] Set initial checkreqprot flag value. ++ Format: { "0" | "1" } ++ See security/selinux/Kconfig help text. ++ 0 -- check protection applied by kernel (includes ++ any implied execute protection). ++ 1 -- check protection requested by application. ++ Default value is set via a kernel config option. ++ Value can be changed at runtime via ++ /selinux/checkreqprot. ++ ++ cio_ignore= [S390] ++ See Documentation/s390/CommonIO for details. ++ clk_ignore_unused ++ [CLK] ++ Keep all clocks already enabled by bootloader on, ++ even if no driver has claimed them. This is useful ++ for debug and development, but should not be ++ needed on a platform with proper driver support. ++ For more information, see Documentation/clk.txt. ++ ++ clock= [BUGS=X86-32, HW] gettimeofday clocksource override. ++ [Deprecated] ++ Forces specified clocksource (if available) to be used ++ when calculating gettimeofday(). If specified ++ clocksource is not available, it defaults to PIT. ++ Format: { pit | tsc | cyclone | pmtmr } ++ ++ clocksource= Override the default clocksource ++ Format: ++ Override the default clocksource and use the clocksource ++ with the name specified. ++ Some clocksource names to choose from, depending on ++ the platform: ++ [all] jiffies (this is the base, fallback clocksource) ++ [ACPI] acpi_pm ++ [ARM] imx_timer1,OSTS,netx_timer,mpu_timer2, ++ pxa_timer,timer3,32k_counter,timer0_1 ++ [AVR32] avr32 ++ [X86-32] pit,hpet,tsc; ++ scx200_hrt on Geode; cyclone on IBM x440 ++ [MIPS] MIPS ++ [PARISC] cr16 ++ [S390] tod ++ [SH] SuperH ++ [SPARC64] tick ++ [X86-64] hpet,tsc ++ ++ clearcpuid=BITNUM [X86] ++ Disable CPUID feature X for the kernel. See ++ arch/x86/include/asm/cpufeature.h for the valid bit ++ numbers. Note the Linux specific bits are not necessarily ++ stable over kernel options, but the vendor specific ++ ones should be. ++ Also note that user programs calling CPUID directly ++ or using the feature without checking anything ++ will still see it. This just prevents it from ++ being used by the kernel or shown in /proc/cpuinfo. ++ Also note the kernel might malfunction if you disable ++ some critical bits. ++ ++ cma=nn[MG]@[start[MG][-end[MG]]] ++ [ARM,X86,KNL] ++ Sets the size of kernel global memory area for ++ contiguous memory allocations and optionally the ++ placement constraint by the physical address range of ++ memory allocations. For more information, see ++ include/linux/dma-contiguous.h ++ ++ cmo_free_hint= [PPC] Format: { yes | no } ++ Specify whether pages are marked as being inactive ++ when they are freed. This is used in CMO environments ++ to determine OS memory pressure for page stealing by ++ a hypervisor. ++ Default: yes ++ ++ coherent_pool=nn[KMG] [ARM,KNL] ++ Sets the size of memory pool for coherent, atomic dma ++ allocations, by default set to 256K. ++ ++ code_bytes [X86] How many bytes of object code to print ++ in an oops report. ++ Range: 0 - 8192 ++ Default: 64 ++ ++ com20020= [HW,NET] ARCnet - COM20020 chipset ++ Format: ++ [,[,[,[,[,]]]]] ++ ++ com90io= [HW,NET] ARCnet - COM90xx chipset (IO-mapped buffers) ++ Format: [,] ++ ++ com90xx= [HW,NET] ++ ARCnet - COM90xx chipset (memory-mapped buffers) ++ Format: [,[,]] ++ ++ condev= [HW,S390] console device ++ conmode= ++ ++ console= [KNL] Output console device and options. ++ ++ tty Use the virtual console device . ++ ++ ttyS[,options] ++ ttyUSB0[,options] ++ Use the specified serial port. The options are of ++ the form "bbbbpnf", where "bbbb" is the baud rate, ++ "p" is parity ("n", "o", or "e"), "n" is number of ++ bits, and "f" is flow control ("r" for RTS or ++ omit it). Default is "9600n8". ++ ++ See Documentation/serial-console.txt for more ++ information. See ++ Documentation/networking/netconsole.txt for an ++ alternative. ++ ++ uart[8250],io,[,options] ++ uart[8250],mmio,[,options] ++ Start an early, polled-mode console on the 8250/16550 ++ UART at the specified I/O port or MMIO address, ++ switching to the matching ttyS device later. The ++ options are the same as for ttyS, above. ++ hvc Use the hypervisor console device . This is for ++ both Xen and PowerPC hypervisors. ++ ++ If the device connected to the port is not a TTY but a braille ++ device, prepend "brl," before the device type, for instance ++ console=brl,ttyS0 ++ For now, only VisioBraille is supported. ++ ++ consoleblank= [KNL] The console blank (screen saver) timeout in ++ seconds. Defaults to 10*60 = 10mins. A value of 0 ++ disables the blank timer. ++ ++ coredump_filter= ++ [KNL] Change the default value for ++ /proc//coredump_filter. ++ See also Documentation/filesystems/proc.txt. ++ ++ cpuidle.off=1 [CPU_IDLE] ++ disable the cpuidle sub-system ++ ++ cpcihp_generic= [HW,PCI] Generic port I/O CompactPCI driver ++ Format: ++ ,,,[,] ++ ++ crashkernel=size[KMG][@offset[KMG]] ++ [KNL] Using kexec, Linux can switch to a 'crash kernel' ++ upon panic. This parameter reserves the physical ++ memory region [offset, offset + size] for that kernel ++ image. If '@offset' is omitted, then a suitable offset ++ is selected automatically. Check ++ Documentation/kdump/kdump.txt for further details. ++ ++ crashkernel=range1:size1[,range2:size2,...][@offset] ++ [KNL] Same as above, but depends on the memory ++ in the running system. The syntax of range is ++ start-[end] where start and end are both ++ a memory unit (amount[KMG]). See also ++ Documentation/kdump/kdump.txt for an example. ++ ++ crashkernel=size[KMG],high ++ [KNL, x86_64] range could be above 4G. Allow kernel ++ to allocate physical memory region from top, so could ++ be above 4G if system have more than 4G ram installed. ++ Otherwise memory region will be allocated below 4G, if ++ available. ++ It will be ignored if crashkernel=X is specified. ++ crashkernel=size[KMG],low ++ [KNL, x86_64] range under 4G. When crashkernel=X,high ++ is passed, kernel could allocate physical memory region ++ above 4G, that cause second kernel crash on system ++ that require some amount of low memory, e.g. swiotlb ++ requires at least 64M+32K low memory. Kernel would ++ try to allocate 72M below 4G automatically. ++ This one let user to specify own low range under 4G ++ for second kernel instead. ++ 0: to disable low allocation. ++ It will be ignored when crashkernel=X,high is not used ++ or memory reserved is below 4G. ++ ++ cs89x0_dma= [HW,NET] ++ Format: ++ ++ cs89x0_media= [HW,NET] ++ Format: { rj45 | aui | bnc } ++ ++ dasd= [HW,NET] ++ See header of drivers/s390/block/dasd_devmap.c. ++ ++ db9.dev[2|3]= [HW,JOY] Multisystem joystick support via parallel port ++ (one device per port) ++ Format: , ++ See also Documentation/input/joystick-parport.txt ++ ++ ddebug_query= [KNL,DYNAMIC_DEBUG] Enable debug messages at early boot ++ time. See Documentation/dynamic-debug-howto.txt for ++ details. Deprecated, see dyndbg. ++ ++ debug [KNL] Enable kernel debugging (events log level). ++ ++ debug_locks_verbose= ++ [KNL] verbose self-tests ++ Format=<0|1> ++ Print debugging info while doing the locking API ++ self-tests. ++ We default to 0 (no extra messages), setting it to ++ 1 will print _a lot_ more information - normally ++ only useful to kernel developers. ++ ++ debug_objects [KNL] Enable object debugging ++ ++ no_debug_objects ++ [KNL] Disable object debugging ++ ++ debug_guardpage_minorder= ++ [KNL] When CONFIG_DEBUG_PAGEALLOC is set, this ++ parameter allows control of the order of pages that will ++ be intentionally kept free (and hence protected) by the ++ buddy allocator. Bigger value increase the probability ++ of catching random memory corruption, but reduce the ++ amount of memory for normal system use. The maximum ++ possible value is MAX_ORDER/2. Setting this parameter ++ to 1 or 2 should be enough to identify most random ++ memory corruption problems caused by bugs in kernel or ++ driver code when a CPU writes to (or reads from) a ++ random memory location. Note that there exists a class ++ of memory corruptions problems caused by buggy H/W or ++ F/W or by drivers badly programing DMA (basically when ++ memory is written at bus level and the CPU MMU is ++ bypassed) which are not detectable by ++ CONFIG_DEBUG_PAGEALLOC, hence this option will not help ++ tracking down these problems. ++ ++ debugpat [X86] Enable PAT debugging ++ ++ decnet.addr= [HW,NET] ++ Format: [,] ++ See also Documentation/networking/decnet.txt. ++ ++ default_hugepagesz= ++ [same as hugepagesz=] The size of the default ++ HugeTLB page size. This is the size represented by ++ the legacy /proc/ hugepages APIs, used for SHM, and ++ default size when mounting hugetlbfs filesystems. ++ Defaults to the default architecture's huge page size ++ if not specified. ++ ++ dhash_entries= [KNL] ++ Set number of hash buckets for dentry cache. ++ ++ digi= [HW,SERIAL] ++ IO parameters + enable/disable command. ++ ++ digiepca= [HW,SERIAL] ++ See drivers/char/README.epca and ++ Documentation/serial/digiepca.txt. ++ ++ disable= [IPV6] ++ See Documentation/networking/ipv6.txt. ++ ++ disable_cpu_apicid= [X86,APIC,SMP] ++ Format: ++ The number of initial APIC ID for the ++ corresponding CPU to be disabled at boot, ++ mostly used for the kdump 2nd kernel to ++ disable BSP to wake up multiple CPUs without ++ causing system reset or hang due to sending ++ INIT from AP to BSP. ++ ++ disable_ddw [PPC/PSERIES] ++ Disable Dynamic DMA Window support. Use this if ++ to workaround buggy firmware. ++ ++ disable_ipv6= [IPV6] ++ See Documentation/networking/ipv6.txt. ++ ++ disable_mtrr_cleanup [X86] ++ The kernel tries to adjust MTRR layout from continuous ++ to discrete, to make X server driver able to add WB ++ entry later. This parameter disables that. ++ ++ disable_mtrr_trim [X86, Intel and AMD only] ++ By default the kernel will trim any uncacheable ++ memory out of your available memory pool based on ++ MTRR settings. This parameter disables that behavior, ++ possibly causing your machine to run very slowly. ++ ++ disable_timer_pin_1 [X86] ++ Disable PIN 1 of APIC timer ++ Can be useful to work around chipset bugs. ++ ++ dma_debug=off If the kernel is compiled with DMA_API_DEBUG support, ++ this option disables the debugging code at boot. ++ ++ dma_debug_entries= ++ This option allows to tune the number of preallocated ++ entries for DMA-API debugging code. One entry is ++ required per DMA-API allocation. Use this if the ++ DMA-API debugging code disables itself because the ++ architectural default is too low. ++ ++ dma_debug_driver= ++ With this option the DMA-API debugging driver ++ filter feature can be enabled at boot time. Just ++ pass the driver to filter for as the parameter. ++ The filter can be disabled or changed to another ++ driver later using sysfs. ++ ++ drm_kms_helper.edid_firmware=[:] ++ Broken monitors, graphic adapters and KVMs may ++ send no or incorrect EDID data sets. This parameter ++ allows to specify an EDID data set in the ++ /lib/firmware directory that is used instead. ++ Generic built-in EDID data sets are used, if one of ++ edid/1024x768.bin, edid/1280x1024.bin, ++ edid/1680x1050.bin, or edid/1920x1080.bin is given ++ and no file with the same name exists. Details and ++ instructions how to build your own EDID data are ++ available in Documentation/EDID/HOWTO.txt. An EDID ++ data set will only be used for a particular connector, ++ if its name and a colon are prepended to the EDID ++ name. ++ ++ dscc4.setup= [NET] ++ ++ dyndbg[="val"] [KNL,DYNAMIC_DEBUG] ++ module.dyndbg[="val"] ++ Enable debug messages at boot time. See ++ Documentation/dynamic-debug-howto.txt for details. ++ ++ earlycon= [KNL] Output early console device and options. ++ uart[8250],io,[,options] ++ uart[8250],mmio,[,options] ++ uart[8250],mmio32,[,options] ++ Start an early, polled-mode console on the 8250/16550 ++ UART at the specified I/O port or MMIO address. ++ MMIO inter-register address stride is either 8-bit ++ (mmio) or 32-bit (mmio32). ++ The options are the same as for ttyS, above. ++ ++ earlyprintk= [X86,SH,BLACKFIN,ARM] ++ earlyprintk=vga ++ earlyprintk=efi ++ earlyprintk=xen ++ earlyprintk=serial[,ttySn[,baudrate]] ++ earlyprintk=serial[,0x...[,baudrate]] ++ earlyprintk=ttySn[,baudrate] ++ earlyprintk=dbgp[debugController#] ++ ++ earlyprintk is useful when the kernel crashes before ++ the normal console is initialized. It is not enabled by ++ default because it has some cosmetic problems. ++ ++ Append ",keep" to not disable it when the real console ++ takes over. ++ ++ Only one of vga, efi, serial, or usb debug port can ++ be used at a time. ++ ++ Currently only ttyS0 and ttyS1 may be specified by ++ name. Other I/O ports may be explicitly specified ++ on some architectures (x86 and arm at least) by ++ replacing ttySn with an I/O port address, like this: ++ earlyprintk=serial,0x1008,115200 ++ You can find the port for a given device in ++ /proc/tty/driver/serial: ++ 2: uart:ST16650V2 port:00001008 irq:18 ... ++ ++ Interaction with the standard serial driver is not ++ very good. ++ ++ The VGA and EFI output is eventually overwritten by ++ the real console. ++ ++ The xen output can only be used by Xen PV guests. ++ ++ edac_report= [HW,EDAC] Control how to report EDAC event ++ Format: {"on" | "off" | "force"} ++ on: enable EDAC to report H/W event. May be overridden ++ by other higher priority error reporting module. ++ off: disable H/W event reporting through EDAC. ++ force: enforce the use of EDAC to report H/W event. ++ default: on. ++ ++ ekgdboc= [X86,KGDB] Allow early kernel console debugging ++ ekgdboc=kbd ++ ++ This is designed to be used in conjunction with ++ the boot argument: earlyprintk=vga ++ ++ edd= [EDD] ++ Format: {"off" | "on" | "skip[mbr]"} ++ ++ efi= [EFI] ++ Format: { "old_map" } ++ old_map [X86-64]: switch to the old ioremap-based EFI ++ runtime services mapping. 32-bit still uses this one by ++ default. ++ ++ efi_no_storage_paranoia [EFI; X86] ++ Using this parameter you can use more than 50% of ++ your efi variable storage. Use this parameter only if ++ you are really sure that your UEFI does sane gc and ++ fulfills the spec otherwise your board may brick. ++ ++ eisa_irq_edge= [PARISC,HW] ++ See header of drivers/parisc/eisa.c. ++ ++ elanfreq= [X86-32] ++ See comment before function elanfreq_setup() in ++ arch/x86/kernel/cpu/cpufreq/elanfreq.c. ++ ++ elevator= [IOSCHED] ++ Format: {"cfq" | "deadline" | "noop"} ++ See Documentation/block/cfq-iosched.txt and ++ Documentation/block/deadline-iosched.txt for details. ++ ++ elfcorehdr=[size[KMG]@]offset[KMG] [IA64,PPC,SH,X86,S390] ++ Specifies physical address of start of kernel core ++ image elf header and optionally the size. Generally ++ kexec loader will pass this option to capture kernel. ++ See Documentation/kdump/kdump.txt for details. ++ ++ enable_mtrr_cleanup [X86] ++ The kernel tries to adjust MTRR layout from continuous ++ to discrete, to make X server driver able to add WB ++ entry later. This parameter enables that. ++ ++ enable_timer_pin_1 [X86] ++ Enable PIN 1 of APIC timer ++ Can be useful to work around chipset bugs ++ (in particular on some ATI chipsets). ++ The kernel tries to set a reasonable default. ++ ++ enforcing [SELINUX] Set initial enforcing status. ++ Format: {"0" | "1"} ++ See security/selinux/Kconfig help text. ++ 0 -- permissive (log only, no denials). ++ 1 -- enforcing (deny and log). ++ Default value is 0. ++ Value can be changed at runtime via /selinux/enforce. ++ ++ erst_disable [ACPI] ++ Disable Error Record Serialization Table (ERST) ++ support. ++ ++ ether= [HW,NET] Ethernet cards parameters ++ This option is obsoleted by the "netdev=" option, which ++ has equivalent usage. See its documentation for details. ++ ++ evm= [EVM] ++ Format: { "fix" } ++ Permit 'security.evm' to be updated regardless of ++ current integrity status. ++ ++ failslab= ++ fail_page_alloc= ++ fail_make_request=[KNL] ++ General fault injection mechanism. ++ Format: ,,, ++ See also Documentation/fault-injection/. ++ ++ floppy= [HW] ++ See Documentation/blockdev/floppy.txt. ++ ++ force_pal_cache_flush ++ [IA-64] Avoid check_sal_cache_flush which may hang on ++ buggy SAL_CACHE_FLUSH implementations. Using this ++ parameter will force ia64_sal_cache_flush to call ++ ia64_pal_cache_flush instead of SAL_CACHE_FLUSH. ++ ++ ftrace=[tracer] ++ [FTRACE] will set and start the specified tracer ++ as early as possible in order to facilitate early ++ boot debugging. ++ ++ ftrace_dump_on_oops[=orig_cpu] ++ [FTRACE] will dump the trace buffers on oops. ++ If no parameter is passed, ftrace will dump ++ buffers of all CPUs, but if you pass orig_cpu, it will ++ dump only the buffer of the CPU that triggered the ++ oops. ++ ++ ftrace_filter=[function-list] ++ [FTRACE] Limit the functions traced by the function ++ tracer at boot up. function-list is a comma separated ++ list of functions. This list can be changed at run ++ time by the set_ftrace_filter file in the debugfs ++ tracing directory. ++ ++ ftrace_notrace=[function-list] ++ [FTRACE] Do not trace the functions specified in ++ function-list. This list can be changed at run time ++ by the set_ftrace_notrace file in the debugfs ++ tracing directory. ++ ++ ftrace_graph_filter=[function-list] ++ [FTRACE] Limit the top level callers functions traced ++ by the function graph tracer at boot up. ++ function-list is a comma separated list of functions ++ that can be changed at run time by the ++ set_graph_function file in the debugfs tracing directory. ++ ++ gamecon.map[2|3]= ++ [HW,JOY] Multisystem joystick and NES/SNES/PSX pad ++ support via parallel port (up to 5 devices per port) ++ Format: ,,,,, ++ See also Documentation/input/joystick-parport.txt ++ ++ gamma= [HW,DRM] ++ ++ gart_fix_e820= [X86_64] disable the fix e820 for K8 GART ++ Format: off | on ++ default: on ++ ++ gcov_persist= [GCOV] When non-zero (default), profiling data for ++ kernel modules is saved and remains accessible via ++ debugfs, even when the module is unloaded/reloaded. ++ When zero, profiling data is discarded and associated ++ debugfs files are removed at module unload time. ++ ++ gpt [EFI] Forces disk with valid GPT signature but ++ invalid Protective MBR to be treated as GPT. If the ++ primary GPT is corrupted, it enables the backup/alternate ++ GPT to be used instead. ++ ++ grcan.enable0= [HW] Configuration of physical interface 0. Determines ++ the "Enable 0" bit of the configuration register. ++ Format: 0 | 1 ++ Default: 0 ++ grcan.enable1= [HW] Configuration of physical interface 1. Determines ++ the "Enable 0" bit of the configuration register. ++ Format: 0 | 1 ++ Default: 0 ++ grcan.select= [HW] Select which physical interface to use. ++ Format: 0 | 1 ++ Default: 0 ++ grcan.txsize= [HW] Sets the size of the tx buffer. ++ Format: such that (txsize & ~0x1fffc0) == 0. ++ Default: 1024 ++ grcan.rxsize= [HW] Sets the size of the rx buffer. ++ Format: such that (rxsize & ~0x1fffc0) == 0. ++ Default: 1024 ++ ++ hashdist= [KNL,NUMA] Large hashes allocated during boot ++ are distributed across NUMA nodes. Defaults on ++ for 64-bit NUMA, off otherwise. ++ Format: 0 | 1 (for off | on) ++ ++ hcl= [IA-64] SGI's Hardware Graph compatibility layer ++ ++ hd= [EIDE] (E)IDE hard drive subsystem geometry ++ Format: ,, ++ ++ hest_disable [ACPI] ++ Disable Hardware Error Source Table (HEST) support; ++ corresponding firmware-first mode error processing ++ logic will be disabled. ++ ++ highmem=nn[KMG] [KNL,BOOT] forces the highmem zone to have an exact ++ size of . This works even on boxes that have no ++ highmem otherwise. This also works to reduce highmem ++ size on bigger boxes. ++ ++ highres= [KNL] Enable/disable high resolution timer mode. ++ Valid parameters: "on", "off" ++ Default: "on" ++ ++ hisax= [HW,ISDN] ++ See Documentation/isdn/README.HiSax. ++ ++ hlt [BUGS=ARM,SH] ++ ++ hpet= [X86-32,HPET] option to control HPET usage ++ Format: { enable (default) | disable | force | ++ verbose } ++ disable: disable HPET and use PIT instead ++ force: allow force enabled of undocumented chips (ICH4, ++ VIA, nVidia) ++ verbose: show contents of HPET registers during setup ++ ++ hpet_mmap= [X86, HPET_MMAP] Allow userspace to mmap HPET ++ registers. Default set by CONFIG_HPET_MMAP_DEFAULT. ++ ++ hugepages= [HW,X86-32,IA-64] HugeTLB pages to allocate at boot. ++ hugepagesz= [HW,IA-64,PPC,X86-64] The size of the HugeTLB pages. ++ On x86-64 and powerpc, this option can be specified ++ multiple times interleaved with hugepages= to reserve ++ huge pages of different sizes. Valid pages sizes on ++ x86-64 are 2M (when the CPU supports "pse") and 1G ++ (when the CPU supports the "pdpe1gb" cpuinfo flag) ++ Note that 1GB pages can only be allocated at boot time ++ using hugepages= and not freed afterwards. ++ ++ hvc_iucv= [S390] Number of z/VM IUCV hypervisor console (HVC) ++ terminal devices. Valid values: 0..8 ++ hvc_iucv_allow= [S390] Comma-separated list of z/VM user IDs. ++ If specified, z/VM IUCV HVC accepts connections ++ from listed z/VM user IDs only. ++ ++ hwthread_map= [METAG] Comma-separated list of Linux cpu id to ++ hardware thread id mappings. ++ Format: : ++ ++ keep_bootcon [KNL] ++ Do not unregister boot console at start. This is only ++ useful for debugging when something happens in the window ++ between unregistering the boot console and initializing ++ the real console. ++ ++ i2c_bus= [HW] Override the default board specific I2C bus speed ++ or register an additional I2C bus that is not ++ registered from board initialization code. ++ Format: ++ , ++ ++ i8042.debug [HW] Toggle i8042 debug mode ++ i8042.direct [HW] Put keyboard port into non-translated mode ++ i8042.dumbkbd [HW] Pretend that controller can only read data from ++ keyboard and cannot control its state ++ (Don't attempt to blink the leds) ++ i8042.noaux [HW] Don't check for auxiliary (== mouse) port ++ i8042.nokbd [HW] Don't check/create keyboard port ++ i8042.noloop [HW] Disable the AUX Loopback command while probing ++ for the AUX port ++ i8042.nomux [HW] Don't check presence of an active multiplexing ++ controller ++ i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX ++ controllers ++ i8042.notimeout [HW] Ignore timeout condition signalled by controller ++ i8042.reset [HW] Reset the controller during init and cleanup ++ i8042.unlock [HW] Unlock (ignore) the keylock ++ ++ i810= [HW,DRM] ++ ++ i8k.ignore_dmi [HW] Continue probing hardware even if DMI data ++ indicates that the driver is running on unsupported ++ hardware. ++ i8k.force [HW] Activate i8k driver even if SMM BIOS signature ++ does not match list of supported models. ++ i8k.power_status ++ [HW] Report power status in /proc/i8k ++ (disabled by default) ++ i8k.restricted [HW] Allow controlling fans only if SYS_ADMIN ++ capability is set. ++ ++ i915.invert_brightness= ++ [DRM] Invert the sense of the variable that is used to ++ set the brightness of the panel backlight. Normally a ++ brightness value of 0 indicates backlight switched off, ++ and the maximum of the brightness value sets the backlight ++ to maximum brightness. If this parameter is set to 0 ++ (default) and the machine requires it, or this parameter ++ is set to 1, a brightness value of 0 sets the backlight ++ to maximum brightness, and the maximum of the brightness ++ value switches the backlight off. ++ -1 -- never invert brightness ++ 0 -- machine default ++ 1 -- force brightness inversion ++ ++ icn= [HW,ISDN] ++ Format: [,[,[,]]] ++ ++ ide-core.nodma= [HW] (E)IDE subsystem ++ Format: =0.0 to prevent dma on hda, =0.1 hdb =1.0 hdc ++ .vlb_clock .pci_clock .noflush .nohpa .noprobe .nowerr ++ .cdrom .chs .ignore_cable are additional options ++ See Documentation/ide/ide.txt. ++ ++ ide-pci-generic.all-generic-ide [HW] (E)IDE subsystem ++ Claim all unknown PCI IDE storage controllers. ++ ++ idle= [X86] ++ Format: idle=poll, idle=halt, idle=nomwait ++ Poll forces a polling idle loop that can slightly ++ improve the performance of waking up a idle CPU, but ++ will use a lot of power and make the system run hot. ++ Not recommended. ++ idle=halt: Halt is forced to be used for CPU idle. ++ In such case C2/C3 won't be used again. ++ idle=nomwait: Disable mwait for CPU C-states ++ ++ ignore_loglevel [KNL] ++ Ignore loglevel setting - this will print /all/ ++ kernel messages to the console. Useful for debugging. ++ We also add it as printk module parameter, so users ++ could change it dynamically, usually by ++ /sys/module/printk/parameters/ignore_loglevel. ++ ++ ihash_entries= [KNL] ++ Set number of hash buckets for inode cache. ++ ++ ima_appraise= [IMA] appraise integrity measurements ++ Format: { "off" | "enforce" | "fix" } ++ default: "enforce" ++ ++ ima_appraise_tcb [IMA] ++ The builtin appraise policy appraises all files ++ owned by uid=0. ++ ++ ima_hash= [IMA] ++ Format: { md5 | sha1 | rmd160 | sha256 | sha384 ++ | sha512 | ... } ++ default: "sha1" ++ ++ The list of supported hash algorithms is defined ++ in crypto/hash_info.h. ++ ++ ima_tcb [IMA] ++ Load a policy which meets the needs of the Trusted ++ Computing Base. This means IMA will measure all ++ programs exec'd, files mmap'd for exec, and all files ++ opened for read by uid=0. ++ ++ ima_template= [IMA] ++ Select one of defined IMA measurements template formats. ++ Formats: { "ima" | "ima-ng" } ++ Default: "ima-ng" ++ ++ init= [KNL] ++ Format: ++ Run specified binary instead of /sbin/init as init ++ process. ++ ++ initcall_debug [KNL] Trace initcalls as they are executed. Useful ++ for working out where the kernel is dying during ++ startup. ++ ++ initrd= [BOOT] Specify the location of the initial ramdisk ++ ++ inport.irq= [HW] Inport (ATI XL and Microsoft) busmouse driver ++ Format: ++ ++ int_pln_enable [x86] Enable power limit notification interrupt ++ ++ integrity_audit=[IMA] ++ Format: { "0" | "1" } ++ 0 -- basic integrity auditing messages. (Default) ++ 1 -- additional integrity auditing messages. ++ ++ intel_iommu= [DMAR] Intel IOMMU driver (DMAR) option ++ on ++ Enable intel iommu driver. ++ off ++ Disable intel iommu driver. ++ igfx_off [Default Off] ++ By default, gfx is mapped as normal device. If a gfx ++ device has a dedicated DMAR unit, the DMAR unit is ++ bypassed by not enabling DMAR with this option. In ++ this case, gfx device will use physical address for ++ DMA. ++ forcedac [x86_64] ++ With this option iommu will not optimize to look ++ for io virtual address below 32-bit forcing dual ++ address cycle on pci bus for cards supporting greater ++ than 32-bit addressing. The default is to look ++ for translation below 32-bit and if not available ++ then look in the higher range. ++ strict [Default Off] ++ With this option on every unmap_single operation will ++ result in a hardware IOTLB flush operation as opposed ++ to batching them for performance. ++ sp_off [Default Off] ++ By default, super page will be supported if Intel IOMMU ++ has the capability. With this option, super page will ++ not be supported. ++ ++ intel_idle.max_cstate= [KNL,HW,ACPI,X86] ++ 0 disables intel_idle and fall back on acpi_idle. ++ 1 to 6 specify maximum depth of C-state. ++ ++ intel_pstate= [X86] ++ disable ++ Do not enable intel_pstate as the default ++ scaling driver for the supported processors ++ ++ intremap= [X86-64, Intel-IOMMU] ++ on enable Interrupt Remapping (default) ++ off disable Interrupt Remapping ++ nosid disable Source ID checking ++ no_x2apic_optout ++ BIOS x2APIC opt-out request will be ignored ++ ++ iomem= Disable strict checking of access to MMIO memory ++ strict regions from userspace. ++ relaxed ++ ++ iommu= [x86] ++ off ++ force ++ noforce ++ biomerge ++ panic ++ nopanic ++ merge ++ nomerge ++ forcesac ++ soft ++ pt [x86, IA-64] ++ ++ ++ io7= [HW] IO7 for Marvel based alpha systems ++ See comment before marvel_specify_io7 in ++ arch/alpha/kernel/core_marvel.c. ++ ++ io_delay= [X86] I/O delay method ++ 0x80 ++ Standard port 0x80 based delay ++ 0xed ++ Alternate port 0xed based delay (needed on some systems) ++ udelay ++ Simple two microseconds delay ++ none ++ No delay ++ ++ ip= [IP_PNP] ++ See Documentation/filesystems/nfs/nfsroot.txt. ++ ++ ip2= [HW] Set IO/IRQ pairs for up to 4 IntelliPort boards ++ See comment before ip2_setup() in ++ drivers/char/ip2/ip2base.c. ++ ++ irqfixup [HW] ++ When an interrupt is not handled search all handlers ++ for it. Intended to get systems with badly broken ++ firmware running. ++ ++ irqpoll [HW] ++ When an interrupt is not handled search all handlers ++ for it. Also check all handlers each timer ++ interrupt. Intended to get systems with badly broken ++ firmware running. ++ ++ isapnp= [ISAPNP] ++ Format: ,,, ++ ++ isolcpus= [KNL,SMP] Isolate CPUs from the general scheduler. ++ Format: ++ ,..., ++ or ++ - ++ (must be a positive range in ascending order) ++ or a mixture ++ ,...,- ++ ++ This option can be used to specify one or more CPUs ++ to isolate from the general SMP balancing and scheduling ++ algorithms. You can move a process onto or off an ++ "isolated" CPU via the CPU affinity syscalls or cpuset. ++ begins at 0 and the maximum value is ++ "number of CPUs in system - 1". ++ ++ This option is the preferred way to isolate CPUs. The ++ alternative -- manually setting the CPU mask of all ++ tasks in the system -- can cause problems and ++ suboptimal load balancer performance. ++ ++ iucv= [HW,NET] ++ ++ ivrs_ioapic [HW,X86_64] ++ Provide an override to the IOAPIC-ID<->DEVICE-ID ++ mapping provided in the IVRS ACPI table. For ++ example, to map IOAPIC-ID decimal 10 to ++ PCI device 00:14.0 write the parameter as: ++ ivrs_ioapic[10]=00:14.0 ++ ++ ivrs_hpet [HW,X86_64] ++ Provide an override to the HPET-ID<->DEVICE-ID ++ mapping provided in the IVRS ACPI table. For ++ example, to map HPET-ID decimal 0 to ++ PCI device 00:14.0 write the parameter as: ++ ivrs_hpet[0]=00:14.0 ++ ++ js= [HW,JOY] Analog joystick ++ See Documentation/input/joystick.txt. ++ ++ keepinitrd [HW,ARM] ++ ++ kernelcore=nn[KMG] [KNL,X86,IA-64,PPC] This parameter ++ specifies the amount of memory usable by the kernel ++ for non-movable allocations. The requested amount is ++ spread evenly throughout all nodes in the system. The ++ remaining memory in each node is used for Movable ++ pages. In the event, a node is too small to have both ++ kernelcore and Movable pages, kernelcore pages will ++ take priority and other nodes will have a larger number ++ of Movable pages. The Movable zone is used for the ++ allocation of pages that may be reclaimed or moved ++ by the page migration subsystem. This means that ++ HugeTLB pages may not be allocated from this zone. ++ Note that allocations like PTEs-from-HighMem still ++ use the HighMem zone if it exists, and the Normal ++ zone if it does not. ++ ++ kgdbdbgp= [KGDB,HW] kgdb over EHCI usb debug port. ++ Format: [,poll interval] ++ The controller # is the number of the ehci usb debug ++ port as it is probed via PCI. The poll interval is ++ optional and is the number seconds in between ++ each poll cycle to the debug port in case you need ++ the functionality for interrupting the kernel with ++ gdb or control-c on the dbgp connection. When ++ not using this parameter you use sysrq-g to break into ++ the kernel debugger. ++ ++ kgdboc= [KGDB,HW] kgdb over consoles. ++ Requires a tty driver that supports console polling, ++ or a supported polling keyboard driver (non-usb). ++ Serial only format: [,baud] ++ keyboard only format: kbd ++ keyboard and serial format: kbd,[,baud] ++ Optional Kernel mode setting: ++ kms, kbd format: kms,kbd ++ kms, kbd and serial format: kms,kbd,[,baud] ++ ++ kgdbwait [KGDB] Stop kernel execution and enter the ++ kernel debugger at the earliest opportunity. ++ ++ kmac= [MIPS] korina ethernet MAC address. ++ Configure the RouterBoard 532 series on-chip ++ Ethernet adapter MAC address. ++ ++ kmemleak= [KNL] Boot-time kmemleak enable/disable ++ Valid arguments: on, off ++ Default: on ++ ++ kmemcheck= [X86] Boot-time kmemcheck enable/disable/one-shot mode ++ Valid arguments: 0, 1, 2 ++ kmemcheck=0 (disabled) ++ kmemcheck=1 (enabled) ++ kmemcheck=2 (one-shot mode) ++ Default: 2 (one-shot mode) ++ ++ kstack=N [X86] Print N words from the kernel stack ++ in oops dumps. ++ ++ kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs. ++ Default is 0 (don't ignore, but inject #GP) ++ ++ kvm.mmu_audit= [KVM] This is a R/W parameter which allows audit ++ KVM MMU at runtime. ++ Default is 0 (off) ++ ++ kvm-amd.nested= [KVM,AMD] Allow nested virtualization in KVM/SVM. ++ Default is 1 (enabled) ++ ++ kvm-amd.npt= [KVM,AMD] Disable nested paging (virtualized MMU) ++ for all guests. ++ Default is 1 (enabled) if in 64-bit or 32-bit PAE mode. ++ ++ kvm-intel.ept= [KVM,Intel] Disable extended page tables ++ (virtualized MMU) support on capable Intel chips. ++ Default is 1 (enabled) ++ ++ kvm-intel.emulate_invalid_guest_state= ++ [KVM,Intel] Enable emulation of invalid guest states ++ Default is 0 (disabled) ++ ++ kvm-intel.flexpriority= ++ [KVM,Intel] Disable FlexPriority feature (TPR shadow). ++ Default is 1 (enabled) ++ ++ kvm-intel.nested= ++ [KVM,Intel] Enable VMX nesting (nVMX). ++ Default is 0 (disabled) ++ ++ kvm-intel.unrestricted_guest= ++ [KVM,Intel] Disable unrestricted guest feature ++ (virtualized real and unpaged mode) on capable ++ Intel chips. Default is 1 (enabled) ++ ++ kvm-intel.vpid= [KVM,Intel] Disable Virtual Processor Identification ++ feature (tagged TLBs) on capable Intel chips. ++ Default is 1 (enabled) ++ ++ l2cr= [PPC] ++ ++ l3cr= [PPC] ++ ++ lapic [X86-32,APIC] Enable the local APIC even if BIOS ++ disabled it. ++ ++ lapic= [x86,APIC] "notscdeadline" Do not use TSC deadline ++ value for LAPIC timer one-shot implementation. Default ++ back to the programmable timer unit in the LAPIC. ++ ++ lapic_timer_c2_ok [X86,APIC] trust the local apic timer ++ in C2 power state. ++ ++ libata.dma= [LIBATA] DMA control ++ libata.dma=0 Disable all PATA and SATA DMA ++ libata.dma=1 PATA and SATA Disk DMA only ++ libata.dma=2 ATAPI (CDROM) DMA only ++ libata.dma=4 Compact Flash DMA only ++ Combinations also work, so libata.dma=3 enables DMA ++ for disks and CDROMs, but not CFs. ++ ++ libata.ignore_hpa= [LIBATA] Ignore HPA limit ++ libata.ignore_hpa=0 keep BIOS limits (default) ++ libata.ignore_hpa=1 ignore limits, using full disk ++ ++ libata.noacpi [LIBATA] Disables use of ACPI in libata suspend/resume ++ when set. ++ Format: ++ ++ libata.force= [LIBATA] Force configurations. The format is comma ++ separated list of "[ID:]VAL" where ID is ++ PORT[.DEVICE]. PORT and DEVICE are decimal numbers ++ matching port, link or device. Basically, it matches ++ the ATA ID string printed on console by libata. If ++ the whole ID part is omitted, the last PORT and DEVICE ++ values are used. If ID hasn't been specified yet, the ++ configuration applies to all ports, links and devices. ++ ++ If only DEVICE is omitted, the parameter applies to ++ the port and all links and devices behind it. DEVICE ++ number of 0 either selects the first device or the ++ first fan-out link behind PMP device. It does not ++ select the host link. DEVICE number of 15 selects the ++ host link and device attached to it. ++ ++ The VAL specifies the configuration to force. As long ++ as there's no ambiguity shortcut notation is allowed. ++ For example, both 1.5 and 1.5G would work for 1.5Gbps. ++ The following configurations can be forced. ++ ++ * Cable type: 40c, 80c, short40c, unk, ign or sata. ++ Any ID with matching PORT is used. ++ ++ * SATA link speed limit: 1.5Gbps or 3.0Gbps. ++ ++ * Transfer mode: pio[0-7], mwdma[0-4] and udma[0-7]. ++ udma[/][16,25,33,44,66,100,133] notation is also ++ allowed. ++ ++ * [no]ncq: Turn on or off NCQ. ++ ++ * nohrst, nosrst, norst: suppress hard, soft ++ and both resets. ++ ++ * rstonce: only attempt one reset during ++ hot-unplug link recovery ++ ++ * dump_id: dump IDENTIFY data. ++ ++ * atapi_dmadir: Enable ATAPI DMADIR bridge support ++ ++ * disable: Disable this device. ++ ++ If there are multiple matching configurations changing ++ the same attribute, the last one is used. ++ ++ memblock=debug [KNL] Enable memblock debug messages. ++ ++ load_ramdisk= [RAM] List of ramdisks to load from floppy ++ See Documentation/blockdev/ramdisk.txt. ++ ++ lockd.nlm_grace_period=P [NFS] Assign grace period. ++ Format: ++ ++ lockd.nlm_tcpport=N [NFS] Assign TCP port. ++ Format: ++ ++ lockd.nlm_timeout=T [NFS] Assign timeout value. ++ Format: ++ ++ lockd.nlm_udpport=M [NFS] Assign UDP port. ++ Format: ++ ++ logibm.irq= [HW,MOUSE] Logitech Bus Mouse Driver ++ Format: ++ ++ loglevel= All Kernel Messages with a loglevel smaller than the ++ console loglevel will be printed to the console. It can ++ also be changed with klogd or other programs. The ++ loglevels are defined as follows: ++ ++ 0 (KERN_EMERG) system is unusable ++ 1 (KERN_ALERT) action must be taken immediately ++ 2 (KERN_CRIT) critical conditions ++ 3 (KERN_ERR) error conditions ++ 4 (KERN_WARNING) warning conditions ++ 5 (KERN_NOTICE) normal but significant condition ++ 6 (KERN_INFO) informational ++ 7 (KERN_DEBUG) debug-level messages ++ ++ log_buf_len=n[KMG] Sets the size of the printk ring buffer, ++ in bytes. n must be a power of two. The default ++ size is set in the kernel config file. ++ ++ logo.nologo [FB] Disables display of the built-in Linux logo. ++ This may be used to provide more screen space for ++ kernel log messages and is useful when debugging ++ kernel boot problems. ++ ++ lp=0 [LP] Specify parallel ports to use, e.g, ++ lp=port[,port...] lp=none,parport0 (lp0 not configured, lp1 uses ++ lp=reset first parallel port). 'lp=0' disables the ++ lp=auto printer driver. 'lp=reset' (which can be ++ specified in addition to the ports) causes ++ attached printers to be reset. Using ++ lp=port1,port2,... specifies the parallel ports ++ to associate lp devices with, starting with ++ lp0. A port specification may be 'none' to skip ++ that lp device, or a parport name such as ++ 'parport0'. Specifying 'lp=auto' instead of a ++ port specification list means that device IDs ++ from each port should be examined, to see if ++ an IEEE 1284-compliant printer is attached; if ++ so, the driver will manage that printer. ++ See also header of drivers/char/lp.c. ++ ++ lpj=n [KNL] ++ Sets loops_per_jiffy to given constant, thus avoiding ++ time-consuming boot-time autodetection (up to 250 ms per ++ CPU). 0 enables autodetection (default). To determine ++ the correct value for your kernel, boot with normal ++ autodetection and see what value is printed. Note that ++ on SMP systems the preset will be applied to all CPUs, ++ which is likely to cause problems if your CPUs need ++ significantly divergent settings. An incorrect value ++ will cause delays in the kernel to be wrong, leading to ++ unpredictable I/O errors and other breakage. Although ++ unlikely, in the extreme case this might damage your ++ hardware. ++ ++ ltpc= [NET] ++ Format: ,, ++ ++ machvec= [IA-64] Force the use of a particular machine-vector ++ (machvec) in a generic kernel. ++ Example: machvec=hpzx1_swiotlb ++ ++ machtype= [Loongson] Share the same kernel image file between different ++ yeeloong laptop. ++ Example: machtype=lemote-yeeloong-2f-7inch ++ ++ max_addr=nn[KMG] [KNL,BOOT,ia64] All physical memory greater ++ than or equal to this physical address is ignored. ++ ++ maxcpus= [SMP] Maximum number of processors that an SMP kernel ++ should make use of. maxcpus=n : n >= 0 limits the ++ kernel to using 'n' processors. n=0 is a special case, ++ it is equivalent to "nosmp", which also disables ++ the IO APIC. ++ ++ max_loop= [LOOP] The number of loop block devices that get ++ (loop.max_loop) unconditionally pre-created at init time. The default ++ number is configured by BLK_DEV_LOOP_MIN_COUNT. Instead ++ of statically allocating a predefined number, loop ++ devices can be requested on-demand with the ++ /dev/loop-control interface. ++ ++ mce [X86-32] Machine Check Exception ++ ++ mce=option [X86-64] See Documentation/x86/x86_64/boot-options.txt ++ ++ md= [HW] RAID subsystems devices and level ++ See Documentation/md.txt. ++ ++ mdacon= [MDA] ++ Format: , ++ Specifies range of consoles to be captured by the MDA. ++ ++ mem=nn[KMG] [KNL,BOOT] Force usage of a specific amount of memory ++ Amount of memory to be used when the kernel is not able ++ to see the whole system memory or for test. ++ [X86] Work as limiting max address. Use together ++ with memmap= to avoid physical address space collisions. ++ Without memmap= PCI devices could be placed at addresses ++ belonging to unused RAM. ++ ++ mem=nopentium [BUGS=X86-32] Disable usage of 4MB pages for kernel ++ memory. ++ ++ memchunk=nn[KMG] ++ [KNL,SH] Allow user to override the default size for ++ per-device physically contiguous DMA buffers. ++ ++ memmap=exactmap [KNL,X86] Enable setting of an exact ++ E820 memory map, as specified by the user. ++ Such memmap=exactmap lines can be constructed based on ++ BIOS output or other requirements. See the memmap=nn@ss ++ option description. ++ ++ memmap=nn[KMG]@ss[KMG] ++ [KNL] Force usage of a specific region of memory. ++ Region of memory to be used is from ss to ss+nn. ++ ++ memmap=nn[KMG]#ss[KMG] ++ [KNL,ACPI] Mark specific memory as ACPI data. ++ Region of memory to be marked is from ss to ss+nn. ++ ++ memmap=nn[KMG]$ss[KMG] ++ [KNL,ACPI] Mark specific memory as reserved. ++ Region of memory to be reserved is from ss to ss+nn. ++ Example: Exclude memory from 0x18690000-0x1869ffff ++ memmap=64K$0x18690000 ++ or ++ memmap=0x10000$0x18690000 ++ ++ memory_corruption_check=0/1 [X86] ++ Some BIOSes seem to corrupt the first 64k of ++ memory when doing things like suspend/resume. ++ Setting this option will scan the memory ++ looking for corruption. Enabling this will ++ both detect corruption and prevent the kernel ++ from using the memory being corrupted. ++ However, its intended as a diagnostic tool; if ++ repeatable BIOS-originated corruption always ++ affects the same memory, you can use memmap= ++ to prevent the kernel from using that memory. ++ ++ memory_corruption_check_size=size [X86] ++ By default it checks for corruption in the low ++ 64k, making this memory unavailable for normal ++ use. Use this parameter to scan for ++ corruption in more or less memory. ++ ++ memory_corruption_check_period=seconds [X86] ++ By default it checks for corruption every 60 ++ seconds. Use this parameter to check at some ++ other rate. 0 disables periodic checking. ++ ++ memtest= [KNL,X86] Enable memtest ++ Format: ++ default : 0 ++ Specifies the number of memtest passes to be ++ performed. Each pass selects another test ++ pattern from a given set of patterns. Memtest ++ fills the memory with this pattern, validates ++ memory contents and reserves bad memory ++ regions that are detected. ++ ++ meye.*= [HW] Set MotionEye Camera parameters ++ See Documentation/video4linux/meye.txt. ++ ++ mfgpt_irq= [IA-32] Specify the IRQ to use for the ++ Multi-Function General Purpose Timers on AMD Geode ++ platforms. ++ ++ mfgptfix [X86-32] Fix MFGPT timers on AMD Geode platforms when ++ the BIOS has incorrectly applied a workaround. TinyBIOS ++ version 0.98 is known to be affected, 0.99 fixes the ++ problem by letting the user disable the workaround. ++ ++ mga= [HW,DRM] ++ ++ min_addr=nn[KMG] [KNL,BOOT,ia64] All physical memory below this ++ physical address is ignored. ++ ++ mini2440= [ARM,HW,KNL] ++ Format:[0..2][b][c][t] ++ Default: "0tb" ++ MINI2440 configuration specification: ++ 0 - The attached screen is the 3.5" TFT ++ 1 - The attached screen is the 7" TFT ++ 2 - The VGA Shield is attached (1024x768) ++ Leaving out the screen size parameter will not load ++ the TFT driver, and the framebuffer will be left ++ unconfigured. ++ b - Enable backlight. The TFT backlight pin will be ++ linked to the kernel VESA blanking code and a GPIO ++ LED. This parameter is not necessary when using the ++ VGA shield. ++ c - Enable the s3c camera interface. ++ t - Reserved for enabling touchscreen support. The ++ touchscreen support is not enabled in the mainstream ++ kernel as of 2.6.30, a preliminary port can be found ++ in the "bleeding edge" mini2440 support kernel at ++ http://repo.or.cz/w/linux-2.6/mini2440.git ++ ++ mminit_loglevel= ++ [KNL] When CONFIG_DEBUG_MEMORY_INIT is set, this ++ parameter allows control of the logging verbosity for ++ the additional memory initialisation checks. A value ++ of 0 disables mminit logging and a level of 4 will ++ log everything. Information is printed at KERN_DEBUG ++ so loglevel=8 may also need to be specified. ++ ++ module.sig_enforce ++ [KNL] When CONFIG_MODULE_SIG is set, this means that ++ modules without (valid) signatures will fail to load. ++ Note that if CONFIG_MODULE_SIG_FORCE is set, that ++ is always true, so this option does nothing. ++ ++ mousedev.tap_time= ++ [MOUSE] Maximum time between finger touching and ++ leaving touchpad surface for touch to be considered ++ a tap and be reported as a left button click (for ++ touchpads working in absolute mode only). ++ Format: ++ mousedev.xres= [MOUSE] Horizontal screen resolution, used for devices ++ reporting absolute coordinates, such as tablets ++ mousedev.yres= [MOUSE] Vertical screen resolution, used for devices ++ reporting absolute coordinates, such as tablets ++ ++ movablecore=nn[KMG] [KNL,X86,IA-64,PPC] This parameter ++ is similar to kernelcore except it specifies the ++ amount of memory used for migratable allocations. ++ If both kernelcore and movablecore is specified, ++ then kernelcore will be at *least* the specified ++ value but may be more. If movablecore on its own ++ is specified, the administrator must be careful ++ that the amount of memory usable for all allocations ++ is not too small. ++ ++ movable_node [KNL,X86] Boot-time switch to enable the effects ++ of CONFIG_MOVABLE_NODE=y. See mm/Kconfig for details. ++ ++ MTD_Partition= [MTD] ++ Format: ,,, ++ ++ MTD_Region= [MTD] Format: ++ ,[,,,,] ++ ++ mtdparts= [MTD] ++ See drivers/mtd/cmdlinepart.c. ++ ++ multitce=off [PPC] This parameter disables the use of the pSeries ++ firmware feature for updating multiple TCE entries ++ at a time. ++ ++ onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration ++ ++ Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock] ++ ++ boundary - index of last SLC block on Flex-OneNAND. ++ The remaining blocks are configured as MLC blocks. ++ lock - Configure if Flex-OneNAND boundary should be locked. ++ Once locked, the boundary cannot be changed. ++ 1 indicates lock status, 0 indicates unlock status. ++ ++ mtdset= [ARM] ++ ARM/S3C2412 JIVE boot control ++ ++ See arch/arm/mach-s3c2412/mach-jive.c ++ ++ mtouchusb.raw_coordinates= ++ [HW] Make the MicroTouch USB driver use raw coordinates ++ ('y', default) or cooked coordinates ('n') ++ ++ mtrr_chunk_size=nn[KMG] [X86] ++ used for mtrr cleanup. It is largest continuous chunk ++ that could hold holes aka. UC entries. ++ ++ mtrr_gran_size=nn[KMG] [X86] ++ Used for mtrr cleanup. It is granularity of mtrr block. ++ Default is 1. ++ Large value could prevent small alignment from ++ using up MTRRs. ++ ++ mtrr_spare_reg_nr=n [X86] ++ Format: ++ Range: 0,7 : spare reg number ++ Default : 1 ++ Used for mtrr cleanup. It is spare mtrr entries number. ++ Set to 2 or more if your graphical card needs more. ++ ++ n2= [NET] SDL Inc. RISCom/N2 synchronous serial card ++ ++ netdev= [NET] Network devices parameters ++ Format: ,,,, ++ Note that mem_start is often overloaded to mean ++ something different and driver-specific. ++ This usage is only documented in each driver source ++ file if at all. ++ ++ nf_conntrack.acct= ++ [NETFILTER] Enable connection tracking flow accounting ++ 0 to disable accounting ++ 1 to enable accounting ++ Default value is 0. ++ ++ nfsaddrs= [NFS] Deprecated. Use ip= instead. ++ See Documentation/filesystems/nfs/nfsroot.txt. ++ ++ nfsroot= [NFS] nfs root filesystem for disk-less boxes. ++ See Documentation/filesystems/nfs/nfsroot.txt. ++ ++ nfsrootdebug [NFS] enable nfsroot debugging messages. ++ See Documentation/filesystems/nfs/nfsroot.txt. ++ ++ nfs.callback_tcpport= ++ [NFS] set the TCP port on which the NFSv4 callback ++ channel should listen. ++ ++ nfs.cache_getent= ++ [NFS] sets the pathname to the program which is used ++ to update the NFS client cache entries. ++ ++ nfs.cache_getent_timeout= ++ [NFS] sets the timeout after which an attempt to ++ update a cache entry is deemed to have failed. ++ ++ nfs.idmap_cache_timeout= ++ [NFS] set the maximum lifetime for idmapper cache ++ entries. ++ ++ nfs.enable_ino64= ++ [NFS] enable 64-bit inode numbers. ++ If zero, the NFS client will fake up a 32-bit inode ++ number for the readdir() and stat() syscalls instead ++ of returning the full 64-bit number. ++ The default is to return 64-bit inode numbers. ++ ++ nfs.max_session_slots= ++ [NFSv4.1] Sets the maximum number of session slots ++ the client will attempt to negotiate with the server. ++ This limits the number of simultaneous RPC requests ++ that the client can send to the NFSv4.1 server. ++ Note that there is little point in setting this ++ value higher than the max_tcp_slot_table_limit. ++ ++ nfs.nfs4_disable_idmapping= ++ [NFSv4] When set to the default of '1', this option ++ ensures that both the RPC level authentication ++ scheme and the NFS level operations agree to use ++ numeric uids/gids if the mount is using the ++ 'sec=sys' security flavour. In effect it is ++ disabling idmapping, which can make migration from ++ legacy NFSv2/v3 systems to NFSv4 easier. ++ Servers that do not support this mode of operation ++ will be autodetected by the client, and it will fall ++ back to using the idmapper. ++ To turn off this behaviour, set the value to '0'. ++ nfs.nfs4_unique_id= ++ [NFS4] Specify an additional fixed unique ident- ++ ification string that NFSv4 clients can insert into ++ their nfs_client_id4 string. This is typically a ++ UUID that is generated at system install time. ++ ++ nfs.send_implementation_id = ++ [NFSv4.1] Send client implementation identification ++ information in exchange_id requests. ++ If zero, no implementation identification information ++ will be sent. ++ The default is to send the implementation identification ++ information. ++ ++ nfs.recover_lost_locks = ++ [NFSv4] Attempt to recover locks that were lost due ++ to a lease timeout on the server. Please note that ++ doing this risks data corruption, since there are ++ no guarantees that the file will remain unchanged ++ after the locks are lost. ++ If you want to enable the kernel legacy behaviour of ++ attempting to recover these locks, then set this ++ parameter to '1'. ++ The default parameter value of '0' causes the kernel ++ not to attempt recovery of lost locks. ++ ++ nfsd.nfs4_disable_idmapping= ++ [NFSv4] When set to the default of '1', the NFSv4 ++ server will return only numeric uids and gids to ++ clients using auth_sys, and will accept numeric uids ++ and gids from such clients. This is intended to ease ++ migration from NFSv2/v3. ++ ++ objlayoutdriver.osd_login_prog= ++ [NFS] [OBJLAYOUT] sets the pathname to the program which ++ is used to automatically discover and login into new ++ osd-targets. Please see: ++ Documentation/filesystems/pnfs.txt for more explanations ++ ++ nmi_debug= [KNL,AVR32,SH] Specify one or more actions to take ++ when a NMI is triggered. ++ Format: [state][,regs][,debounce][,die] ++ ++ nmi_watchdog= [KNL,BUGS=X86] Debugging features for SMP kernels ++ Format: [panic,][nopanic,][num] ++ Valid num: 0 ++ 0 - turn nmi_watchdog off ++ When panic is specified, panic when an NMI watchdog ++ timeout occurs (or 'nopanic' to override the opposite ++ default). ++ This is useful when you use a panic=... timeout and ++ need the box quickly up again. ++ ++ netpoll.carrier_timeout= ++ [NET] Specifies amount of time (in seconds) that ++ netpoll should wait for a carrier. By default netpoll ++ waits 4 seconds. ++ ++ no387 [BUGS=X86-32] Tells the kernel to use the 387 maths ++ emulation library even if a 387 maths coprocessor ++ is present. ++ ++ no_console_suspend ++ [HW] Never suspend the console ++ Disable suspending of consoles during suspend and ++ hibernate operations. Once disabled, debugging ++ messages can reach various consoles while the rest ++ of the system is being put to sleep (ie, while ++ debugging driver suspend/resume hooks). This may ++ not work reliably with all consoles, but is known ++ to work with serial and VGA consoles. ++ To facilitate more flexible debugging, we also add ++ console_suspend, a printk module parameter to control ++ it. Users could use console_suspend (usually ++ /sys/module/printk/parameters/console_suspend) to ++ turn on/off it dynamically. ++ ++ noaliencache [MM, NUMA, SLAB] Disables the allocation of alien ++ caches in the slab allocator. Saves per-node memory, ++ but will impact performance. ++ ++ noalign [KNL,ARM] ++ ++ noapic [SMP,APIC] Tells the kernel to not make use of any ++ IOAPICs that may be present in the system. ++ ++ nokaslr [X86] ++ Disable kernel base offset ASLR (Address Space ++ Layout Randomization) if built into the kernel. ++ ++ noautogroup Disable scheduler automatic task group creation. ++ ++ nobats [PPC] Do not use BATs for mapping kernel lowmem ++ on "Classic" PPC cores. ++ ++ nocache [ARM] ++ ++ noclflush [BUGS=X86] Don't use the CLFLUSH instruction ++ ++ nodelayacct [KNL] Disable per-task delay accounting ++ ++ nodisconnect [HW,SCSI,M68K] Disables SCSI disconnects. ++ ++ nodsp [SH] Disable hardware DSP at boot time. ++ ++ noefi [X86] Disable EFI runtime services support. ++ ++ noexec [IA-64] ++ ++ noexec [X86] ++ On X86-32 available only on PAE configured kernels. ++ noexec=on: enable non-executable mappings (default) ++ noexec=off: disable non-executable mappings ++ ++ nosmap [X86] ++ Disable SMAP (Supervisor Mode Access Prevention) ++ even if it is supported by processor. ++ ++ nosmep [X86] ++ Disable SMEP (Supervisor Mode Execution Prevention) ++ even if it is supported by processor. ++ ++ noexec32 [X86-64] ++ This affects only 32-bit executables. ++ noexec32=on: enable non-executable mappings (default) ++ read doesn't imply executable mappings ++ noexec32=off: disable non-executable mappings ++ read implies executable mappings ++ ++ nofpu [SH] Disable hardware FPU at boot time. ++ ++ nofxsr [BUGS=X86-32] Disables x86 floating point extended ++ register save and restore. The kernel will only save ++ legacy floating-point registers on task switch. ++ ++ noxsave [BUGS=X86] Disables x86 extended register state save ++ and restore using xsave. The kernel will fallback to ++ enabling legacy floating-point and sse state. ++ ++ eagerfpu= [X86] ++ on enable eager fpu restore ++ off disable eager fpu restore ++ auto selects the default scheme, which automatically ++ enables eagerfpu restore for xsaveopt. ++ ++ nohlt [BUGS=ARM,SH] Tells the kernel that the sleep(SH) or ++ wfi(ARM) instruction doesn't work correctly and not to ++ use it. This is also useful when using JTAG debugger. ++ ++ no_file_caps Tells the kernel not to honor file capabilities. The ++ only way then for a file to be executed with privilege ++ is to be setuid root or executed by root. ++ ++ nohalt [IA-64] Tells the kernel not to use the power saving ++ function PAL_HALT_LIGHT when idle. This increases ++ power-consumption. On the positive side, it reduces ++ interrupt wake-up latency, which may improve performance ++ in certain environments such as networked servers or ++ real-time systems. ++ ++ nohz= [KNL] Boottime enable/disable dynamic ticks ++ Valid arguments: on, off ++ Default: on ++ ++ nohz_full= [KNL,BOOT] ++ In kernels built with CONFIG_NO_HZ_FULL=y, set ++ the specified list of CPUs whose tick will be stopped ++ whenever possible. The boot CPU will be forced outside ++ the range to maintain the timekeeping. ++ The CPUs in this range must also be included in the ++ rcu_nocbs= set. ++ ++ noiotrap [SH] Disables trapped I/O port accesses. ++ ++ noirqdebug [X86-32] Disables the code which attempts to detect and ++ disable unhandled interrupt sources. ++ ++ no_timer_check [X86,APIC] Disables the code which tests for ++ broken timer IRQ sources. ++ ++ noisapnp [ISAPNP] Disables ISA PnP code. ++ ++ noinitrd [RAM] Tells the kernel not to load any configured ++ initial RAM disk. ++ ++ nointremap [X86-64, Intel-IOMMU] Do not enable interrupt ++ remapping. ++ [Deprecated - use intremap=off] ++ ++ nointroute [IA-64] ++ ++ nojitter [IA-64] Disables jitter checking for ITC timers. ++ ++ no-kvmclock [X86,KVM] Disable paravirtualized KVM clock driver ++ ++ no-kvmapf [X86,KVM] Disable paravirtualized asynchronous page ++ fault handling. ++ ++ no-steal-acc [X86,KVM] Disable paravirtualized steal time accounting. ++ steal time is computed, but won't influence scheduler ++ behaviour ++ ++ nolapic [X86-32,APIC] Do not enable or use the local APIC. ++ ++ nolapic_timer [X86-32,APIC] Do not use the local APIC timer. ++ ++ noltlbs [PPC] Do not use large page/tlb entries for kernel ++ lowmem mapping on PPC40x. ++ ++ nomca [IA-64] Disable machine check abort handling ++ ++ nomce [X86-32] Machine Check Exception ++ ++ nomfgpt [X86-32] Disable Multi-Function General Purpose ++ Timer usage (for AMD Geode machines). ++ ++ nonmi_ipi [X86] Disable using NMI IPIs during panic/reboot to ++ shutdown the other cpus. Instead use the REBOOT_VECTOR ++ irq. ++ ++ nomodule Disable module load ++ ++ nopat [X86] Disable PAT (page attribute table extension of ++ pagetables) support. ++ ++ norandmaps Don't use address space randomization. Equivalent to ++ echo 0 > /proc/sys/kernel/randomize_va_space ++ ++ noreplace-paravirt [X86,IA-64,PV_OPS] Don't patch paravirt_ops ++ ++ noreplace-smp [X86-32,SMP] Don't replace SMP instructions ++ with UP alternatives ++ ++ nordrand [X86] Disable the direct use of the RDRAND ++ instruction even if it is supported by the ++ processor. RDRAND is still available to user ++ space applications. ++ ++ noresume [SWSUSP] Disables resume and restores original swap ++ space. ++ ++ no-scroll [VGA] Disables scrollback. ++ This is required for the Braillex ib80-piezo Braille ++ reader made by F.H. Papenmeier (Germany). ++ ++ nosbagart [IA-64] ++ ++ nosep [BUGS=X86-32] Disables x86 SYSENTER/SYSEXIT support. ++ ++ nosmp [SMP] Tells an SMP kernel to act as a UP kernel, ++ and disable the IO APIC. legacy for "maxcpus=0". ++ ++ nosoftlockup [KNL] Disable the soft-lockup detector. ++ ++ nosync [HW,M68K] Disables sync negotiation for all devices. ++ ++ notsc [BUGS=X86-32] Disable Time Stamp Counter ++ ++ nousb [USB] Disable the USB subsystem ++ ++ nowatchdog [KNL] Disable the lockup detector (NMI watchdog). ++ ++ nowb [ARM] ++ ++ nox2apic [X86-64,APIC] Do not enable x2APIC mode. ++ ++ cpu0_hotplug [X86] Turn on CPU0 hotplug feature when ++ CONFIG_BOOTPARAM_HOTPLUG_CPU0 is off. ++ Some features depend on CPU0. Known dependencies are: ++ 1. Resume from suspend/hibernate depends on CPU0. ++ Suspend/hibernate will fail if CPU0 is offline and you ++ need to online CPU0 before suspend/hibernate. ++ 2. PIC interrupts also depend on CPU0. CPU0 can't be ++ removed if a PIC interrupt is detected. ++ It's said poweroff/reboot may depend on CPU0 on some ++ machines although I haven't seen such issues so far ++ after CPU0 is offline on a few tested machines. ++ If the dependencies are under your control, you can ++ turn on cpu0_hotplug. ++ ++ nptcg= [IA-64] Override max number of concurrent global TLB ++ purges which is reported from either PAL_VM_SUMMARY or ++ SAL PALO. ++ ++ nr_cpus= [SMP] Maximum number of processors that an SMP kernel ++ could support. nr_cpus=n : n >= 1 limits the kernel to ++ supporting 'n' processors. Later in runtime you can not ++ use hotplug cpu feature to put more cpu back to online. ++ just like you compile the kernel NR_CPUS=n ++ ++ nr_uarts= [SERIAL] maximum number of UARTs to be registered. ++ ++ numa_balancing= [KNL,X86] Enable or disable automatic NUMA balancing. ++ Allowed values are enable and disable ++ ++ numa_zonelist_order= [KNL, BOOT] Select zonelist order for NUMA. ++ one of ['zone', 'node', 'default'] can be specified ++ This can be set from sysctl after boot. ++ See Documentation/sysctl/vm.txt for details. ++ ++ ohci1394_dma=early [HW] enable debugging via the ohci1394 driver. ++ See Documentation/debugging-via-ohci1394.txt for more ++ info. ++ ++ olpc_ec_timeout= [OLPC] ms delay when issuing EC commands ++ Rather than timing out after 20 ms if an EC ++ command is not properly ACKed, override the length ++ of the timeout. We have interrupts disabled while ++ waiting for the ACK, so if this is set too high ++ interrupts *may* be lost! ++ ++ omap_mux= [OMAP] Override bootloader pin multiplexing. ++ Format: ... ++ For example, to override I2C bus2: ++ omap_mux=i2c2_scl.i2c2_scl=0x100,i2c2_sda.i2c2_sda=0x100 ++ ++ oprofile.timer= [HW] ++ Use timer interrupt instead of performance counters ++ ++ oprofile.cpu_type= Force an oprofile cpu type ++ This might be useful if you have an older oprofile ++ userland or if you want common events. ++ Format: { arch_perfmon } ++ arch_perfmon: [X86] Force use of architectural ++ perfmon on Intel CPUs instead of the ++ CPU specific event set. ++ timer: [X86] Force use of architectural NMI ++ timer mode (see also oprofile.timer ++ for generic hr timer mode) ++ [s390] Force legacy basic mode sampling ++ (report cpu_type "timer") ++ ++ oops=panic Always panic on oopses. Default is to just kill the ++ process, but there is a small probability of ++ deadlocking the machine. ++ This will also cause panics on machine check exceptions. ++ Useful together with panic=30 to trigger a reboot. ++ ++ OSS [HW,OSS] ++ See Documentation/sound/oss/oss-parameters.txt ++ ++ panic= [KNL] Kernel behaviour on panic: delay ++ timeout > 0: seconds before rebooting ++ timeout = 0: wait forever ++ timeout < 0: reboot immediately ++ Format: ++ ++ parkbd.port= [HW] Parallel port number the keyboard adapter is ++ connected to, default is 0. ++ Format: ++ parkbd.mode= [HW] Parallel port keyboard adapter mode of operation, ++ 0 for XT, 1 for AT (default is AT). ++ Format: ++ ++ parport= [HW,PPT] Specify parallel ports. 0 disables. ++ Format: { 0 | auto | 0xBBB[,IRQ[,DMA]] } ++ Use 'auto' to force the driver to use any ++ IRQ/DMA settings detected (the default is to ++ ignore detected IRQ/DMA settings because of ++ possible conflicts). You can specify the base ++ address, IRQ, and DMA settings; IRQ and DMA ++ should be numbers, or 'auto' (for using detected ++ settings on that particular port), or 'nofifo' ++ (to avoid using a FIFO even if it is detected). ++ Parallel ports are assigned in the order they ++ are specified on the command line, starting ++ with parport0. ++ ++ parport_init_mode= [HW,PPT] ++ Configure VIA parallel port to operate in ++ a specific mode. This is necessary on Pegasos ++ computer where firmware has no options for setting ++ up parallel port mode and sets it to spp. ++ Currently this function knows 686a and 8231 chips. ++ Format: [spp|ps2|epp|ecp|ecpepp] ++ ++ pause_on_oops= ++ Halt all CPUs after the first oops has been printed for ++ the specified number of seconds. This is to be used if ++ your oopses keep scrolling off the screen. ++ ++ pcbit= [HW,ISDN] ++ ++ pcd. [PARIDE] ++ See header of drivers/block/paride/pcd.c. ++ See also Documentation/blockdev/paride.txt. ++ ++ pci=option[,option...] [PCI] various PCI subsystem options: ++ earlydump [X86] dump PCI config space before the kernel ++ changes anything ++ off [X86] don't probe for the PCI bus ++ bios [X86-32] force use of PCI BIOS, don't access ++ the hardware directly. Use this if your machine ++ has a non-standard PCI host bridge. ++ nobios [X86-32] disallow use of PCI BIOS, only direct ++ hardware access methods are allowed. Use this ++ if you experience crashes upon bootup and you ++ suspect they are caused by the BIOS. ++ conf1 [X86] Force use of PCI Configuration ++ Mechanism 1. ++ conf2 [X86] Force use of PCI Configuration ++ Mechanism 2. ++ noaer [PCIE] If the PCIEAER kernel config parameter is ++ enabled, this kernel boot option can be used to ++ disable the use of PCIE advanced error reporting. ++ nodomains [PCI] Disable support for multiple PCI ++ root domains (aka PCI segments, in ACPI-speak). ++ nommconf [X86] Disable use of MMCONFIG for PCI ++ Configuration ++ check_enable_amd_mmconf [X86] check for and enable ++ properly configured MMIO access to PCI ++ config space on AMD family 10h CPU ++ nomsi [MSI] If the PCI_MSI kernel config parameter is ++ enabled, this kernel boot option can be used to ++ disable the use of MSI interrupts system-wide. ++ noioapicquirk [APIC] Disable all boot interrupt quirks. ++ Safety option to keep boot IRQs enabled. This ++ should never be necessary. ++ ioapicreroute [APIC] Enable rerouting of boot IRQs to the ++ primary IO-APIC for bridges that cannot disable ++ boot IRQs. This fixes a source of spurious IRQs ++ when the system masks IRQs. ++ noioapicreroute [APIC] Disable workaround that uses the ++ boot IRQ equivalent of an IRQ that connects to ++ a chipset where boot IRQs cannot be disabled. ++ The opposite of ioapicreroute. ++ biosirq [X86-32] Use PCI BIOS calls to get the interrupt ++ routing table. These calls are known to be buggy ++ on several machines and they hang the machine ++ when used, but on other computers it's the only ++ way to get the interrupt routing table. Try ++ this option if the kernel is unable to allocate ++ IRQs or discover secondary PCI buses on your ++ motherboard. ++ rom [X86] Assign address space to expansion ROMs. ++ Use with caution as certain devices share ++ address decoders between ROMs and other ++ resources. ++ norom [X86] Do not assign address space to ++ expansion ROMs that do not already have ++ BIOS assigned address ranges. ++ nobar [X86] Do not assign address space to the ++ BARs that weren't assigned by the BIOS. ++ irqmask=0xMMMM [X86] Set a bit mask of IRQs allowed to be ++ assigned automatically to PCI devices. You can ++ make the kernel exclude IRQs of your ISA cards ++ this way. ++ pirqaddr=0xAAAAA [X86] Specify the physical address ++ of the PIRQ table (normally generated ++ by the BIOS) if it is outside the ++ F0000h-100000h range. ++ lastbus=N [X86] Scan all buses thru bus #N. Can be ++ useful if the kernel is unable to find your ++ secondary buses and you want to tell it ++ explicitly which ones they are. ++ assign-busses [X86] Always assign all PCI bus ++ numbers ourselves, overriding ++ whatever the firmware may have done. ++ usepirqmask [X86] Honor the possible IRQ mask stored ++ in the BIOS $PIR table. This is needed on ++ some systems with broken BIOSes, notably ++ some HP Pavilion N5400 and Omnibook XE3 ++ notebooks. This will have no effect if ACPI ++ IRQ routing is enabled. ++ noacpi [X86] Do not use ACPI for IRQ routing ++ or for PCI scanning. ++ use_crs [X86] Use PCI host bridge window information ++ from ACPI. On BIOSes from 2008 or later, this ++ is enabled by default. If you need to use this, ++ please report a bug. ++ nocrs [X86] Ignore PCI host bridge windows from ACPI. ++ If you need to use this, please report a bug. ++ routeirq Do IRQ routing for all PCI devices. ++ This is normally done in pci_enable_device(), ++ so this option is a temporary workaround ++ for broken drivers that don't call it. ++ skip_isa_align [X86] do not align io start addr, so can ++ handle more pci cards ++ firmware [ARM] Do not re-enumerate the bus but instead ++ just use the configuration from the ++ bootloader. This is currently used on ++ IXP2000 systems where the bus has to be ++ configured a certain way for adjunct CPUs. ++ noearly [X86] Don't do any early type 1 scanning. ++ This might help on some broken boards which ++ machine check when some devices' config space ++ is read. But various workarounds are disabled ++ and some IOMMU drivers will not work. ++ bfsort Sort PCI devices into breadth-first order. ++ This sorting is done to get a device ++ order compatible with older (<= 2.4) kernels. ++ nobfsort Don't sort PCI devices into breadth-first order. ++ pcie_bus_tune_off Disable PCIe MPS (Max Payload Size) ++ tuning and use the BIOS-configured MPS defaults. ++ pcie_bus_safe Set every device's MPS to the largest value ++ supported by all devices below the root complex. ++ pcie_bus_perf Set device MPS to the largest allowable MPS ++ based on its parent bus. Also set MRRS (Max ++ Read Request Size) to the largest supported ++ value (no larger than the MPS that the device ++ or bus can support) for best performance. ++ pcie_bus_peer2peer Set every device's MPS to 128B, which ++ every device is guaranteed to support. This ++ configuration allows peer-to-peer DMA between ++ any pair of devices, possibly at the cost of ++ reduced performance. This also guarantees ++ that hot-added devices will work. ++ cbiosize=nn[KMG] The fixed amount of bus space which is ++ reserved for the CardBus bridge's IO window. ++ The default value is 256 bytes. ++ cbmemsize=nn[KMG] The fixed amount of bus space which is ++ reserved for the CardBus bridge's memory ++ window. The default value is 64 megabytes. ++ resource_alignment= ++ Format: ++ [@][:]:.[; ...] ++ Specifies alignment and device to reassign ++ aligned memory resources. ++ If is not specified, ++ PAGE_SIZE is used as alignment. ++ PCI-PCI bridge can be specified, if resource ++ windows need to be expanded. ++ ecrc= Enable/disable PCIe ECRC (transaction layer ++ end-to-end CRC checking). ++ bios: Use BIOS/firmware settings. This is the ++ the default. ++ off: Turn ECRC off ++ on: Turn ECRC on. ++ hpiosize=nn[KMG] The fixed amount of bus space which is ++ reserved for hotplug bridge's IO window. ++ Default size is 256 bytes. ++ hpmemsize=nn[KMG] The fixed amount of bus space which is ++ reserved for hotplug bridge's memory window. ++ Default size is 2 megabytes. ++ realloc= Enable/disable reallocating PCI bridge resources ++ if allocations done by BIOS are too small to ++ accommodate resources required by all child ++ devices. ++ off: Turn realloc off ++ on: Turn realloc on ++ realloc same as realloc=on ++ noari do not use PCIe ARI. ++ pcie_scan_all Scan all possible PCIe devices. Otherwise we ++ only look for one device below a PCIe downstream ++ port. ++ ++ pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power ++ Management. ++ off Disable ASPM. ++ force Enable ASPM even on devices that claim not to support it. ++ WARNING: Forcing ASPM on may cause system lockups. ++ ++ pcie_hp= [PCIE] PCI Express Hotplug driver options: ++ nomsi Do not use MSI for PCI Express Native Hotplug (this ++ makes all PCIe ports use INTx for hotplug services). ++ ++ pcie_ports= [PCIE] PCIe ports handling: ++ auto Ask the BIOS whether or not to use native PCIe services ++ associated with PCIe ports (PME, hot-plug, AER). Use ++ them only if that is allowed by the BIOS. ++ native Use native PCIe services associated with PCIe ports ++ unconditionally. ++ compat Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe ++ ports driver. ++ ++ pcie_pme= [PCIE,PM] Native PCIe PME signaling options: ++ nomsi Do not use MSI for native PCIe PME signaling (this makes ++ all PCIe root ports use INTx for all services). ++ ++ pcmv= [HW,PCMCIA] BadgePAD 4 ++ ++ pd. [PARIDE] ++ See Documentation/blockdev/paride.txt. ++ ++ pdcchassis= [PARISC,HW] Disable/Enable PDC Chassis Status codes at ++ boot time. ++ Format: { 0 | 1 } ++ See arch/parisc/kernel/pdc_chassis.c ++ ++ percpu_alloc= Select which percpu first chunk allocator to use. ++ Currently supported values are "embed" and "page". ++ Archs may support subset or none of the selections. ++ See comments in mm/percpu.c for details on each ++ allocator. This parameter is primarily for debugging ++ and performance comparison. ++ ++ pf. [PARIDE] ++ See Documentation/blockdev/paride.txt. ++ ++ pg. [PARIDE] ++ See Documentation/blockdev/paride.txt. ++ ++ pirq= [SMP,APIC] Manual mp-table setup ++ See Documentation/x86/i386/IO-APIC.txt. ++ ++ plip= [PPT,NET] Parallel port network link ++ Format: { parport | timid | 0 } ++ See also Documentation/parport.txt. ++ ++ pmtmr= [X86] Manual setup of pmtmr I/O Port. ++ Override pmtimer IOPort with a hex value. ++ e.g. pmtmr=0x508 ++ ++ pnp.debug=1 [PNP] ++ Enable PNP debug messages (depends on the ++ CONFIG_PNP_DEBUG_MESSAGES option). Change at run-time ++ via /sys/module/pnp/parameters/debug. We always show ++ current resource usage; turning this on also shows ++ possible settings and some assignment information. ++ ++ pnpacpi= [ACPI] ++ { off } ++ ++ pnpbios= [ISAPNP] ++ { on | off | curr | res | no-curr | no-res } ++ ++ pnp_reserve_irq= ++ [ISAPNP] Exclude IRQs for the autoconfiguration ++ ++ pnp_reserve_dma= ++ [ISAPNP] Exclude DMAs for the autoconfiguration ++ ++ pnp_reserve_io= [ISAPNP] Exclude I/O ports for the autoconfiguration ++ Ranges are in pairs (I/O port base and size). ++ ++ pnp_reserve_mem= ++ [ISAPNP] Exclude memory regions for the ++ autoconfiguration. ++ Ranges are in pairs (memory base and size). ++ ++ ports= [IP_VS_FTP] IPVS ftp helper module ++ Default is 21. ++ Up to 8 (IP_VS_APP_MAX_PORTS) ports ++ may be specified. ++ Format: ,.... ++ ++ print-fatal-signals= ++ [KNL] debug: print fatal signals ++ ++ If enabled, warn about various signal handling ++ related application anomalies: too many signals, ++ too many POSIX.1 timers, fatal signals causing a ++ coredump - etc. ++ ++ If you hit the warning due to signal overflow, ++ you might want to try "ulimit -i unlimited". ++ ++ default: off. ++ ++ printk.always_kmsg_dump= ++ Trigger kmsg_dump for cases other than kernel oops or ++ panics ++ Format: (1/Y/y=enable, 0/N/n=disable) ++ default: disabled ++ ++ printk.time= Show timing data prefixed to each printk message line ++ Format: (1/Y/y=enable, 0/N/n=disable) ++ ++ processor.max_cstate= [HW,ACPI] ++ Limit processor to maximum C-state ++ max_cstate=9 overrides any DMI blacklist limit. ++ ++ processor.nocst [HW,ACPI] ++ Ignore the _CST method to determine C-states, ++ instead using the legacy FADT method ++ ++ profile= [KNL] Enable kernel profiling via /proc/profile ++ Format: [schedule,] ++ Param: "schedule" - profile schedule points. ++ Param: - step/bucket size as a power of 2 for ++ statistical time based profiling. ++ Param: "sleep" - profile D-state sleeping (millisecs). ++ Requires CONFIG_SCHEDSTATS ++ Param: "kvm" - profile VM exits. ++ ++ prompt_ramdisk= [RAM] List of RAM disks to prompt for floppy disk ++ before loading. ++ See Documentation/blockdev/ramdisk.txt. ++ ++ psmouse.proto= [HW,MOUSE] Highest PS2 mouse protocol extension to ++ probe for; one of (bare|imps|exps|lifebook|any). ++ psmouse.rate= [HW,MOUSE] Set desired mouse report rate, in reports ++ per second. ++ psmouse.resetafter= [HW,MOUSE] ++ Try to reset the device after so many bad packets ++ (0 = never). ++ psmouse.resolution= ++ [HW,MOUSE] Set desired mouse resolution, in dpi. ++ psmouse.smartscroll= ++ [HW,MOUSE] Controls Logitech smartscroll autorepeat. ++ 0 = disabled, 1 = enabled (default). ++ ++ pstore.backend= Specify the name of the pstore backend to use ++ ++ pt. [PARIDE] ++ See Documentation/blockdev/paride.txt. ++ ++ pty.legacy_count= ++ [KNL] Number of legacy pty's. Overwrites compiled-in ++ default number. ++ ++ quiet [KNL] Disable most log messages ++ ++ r128= [HW,DRM] ++ ++ raid= [HW,RAID] ++ See Documentation/md.txt. ++ ++ ramdisk_blocksize= [RAM] ++ See Documentation/blockdev/ramdisk.txt. ++ ++ ramdisk_size= [RAM] Sizes of RAM disks in kilobytes ++ See Documentation/blockdev/ramdisk.txt. ++ ++ rcu_nocbs= [KNL] ++ In kernels built with CONFIG_RCU_NOCB_CPU=y, set ++ the specified list of CPUs to be no-callback CPUs. ++ Invocation of these CPUs' RCU callbacks will ++ be offloaded to "rcuox/N" kthreads created for ++ that purpose, where "x" is "b" for RCU-bh, "p" ++ for RCU-preempt, and "s" for RCU-sched, and "N" ++ is the CPU number. This reduces OS jitter on the ++ offloaded CPUs, which can be useful for HPC and ++ real-time workloads. It can also improve energy ++ efficiency for asymmetric multiprocessors. ++ ++ rcu_nocb_poll [KNL] ++ Rather than requiring that offloaded CPUs ++ (specified by rcu_nocbs= above) explicitly ++ awaken the corresponding "rcuoN" kthreads, ++ make these kthreads poll for callbacks. ++ This improves the real-time response for the ++ offloaded CPUs by relieving them of the need to ++ wake up the corresponding kthread, but degrades ++ energy efficiency by requiring that the kthreads ++ periodically wake up to do the polling. ++ ++ rcutree.blimit= [KNL] ++ Set maximum number of finished RCU callbacks to ++ process in one batch. ++ ++ rcutree.rcu_fanout_leaf= [KNL] ++ Increase the number of CPUs assigned to each ++ leaf rcu_node structure. Useful for very large ++ systems. ++ ++ rcutree.jiffies_till_first_fqs= [KNL] ++ Set delay from grace-period initialization to ++ first attempt to force quiescent states. ++ Units are jiffies, minimum value is zero, ++ and maximum value is HZ. ++ ++ rcutree.jiffies_till_next_fqs= [KNL] ++ Set delay between subsequent attempts to force ++ quiescent states. Units are jiffies, minimum ++ value is one, and maximum value is HZ. ++ ++ rcutree.qhimark= [KNL] ++ Set threshold of queued RCU callbacks beyond which ++ batch limiting is disabled. ++ ++ rcutree.qlowmark= [KNL] ++ Set threshold of queued RCU callbacks below which ++ batch limiting is re-enabled. ++ ++ rcutree.rcu_idle_gp_delay= [KNL] ++ Set wakeup interval for idle CPUs that have ++ RCU callbacks (RCU_FAST_NO_HZ=y). ++ ++ rcutree.rcu_idle_lazy_gp_delay= [KNL] ++ Set wakeup interval for idle CPUs that have ++ only "lazy" RCU callbacks (RCU_FAST_NO_HZ=y). ++ Lazy RCU callbacks are those which RCU can ++ prove do nothing more than free memory. ++ ++ rcutorture.fqs_duration= [KNL] ++ Set duration of force_quiescent_state bursts. ++ ++ rcutorture.fqs_holdoff= [KNL] ++ Set holdoff time within force_quiescent_state bursts. ++ ++ rcutorture.fqs_stutter= [KNL] ++ Set wait time between force_quiescent_state bursts. ++ ++ rcutorture.gp_exp= [KNL] ++ Use expedited update-side primitives. ++ ++ rcutorture.gp_normal= [KNL] ++ Use normal (non-expedited) update-side primitives. ++ If both gp_exp and gp_normal are set, do both. ++ If neither gp_exp nor gp_normal are set, still ++ do both. ++ ++ rcutorture.n_barrier_cbs= [KNL] ++ Set callbacks/threads for rcu_barrier() testing. ++ ++ rcutorture.nfakewriters= [KNL] ++ Set number of concurrent RCU writers. These just ++ stress RCU, they don't participate in the actual ++ test, hence the "fake". ++ ++ rcutorture.nreaders= [KNL] ++ Set number of RCU readers. ++ ++ rcutorture.object_debug= [KNL] ++ Enable debug-object double-call_rcu() testing. ++ ++ rcutorture.onoff_holdoff= [KNL] ++ Set time (s) after boot for CPU-hotplug testing. ++ ++ rcutorture.onoff_interval= [KNL] ++ Set time (s) between CPU-hotplug operations, or ++ zero to disable CPU-hotplug testing. ++ ++ rcutorture.rcutorture_runnable= [BOOT] ++ Start rcutorture running at boot time. ++ ++ rcutorture.shuffle_interval= [KNL] ++ Set task-shuffle interval (s). Shuffling tasks ++ allows some CPUs to go into dyntick-idle mode ++ during the rcutorture test. ++ ++ rcutorture.shutdown_secs= [KNL] ++ Set time (s) after boot system shutdown. This ++ is useful for hands-off automated testing. ++ ++ rcutorture.stall_cpu= [KNL] ++ Duration of CPU stall (s) to test RCU CPU stall ++ warnings, zero to disable. ++ ++ rcutorture.stall_cpu_holdoff= [KNL] ++ Time to wait (s) after boot before inducing stall. ++ ++ rcutorture.stat_interval= [KNL] ++ Time (s) between statistics printk()s. ++ ++ rcutorture.stutter= [KNL] ++ Time (s) to stutter testing, for example, specifying ++ five seconds causes the test to run for five seconds, ++ wait for five seconds, and so on. This tests RCU's ++ ability to transition abruptly to and from idle. ++ ++ rcutorture.test_boost= [KNL] ++ Test RCU priority boosting? 0=no, 1=maybe, 2=yes. ++ "Maybe" means test if the RCU implementation ++ under test support RCU priority boosting. ++ ++ rcutorture.test_boost_duration= [KNL] ++ Duration (s) of each individual boost test. ++ ++ rcutorture.test_boost_interval= [KNL] ++ Interval (s) between each boost test. ++ ++ rcutorture.test_no_idle_hz= [KNL] ++ Test RCU's dyntick-idle handling. See also the ++ rcutorture.shuffle_interval parameter. ++ ++ rcutorture.torture_type= [KNL] ++ Specify the RCU implementation to test. ++ ++ rcutorture.verbose= [KNL] ++ Enable additional printk() statements. ++ ++ rcupdate.rcu_expedited= [KNL] ++ Use expedited grace-period primitives, for ++ example, synchronize_rcu_expedited() instead ++ of synchronize_rcu(). This reduces latency, ++ but can increase CPU utilization, degrade ++ real-time latency, and degrade energy efficiency. ++ ++ rcupdate.rcu_cpu_stall_suppress= [KNL] ++ Suppress RCU CPU stall warning messages. ++ ++ rcupdate.rcu_cpu_stall_timeout= [KNL] ++ Set timeout for RCU CPU stall warning messages. ++ ++ rdinit= [KNL] ++ Format: ++ Run specified binary instead of /init from the ramdisk, ++ used for early userspace startup. See initrd. ++ ++ reboot= [KNL] ++ Format (x86 or x86_64): ++ [w[arm] | c[old] | h[ard] | s[oft] | g[pio]] \ ++ [[,]s[mp]#### \ ++ [[,]b[ios] | a[cpi] | k[bd] | t[riple] | e[fi] | p[ci]] \ ++ [[,]f[orce] ++ Where reboot_mode is one of warm (soft) or cold (hard) or gpio, ++ reboot_type is one of bios, acpi, kbd, triple, efi, or pci, ++ reboot_force is either force or not specified, ++ reboot_cpu is s[mp]#### with #### being the processor ++ to be used for rebooting. ++ ++ relax_domain_level= ++ [KNL, SMP] Set scheduler's default relax_domain_level. ++ See Documentation/cgroups/cpusets.txt. ++ ++ reserve= [KNL,BUGS] Force the kernel to ignore some iomem area ++ ++ reservetop= [X86-32] ++ Format: nn[KMG] ++ Reserves a hole at the top of the kernel virtual ++ address space. ++ ++ reservelow= [X86] ++ Format: nn[K] ++ Set the amount of memory to reserve for BIOS at ++ the bottom of the address space. ++ ++ reset_devices [KNL] Force drivers to reset the underlying device ++ during initialization. ++ ++ resume= [SWSUSP] ++ Specify the partition device for software suspend ++ Format: ++ {/dev/ | PARTUUID= | : | } ++ ++ resume_offset= [SWSUSP] ++ Specify the offset from the beginning of the partition ++ given by "resume=" at which the swap header is located, ++ in units (needed only for swap files). ++ See Documentation/power/swsusp-and-swap-files.txt ++ ++ resumedelay= [HIBERNATION] Delay (in seconds) to pause before attempting to ++ read the resume files ++ ++ resumewait [HIBERNATION] Wait (indefinitely) for resume device to show up. ++ Useful for devices that are detected asynchronously ++ (e.g. USB and MMC devices). ++ ++ hibernate= [HIBERNATION] ++ noresume Don't check if there's a hibernation image ++ present during boot. ++ nocompress Don't compress/decompress hibernation images. ++ ++ retain_initrd [RAM] Keep initrd memory after extraction ++ ++ rhash_entries= [KNL,NET] ++ Set number of hash buckets for route cache ++ ++ riscom8= [HW,SERIAL] ++ Format: [,[,...]] ++ ++ ro [KNL] Mount root device read-only on boot ++ ++ root= [KNL] Root filesystem ++ See name_to_dev_t comment in init/do_mounts.c. ++ ++ rootdelay= [KNL] Delay (in seconds) to pause before attempting to ++ mount the root filesystem ++ ++ rootflags= [KNL] Set root filesystem mount option string ++ ++ rootfstype= [KNL] Set root filesystem type ++ ++ rootwait [KNL] Wait (indefinitely) for root device to show up. ++ Useful for devices that are detected asynchronously ++ (e.g. USB and MMC devices). ++ ++ rproc_mem=nn[KMG][@address] ++ [KNL,ARM,CMA] Remoteproc physical memory block. ++ Memory area to be used by remote processor image, ++ managed by CMA. ++ ++ rw [KNL] Mount root device read-write on boot ++ ++ S [KNL] Run init in single mode ++ ++ sa1100ir [NET] ++ See drivers/net/irda/sa1100_ir.c. ++ ++ sbni= [NET] Granch SBNI12 leased line adapter ++ ++ sched_debug [KNL] Enables verbose scheduler debug messages. ++ ++ skew_tick= [KNL] Offset the periodic timer tick per cpu to mitigate ++ xtime_lock contention on larger systems, and/or RCU lock ++ contention on all systems with CONFIG_MAXSMP set. ++ Format: { "0" | "1" } ++ 0 -- disable. (may be 1 via CONFIG_CMDLINE="skew_tick=1" ++ 1 -- enable. ++ Note: increases power consumption, thus should only be ++ enabled if running jitter sensitive (HPC/RT) workloads. ++ ++ security= [SECURITY] Choose a security module to enable at boot. ++ If this boot parameter is not specified, only the first ++ security module asking for security registration will be ++ loaded. An invalid security module name will be treated ++ as if no module has been chosen. ++ ++ selinux= [SELINUX] Disable or enable SELinux at boot time. ++ Format: { "0" | "1" } ++ See security/selinux/Kconfig help text. ++ 0 -- disable. ++ 1 -- enable. ++ Default value is set via kernel config option. ++ If enabled at boot time, /selinux/disable can be used ++ later to disable prior to initial policy load. ++ ++ apparmor= [APPARMOR] Disable or enable AppArmor at boot time ++ Format: { "0" | "1" } ++ See security/apparmor/Kconfig help text ++ 0 -- disable. ++ 1 -- enable. ++ Default value is set via kernel config option. ++ ++ serialnumber [BUGS=X86-32] ++ ++ shapers= [NET] ++ Maximal number of shapers. ++ ++ show_msr= [x86] show boot-time MSR settings ++ Format: { } ++ Show boot-time (BIOS-initialized) MSR settings. ++ The parameter means the number of CPUs to show, ++ for example 1 means boot CPU only. ++ ++ simeth= [IA-64] ++ simscsi= ++ ++ slram= [HW,MTD] ++ ++ slab_max_order= [MM, SLAB] ++ Determines the maximum allowed order for slabs. ++ A high setting may cause OOMs due to memory ++ fragmentation. Defaults to 1 for systems with ++ more than 32MB of RAM, 0 otherwise. ++ ++ slub_debug[=options[,slabs]] [MM, SLUB] ++ Enabling slub_debug allows one to determine the ++ culprit if slab objects become corrupted. Enabling ++ slub_debug can create guard zones around objects and ++ may poison objects when not in use. Also tracks the ++ last alloc / free. For more information see ++ Documentation/vm/slub.txt. ++ ++ slub_max_order= [MM, SLUB] ++ Determines the maximum allowed order for slabs. ++ A high setting may cause OOMs due to memory ++ fragmentation. For more information see ++ Documentation/vm/slub.txt. ++ ++ slub_min_objects= [MM, SLUB] ++ The minimum number of objects per slab. SLUB will ++ increase the slab order up to slub_max_order to ++ generate a sufficiently large slab able to contain ++ the number of objects indicated. The higher the number ++ of objects the smaller the overhead of tracking slabs ++ and the less frequently locks need to be acquired. ++ For more information see Documentation/vm/slub.txt. ++ ++ slub_min_order= [MM, SLUB] ++ Determines the minimum page order for slabs. Must be ++ lower than slub_max_order. ++ For more information see Documentation/vm/slub.txt. ++ ++ slub_nomerge [MM, SLUB] ++ Disable merging of slabs with similar size. May be ++ necessary if there is some reason to distinguish ++ allocs to different slabs. Debug options disable ++ merging on their own. ++ For more information see Documentation/vm/slub.txt. ++ ++ smart2= [HW] ++ Format: [,[,...,]] ++ ++ smsc-ircc2.nopnp [HW] Don't use PNP to discover SMC devices ++ smsc-ircc2.ircc_cfg= [HW] Device configuration I/O port ++ smsc-ircc2.ircc_sir= [HW] SIR base I/O port ++ smsc-ircc2.ircc_fir= [HW] FIR base I/O port ++ smsc-ircc2.ircc_irq= [HW] IRQ line ++ smsc-ircc2.ircc_dma= [HW] DMA channel ++ smsc-ircc2.ircc_transceiver= [HW] Transceiver type: ++ 0: Toshiba Satellite 1800 (GP data pin select) ++ 1: Fast pin select (default) ++ 2: ATC IRMode ++ ++ softlockup_panic= ++ [KNL] Should the soft-lockup detector generate panics. ++ Format: ++ ++ sonypi.*= [HW] Sony Programmable I/O Control Device driver ++ See Documentation/laptops/sonypi.txt ++ ++ specialix= [HW,SERIAL] Specialix multi-serial port adapter ++ See Documentation/serial/specialix.txt. ++ ++ spia_io_base= [HW,MTD] ++ spia_fio_base= ++ spia_pedr= ++ spia_peddr= ++ ++ stacktrace [FTRACE] ++ Enabled the stack tracer on boot up. ++ ++ stacktrace_filter=[function-list] ++ [FTRACE] Limit the functions that the stack tracer ++ will trace at boot up. function-list is a comma separated ++ list of functions. This list can be changed at run ++ time by the stack_trace_filter file in the debugfs ++ tracing directory. Note, this enables stack tracing ++ and the stacktrace above is not needed. ++ ++ sti= [PARISC,HW] ++ Format: ++ Set the STI (builtin display/keyboard on the HP-PARISC ++ machines) console (graphic card) which should be used ++ as the initial boot-console. ++ See also comment in drivers/video/console/sticore.c. ++ ++ sti_font= [HW] ++ See comment in drivers/video/console/sticore.c. ++ ++ stifb= [HW] ++ Format: bpp:[:[:...]] ++ ++ sunrpc.min_resvport= ++ sunrpc.max_resvport= ++ [NFS,SUNRPC] ++ SunRPC servers often require that client requests ++ originate from a privileged port (i.e. a port in the ++ range 0 < portnr < 1024). ++ An administrator who wishes to reserve some of these ++ ports for other uses may adjust the range that the ++ kernel's sunrpc client considers to be privileged ++ using these two parameters to set the minimum and ++ maximum port values. ++ ++ sunrpc.pool_mode= ++ [NFS] ++ Control how the NFS server code allocates CPUs to ++ service thread pools. Depending on how many NICs ++ you have and where their interrupts are bound, this ++ option will affect which CPUs will do NFS serving. ++ Note: this parameter cannot be changed while the ++ NFS server is running. ++ ++ auto the server chooses an appropriate mode ++ automatically using heuristics ++ global a single global pool contains all CPUs ++ percpu one pool for each CPU ++ pernode one pool for each NUMA node (equivalent ++ to global on non-NUMA machines) ++ ++ sunrpc.tcp_slot_table_entries= ++ sunrpc.udp_slot_table_entries= ++ [NFS,SUNRPC] ++ Sets the upper limit on the number of simultaneous ++ RPC calls that can be sent from the client to a ++ server. Increasing these values may allow you to ++ improve throughput, but will also increase the ++ amount of memory reserved for use by the client. ++ ++ swapaccount=[0|1] ++ [KNL] Enable accounting of swap in memory resource ++ controller if no parameter or 1 is given or disable ++ it if 0 is given (See Documentation/cgroups/memory.txt) ++ ++ swiotlb= [ARM,IA-64,PPC,MIPS,X86] ++ Format: { | force } ++ -- Number of I/O TLB slabs ++ force -- force using of bounce buffers even if they ++ wouldn't be automatically used by the kernel ++ ++ switches= [HW,M68k] ++ ++ sysfs.deprecated=0|1 [KNL] ++ Enable/disable old style sysfs layout for old udev ++ on older distributions. When this option is enabled ++ very new udev will not work anymore. When this option ++ is disabled (or CONFIG_SYSFS_DEPRECATED not compiled) ++ in older udev will not work anymore. ++ Default depends on CONFIG_SYSFS_DEPRECATED_V2 set in ++ the kernel configuration. ++ ++ sysrq_always_enabled ++ [KNL] ++ Ignore sysrq setting - this boot parameter will ++ neutralize any effect of /proc/sys/kernel/sysrq. ++ Useful for debugging. ++ ++ tdfx= [HW,DRM] ++ ++ test_suspend= [SUSPEND] ++ Specify "mem" (for Suspend-to-RAM) or "standby" (for ++ standby suspend) as the system sleep state to briefly ++ enter during system startup. The system is woken from ++ this state using a wakeup-capable RTC alarm. ++ ++ thash_entries= [KNL,NET] ++ Set number of hash buckets for TCP connection ++ ++ thermal.act= [HW,ACPI] ++ -1: disable all active trip points in all thermal zones ++ : override all lowest active trip points ++ ++ thermal.crt= [HW,ACPI] ++ -1: disable all critical trip points in all thermal zones ++ : override all critical trip points ++ ++ thermal.nocrt= [HW,ACPI] ++ Set to disable actions on ACPI thermal zone ++ critical and hot trip points. ++ ++ thermal.off= [HW,ACPI] ++ 1: disable ACPI thermal control ++ ++ thermal.psv= [HW,ACPI] ++ -1: disable all passive trip points ++ : override all passive trip points to this ++ value ++ ++ thermal.tzp= [HW,ACPI] ++ Specify global default ACPI thermal zone polling rate ++ : poll all this frequency ++ 0: no polling (default) ++ ++ threadirqs [KNL] ++ Force threading of all interrupt handlers except those ++ marked explicitly IRQF_NO_THREAD. ++ ++ tmem [KNL,XEN] ++ Enable the Transcendent memory driver if built-in. ++ ++ tmem.cleancache=0|1 [KNL, XEN] ++ Default is on (1). Disable the usage of the cleancache ++ API to send anonymous pages to the hypervisor. ++ ++ tmem.frontswap=0|1 [KNL, XEN] ++ Default is on (1). Disable the usage of the frontswap ++ API to send swap pages to the hypervisor. If disabled ++ the selfballooning and selfshrinking are force disabled. ++ ++ tmem.selfballooning=0|1 [KNL, XEN] ++ Default is on (1). Disable the driving of swap pages ++ to the hypervisor. ++ ++ tmem.selfshrinking=0|1 [KNL, XEN] ++ Default is on (1). Partial swapoff that immediately ++ transfers pages from Xen hypervisor back to the ++ kernel based on different criteria. ++ ++ topology= [S390] ++ Format: {off | on} ++ Specify if the kernel should make use of the cpu ++ topology information if the hardware supports this. ++ The scheduler will make use of this information and ++ e.g. base its process migration decisions on it. ++ Default is on. ++ ++ tp720= [HW,PS2] ++ ++ tpm_suspend_pcr=[HW,TPM] ++ Format: integer pcr id ++ Specify that at suspend time, the tpm driver ++ should extend the specified pcr with zeros, ++ as a workaround for some chips which fail to ++ flush the last written pcr on TPM_SaveState. ++ This will guarantee that all the other pcrs ++ are saved. ++ ++ trace_buf_size=nn[KMG] ++ [FTRACE] will set tracing buffer size. ++ ++ trace_event=[event-list] ++ [FTRACE] Set and start specified trace events in order ++ to facilitate early boot debugging. ++ See also Documentation/trace/events.txt ++ ++ trace_options=[option-list] ++ [FTRACE] Enable or disable tracer options at boot. ++ The option-list is a comma delimited list of options ++ that can be enabled or disabled just as if you were ++ to echo the option name into ++ ++ /sys/kernel/debug/tracing/trace_options ++ ++ For example, to enable stacktrace option (to dump the ++ stack trace of each event), add to the command line: ++ ++ trace_options=stacktrace ++ ++ See also Documentation/trace/ftrace.txt "trace options" ++ section. ++ ++ traceoff_on_warning ++ [FTRACE] enable this option to disable tracing when a ++ warning is hit. This turns off "tracing_on". Tracing can ++ be enabled again by echoing '1' into the "tracing_on" ++ file located in /sys/kernel/debug/tracing/ ++ ++ This option is useful, as it disables the trace before ++ the WARNING dump is called, which prevents the trace to ++ be filled with content caused by the warning output. ++ ++ This option can also be set at run time via the sysctl ++ option: kernel/traceoff_on_warning ++ ++ transparent_hugepage= ++ [KNL] ++ Format: [always|madvise|never] ++ Can be used to control the default behavior of the system ++ with respect to transparent hugepages. ++ See Documentation/vm/transhuge.txt for more details. ++ ++ tsc= Disable clocksource stability checks for TSC. ++ Format: ++ [x86] reliable: mark tsc clocksource as reliable, this ++ disables clocksource verification at runtime, as well ++ as the stability checks done at bootup. Used to enable ++ high-resolution timer mode on older hardware, and in ++ virtualized environment. ++ [x86] noirqtime: Do not use TSC to do irq accounting. ++ Used to run time disable IRQ_TIME_ACCOUNTING on any ++ platforms where RDTSC is slow and this accounting ++ can add overhead. ++ ++ turbografx.map[2|3]= [HW,JOY] ++ TurboGraFX parallel port interface ++ Format: ++ ,,,,,,, ++ See also Documentation/input/joystick-parport.txt ++ ++ udbg-immortal [PPC] When debugging early kernel crashes that ++ happen after console_init() and before a proper ++ console driver takes over, this boot options might ++ help "seeing" what's going on. ++ ++ uhash_entries= [KNL,NET] ++ Set number of hash buckets for UDP/UDP-Lite connections ++ ++ uhci-hcd.ignore_oc= ++ [USB] Ignore overcurrent events (default N). ++ Some badly-designed motherboards generate lots of ++ bogus events, for ports that aren't wired to ++ anything. Set this parameter to avoid log spamming. ++ Note that genuine overcurrent events won't be ++ reported either. ++ ++ unknown_nmi_panic ++ [X86] Cause panic on unknown NMI. ++ ++ usbcore.authorized_default= ++ [USB] Default USB device authorization: ++ (default -1 = authorized except for wireless USB, ++ 0 = not authorized, 1 = authorized) ++ ++ usbcore.autosuspend= ++ [USB] The autosuspend time delay (in seconds) used ++ for newly-detected USB devices (default 2). This ++ is the time required before an idle device will be ++ autosuspended. Devices for which the delay is set ++ to a negative value won't be autosuspended at all. ++ ++ usbcore.usbfs_snoop= ++ [USB] Set to log all usbfs traffic (default 0 = off). ++ ++ usbcore.blinkenlights= ++ [USB] Set to cycle leds on hubs (default 0 = off). ++ ++ usbcore.old_scheme_first= ++ [USB] Start with the old device initialization ++ scheme (default 0 = off). ++ ++ usbcore.usbfs_memory_mb= ++ [USB] Memory limit (in MB) for buffers allocated by ++ usbfs (default = 16, 0 = max = 2047). ++ ++ usbcore.use_both_schemes= ++ [USB] Try the other device initialization scheme ++ if the first one fails (default 1 = enabled). ++ ++ usbcore.initial_descriptor_timeout= ++ [USB] Specifies timeout for the initial 64-byte ++ USB_REQ_GET_DESCRIPTOR request in milliseconds ++ (default 5000 = 5.0 seconds). ++ ++ usbhid.mousepoll= ++ [USBHID] The interval which mice are to be polled at. ++ ++ usb-storage.delay_use= ++ [UMS] The delay in seconds before a new device is ++ scanned for Logical Units (default 5). ++ ++ usb-storage.quirks= ++ [UMS] A list of quirks entries to supplement or ++ override the built-in unusual_devs list. List ++ entries are separated by commas. Each entry has ++ the form VID:PID:Flags where VID and PID are Vendor ++ and Product ID values (4-digit hex numbers) and ++ Flags is a set of characters, each corresponding ++ to a common usb-storage quirk flag as follows: ++ a = SANE_SENSE (collect more than 18 bytes ++ of sense data); ++ b = BAD_SENSE (don't collect more than 18 ++ bytes of sense data); ++ c = FIX_CAPACITY (decrease the reported ++ device capacity by one sector); ++ d = NO_READ_DISC_INFO (don't use ++ READ_DISC_INFO command); ++ e = NO_READ_CAPACITY_16 (don't use ++ READ_CAPACITY_16 command); ++ h = CAPACITY_HEURISTICS (decrease the ++ reported device capacity by one ++ sector if the number is odd); ++ i = IGNORE_DEVICE (don't bind to this ++ device); ++ l = NOT_LOCKABLE (don't try to lock and ++ unlock ejectable media); ++ m = MAX_SECTORS_64 (don't transfer more ++ than 64 sectors = 32 KB at a time); ++ n = INITIAL_READ10 (force a retry of the ++ initial READ(10) command); ++ o = CAPACITY_OK (accept the capacity ++ reported by the device); ++ p = WRITE_CACHE (the device cache is ON ++ by default); ++ r = IGNORE_RESIDUE (the device reports ++ bogus residue values); ++ s = SINGLE_LUN (the device has only one ++ Logical Unit); ++ w = NO_WP_DETECT (don't test whether the ++ medium is write-protected). ++ Example: quirks=0419:aaf5:rl,0421:0433:rc ++ ++ user_debug= [KNL,ARM] ++ Format: ++ See arch/arm/Kconfig.debug help text. ++ 1 - undefined instruction events ++ 2 - system calls ++ 4 - invalid data aborts ++ 8 - SIGSEGV faults ++ 16 - SIGBUS faults ++ Example: user_debug=31 ++ ++ userpte= ++ [X86] Flags controlling user PTE allocations. ++ ++ nohigh = do not allocate PTE pages in ++ HIGHMEM regardless of setting ++ of CONFIG_HIGHPTE. ++ ++ vdso= [X86,SH] ++ vdso=2: enable compat VDSO (default with COMPAT_VDSO) ++ vdso=1: enable VDSO (default) ++ vdso=0: disable VDSO mapping ++ ++ vdso32= [X86] ++ vdso32=2: enable compat VDSO (default with COMPAT_VDSO) ++ vdso32=1: enable 32-bit VDSO (default) ++ vdso32=0: disable 32-bit VDSO mapping ++ ++ vector= [IA-64,SMP] ++ vector=percpu: enable percpu vector domain ++ ++ video= [FB] Frame buffer configuration ++ See Documentation/fb/modedb.txt. ++ ++ video.brightness_switch_enabled= [0,1] ++ If set to 1, on receiving an ACPI notify event ++ generated by hotkey, video driver will adjust brightness ++ level and then send out the event to user space through ++ the allocated input device; If set to 0, video driver ++ will only send out the event without touching backlight ++ brightness level. ++ default: 1 ++ ++ virtio_mmio.device= ++ [VMMIO] Memory mapped virtio (platform) device. ++ ++ @:[:] ++ where: ++ := size (can use standard suffixes ++ like K, M and G) ++ := physical base address ++ := interrupt number (as passed to ++ request_irq()) ++ := (optional) platform device id ++ example: ++ virtio_mmio.device=1K@0x100b0000:48:7 ++ ++ Can be used multiple times for multiple devices. ++ ++ vga= [BOOT,X86-32] Select a particular video mode ++ See Documentation/x86/boot.txt and ++ Documentation/svga.txt. ++ Use vga=ask for menu. ++ This is actually a boot loader parameter; the value is ++ passed to the kernel using a special protocol. ++ ++ vmalloc=nn[KMG] [KNL,BOOT] Forces the vmalloc area to have an exact ++ size of . This can be used to increase the ++ minimum size (128MB on x86). It can also be used to ++ decrease the size and leave more room for directly ++ mapped kernel RAM. ++ ++ vmhalt= [KNL,S390] Perform z/VM CP command after system halt. ++ Format: ++ ++ vmpanic= [KNL,S390] Perform z/VM CP command after kernel panic. ++ Format: ++ ++ vmpoff= [KNL,S390] Perform z/VM CP command after power off. ++ Format: ++ ++ vsyscall= [X86-64] ++ Controls the behavior of vsyscalls (i.e. calls to ++ fixed addresses of 0xffffffffff600x00 from legacy ++ code). Most statically-linked binaries and older ++ versions of glibc use these calls. Because these ++ functions are at fixed addresses, they make nice ++ targets for exploits that can control RIP. ++ ++ emulate [default] Vsyscalls turn into traps and are ++ emulated reasonably safely. ++ ++ native Vsyscalls are native syscall instructions. ++ This is a little bit faster than trapping ++ and makes a few dynamic recompilers work ++ better than they would in emulation mode. ++ It also makes exploits much easier to write. ++ ++ none Vsyscalls don't work at all. This makes ++ them quite hard to use for exploits but ++ might break your system. ++ ++ vt.color= [VT] Default text color. ++ Format: 0xYX, X = foreground, Y = background. ++ Default: 0x07 = light gray on black. ++ ++ vt.cur_default= [VT] Default cursor shape. ++ Format: 0xCCBBAA, where AA, BB, and CC are the same as ++ the parameters of the [?A;B;Cc escape sequence; ++ see VGA-softcursor.txt. Default: 2 = underline. ++ ++ vt.default_blu= [VT] ++ Format: ,,,..., ++ Change the default blue palette of the console. ++ This is a 16-member array composed of values ++ ranging from 0-255. ++ ++ vt.default_grn= [VT] ++ Format: ,,,..., ++ Change the default green palette of the console. ++ This is a 16-member array composed of values ++ ranging from 0-255. ++ ++ vt.default_red= [VT] ++ Format: ,,,..., ++ Change the default red palette of the console. ++ This is a 16-member array composed of values ++ ranging from 0-255. ++ ++ vt.default_utf8= ++ [VT] ++ Format=<0|1> ++ Set system-wide default UTF-8 mode for all tty's. ++ Default is 1, i.e. UTF-8 mode is enabled for all ++ newly opened terminals. ++ ++ vt.global_cursor_default= ++ [VT] ++ Format=<-1|0|1> ++ Set system-wide default for whether a cursor ++ is shown on new VTs. Default is -1, ++ i.e. cursors will be created by default unless ++ overridden by individual drivers. 0 will hide ++ cursors, 1 will display them. ++ ++ vt.italic= [VT] Default color for italic text; 0-15. ++ Default: 2 = green. ++ ++ vt.underline= [VT] Default color for underlined text; 0-15. ++ Default: 3 = cyan. ++ ++ watchdog timers [HW,WDT] For information on watchdog timers, ++ see Documentation/watchdog/watchdog-parameters.txt ++ or other driver-specific files in the ++ Documentation/watchdog/ directory. ++ ++ workqueue.disable_numa ++ By default, all work items queued to unbound ++ workqueues are affine to the NUMA nodes they're ++ issued on, which results in better behavior in ++ general. If NUMA affinity needs to be disabled for ++ whatever reason, this option can be used. Note ++ that this also can be controlled per-workqueue for ++ workqueues visible under /sys/bus/workqueue/. ++ ++ workqueue.power_efficient ++ Per-cpu workqueues are generally preferred because ++ they show better performance thanks to cache ++ locality; unfortunately, per-cpu workqueues tend to ++ be more power hungry than unbound workqueues. ++ ++ Enabling this makes the per-cpu workqueues which ++ were observed to contribute significantly to power ++ consumption unbound, leading to measurably lower ++ power usage at the cost of small performance ++ overhead. ++ ++ The default value of this parameter is determined by ++ the config option CONFIG_WQ_POWER_EFFICIENT_DEFAULT. ++ ++ x2apic_phys [X86-64,APIC] Use x2apic physical mode instead of ++ default x2apic cluster mode on platforms ++ supporting x2apic. ++ ++ x86_intel_mid_timer= [X86-32,APBT] ++ Choose timer option for x86 Intel MID platform. ++ Two valid options are apbt timer only and lapic timer ++ plus one apbt timer for broadcast timer. ++ x86_intel_mid_timer=apbt_only | lapic_and_apbt ++ ++ xen_emul_unplug= [HW,X86,XEN] ++ Unplug Xen emulated devices ++ Format: [unplug0,][unplug1] ++ ide-disks -- unplug primary master IDE devices ++ aux-ide-disks -- unplug non-primary-master IDE devices ++ nics -- unplug network devices ++ all -- unplug all emulated devices (NICs and IDE disks) ++ unnecessary -- unplugging emulated devices is ++ unnecessary even if the host did not respond to ++ the unplug protocol ++ never -- do not unplug even if version check succeeds ++ ++ xen_nopvspin [X86,XEN] ++ Disables the ticketlock slowpath using Xen PV ++ optimizations. ++ ++ xirc2ps_cs= [NET,PCMCIA] ++ Format: ++ ,,,,,[,[,[,]]] ++ ++______________________________________________________________________ ++ ++TODO: ++ ++ Add more DRM drivers. +diff -Nur linux-3.14.36/Documentation/networking/gianfar.txt linux-openelec/Documentation/networking/gianfar.txt +--- linux-3.14.36/Documentation/networking/gianfar.txt 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/Documentation/networking/gianfar.txt 2015-05-06 12:05:42.000000000 -0500 +@@ -1,38 +1,8 @@ + The Gianfar Ethernet Driver +-Sysfs File description + + Author: Andy Fleming + Updated: 2005-07-28 + +-SYSFS +- +-Several of the features of the gianfar driver are controlled +-through sysfs files. These are: +- +-bd_stash: +-To stash RX Buffer Descriptors in the L2, echo 'on' or '1' to +-bd_stash, echo 'off' or '0' to disable +- +-rx_stash_len: +-To stash the first n bytes of the packet in L2, echo the number +-of bytes to buf_stash_len. echo 0 to disable. +- +-WARNING: You could really screw these up if you set them too low or high! +-fifo_threshold: +-To change the number of bytes the controller needs in the +-fifo before it starts transmission, echo the number of bytes to +-fifo_thresh. Range should be 0-511. +- +-fifo_starve: +-When the FIFO has less than this many bytes during a transmit, it +-enters starve mode, and increases the priority of TX memory +-transactions. To change, echo the number of bytes to +-fifo_starve. Range should be 0-511. +- +-fifo_starve_off: +-Once in starve mode, the FIFO remains there until it has this +-many bytes. To change, echo the number of bytes to +-fifo_starve_off. Range should be 0-511. + + CHECKSUM OFFLOADING + +diff -Nur linux-3.14.36/drivers/ata/acard-ahci.c linux-openelec/drivers/ata/acard-ahci.c +--- linux-3.14.36/drivers/ata/acard-ahci.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/acard-ahci.c 2015-05-06 12:05:42.000000000 -0500 +@@ -36,7 +36,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/ahci.c linux-openelec/drivers/ata/ahci.c +--- linux-3.14.36/drivers/ata/ahci.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/ahci.c 2015-07-24 18:03:29.216842002 -0500 +@@ -35,7 +35,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -610,6 +609,7 @@ + unsigned long deadline) + { + struct ata_port *ap = link->ap; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + bool online; + int rc; + +@@ -620,7 +620,7 @@ + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + +@@ -635,6 +635,7 @@ + { + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -650,7 +651,7 @@ + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* The pseudo configuration device on SIMG4726 attached to + * ASUS P5W-DH Deluxe doesn't send signature FIS after +@@ -1146,6 +1147,17 @@ + return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff); + } + ++static bool ahci_broken_devslp(struct pci_dev *pdev) ++{ ++ /* device with broken DEVSLP but still showing SDS capability */ ++ static const struct pci_device_id ids[] = { ++ { PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */ ++ {} ++ }; ++ ++ return pci_match_id(ids, pdev); ++} ++ + #ifdef CONFIG_ATA_ACPI + static void ahci_gtf_filter_workaround(struct ata_host *host) + { +@@ -1397,6 +1409,10 @@ + + hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; + ++ /* must set flag prior to save config in order to take effect */ ++ if (ahci_broken_devslp(pdev)) ++ hpriv->flags |= AHCI_HFLAG_NO_DEVSLP; ++ + /* save initial config */ + ahci_pci_save_initial_config(pdev, hpriv); + +diff -Nur linux-3.14.36/drivers/ata/ahci.c.orig linux-openelec/drivers/ata/ahci.c.orig +--- linux-3.14.36/drivers/ata/ahci.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/ata/ahci.c.orig 2015-07-24 18:03:29.024842002 -0500 +@@ -0,0 +1,1533 @@ ++/* ++ * ahci.c - AHCI SATA support ++ * ++ * Maintained by: Tejun Heo ++ * Please ALWAYS copy linux-ide@vger.kernel.org ++ * on emails. ++ * ++ * Copyright 2004-2005 Red Hat, Inc. ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ * libata documentation is available via 'make {ps|pdf}docs', ++ * as Documentation/DocBook/libata.* ++ * ++ * AHCI hardware documentation: ++ * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf ++ * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ahci.h" ++ ++#define DRV_NAME "ahci" ++#define DRV_VERSION "3.0" ++ ++enum { ++ AHCI_PCI_BAR_STA2X11 = 0, ++ AHCI_PCI_BAR_ENMOTUS = 2, ++ AHCI_PCI_BAR_STANDARD = 5, ++}; ++ ++enum board_ids { ++ /* board IDs by feature in alphabetical order */ ++ board_ahci, ++ board_ahci_ign_iferr, ++ board_ahci_nomsi, ++ board_ahci_noncq, ++ board_ahci_nosntf, ++ board_ahci_yes_fbs, ++ ++ /* board IDs for specific chipsets in alphabetical order */ ++ board_ahci_mcp65, ++ board_ahci_mcp77, ++ board_ahci_mcp89, ++ board_ahci_mv, ++ board_ahci_sb600, ++ board_ahci_sb700, /* for SB700 and SB800 */ ++ board_ahci_vt8251, ++ ++ /* aliases */ ++ board_ahci_mcp_linux = board_ahci_mcp65, ++ board_ahci_mcp67 = board_ahci_mcp65, ++ board_ahci_mcp73 = board_ahci_mcp65, ++ board_ahci_mcp79 = board_ahci_mcp77, ++}; ++ ++static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); ++static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, ++ unsigned long deadline); ++static void ahci_mcp89_apple_enable(struct pci_dev *pdev); ++static bool is_mcp89_apple(struct pci_dev *pdev); ++static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, ++ unsigned long deadline); ++#ifdef CONFIG_PM ++static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); ++static int ahci_pci_device_resume(struct pci_dev *pdev); ++#endif ++ ++static struct scsi_host_template ahci_sht = { ++ AHCI_SHT("ahci"), ++}; ++ ++static struct ata_port_operations ahci_vt8251_ops = { ++ .inherits = &ahci_ops, ++ .hardreset = ahci_vt8251_hardreset, ++}; ++ ++static struct ata_port_operations ahci_p5wdh_ops = { ++ .inherits = &ahci_ops, ++ .hardreset = ahci_p5wdh_hardreset, ++}; ++ ++static const struct ata_port_info ahci_port_info[] = { ++ /* by features */ ++ [board_ahci] = { ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_ign_iferr] = { ++ AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_nomsi] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_MSI), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_noncq] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_nosntf] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_yes_fbs] = { ++ AHCI_HFLAGS (AHCI_HFLAG_YES_FBS), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ /* by chipsets */ ++ [board_ahci_mcp65] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | ++ AHCI_HFLAG_YES_NCQ), ++ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_mcp77] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_mcp89] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_mv] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | ++ AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP), ++ .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_ops, ++ }, ++ [board_ahci_sb600] = { ++ AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | ++ AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 | ++ AHCI_HFLAG_32BIT_ONLY), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_pmp_retry_srst_ops, ++ }, ++ [board_ahci_sb700] = { /* for SB700 and SB800 */ ++ AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_pmp_retry_srst_ops, ++ }, ++ [board_ahci_vt8251] = { ++ AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP), ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_vt8251_ops, ++ }, ++}; ++ ++static const struct pci_device_id ahci_pci_tbl[] = { ++ /* Intel */ ++ { PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */ ++ { PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */ ++ { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */ ++ { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */ ++ { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */ ++ { PCI_VDEVICE(AL, 0x5288), board_ahci_ign_iferr }, /* ULi M5288 */ ++ { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */ ++ { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */ ++ { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */ ++ { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */ ++ { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */ ++ { PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8 */ ++ { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */ ++ { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */ ++ { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */ ++ { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */ ++ { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */ ++ { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */ ++ { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */ ++ { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */ ++ { PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */ ++ { PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */ ++ { PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */ ++ { PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */ ++ { PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */ ++ { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */ ++ { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */ ++ { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */ ++ { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */ ++ { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */ ++ { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */ ++ { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */ ++ { PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */ ++ { PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */ ++ { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */ ++ { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */ ++ { PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */ ++ { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */ ++ { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */ ++ { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1c02), board_ahci }, /* CPT AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1c03), board_ahci }, /* CPT AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1c04), board_ahci }, /* CPT RAID */ ++ { PCI_VDEVICE(INTEL, 0x1c05), board_ahci }, /* CPT RAID */ ++ { PCI_VDEVICE(INTEL, 0x1c06), board_ahci }, /* CPT RAID */ ++ { PCI_VDEVICE(INTEL, 0x1c07), board_ahci }, /* CPT RAID */ ++ { PCI_VDEVICE(INTEL, 0x1d02), board_ahci }, /* PBG AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1d04), board_ahci }, /* PBG RAID */ ++ { PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */ ++ { PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* PBG RAID */ ++ { PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1e02), board_ahci }, /* Panther Point AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1e03), board_ahci }, /* Panther Point AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1e04), board_ahci }, /* Panther Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x1e05), board_ahci }, /* Panther Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c02), board_ahci }, /* Lynx Point AHCI */ ++ { PCI_VDEVICE(INTEL, 0x8c03), board_ahci }, /* Lynx Point AHCI */ ++ { PCI_VDEVICE(INTEL, 0x8c04), board_ahci }, /* Lynx Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c05), board_ahci }, /* Lynx Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c06), board_ahci }, /* Lynx Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c07), board_ahci }, /* Lynx Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c0f), board_ahci }, /* Lynx Point RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c02), board_ahci }, /* Lynx Point-LP AHCI */ ++ { PCI_VDEVICE(INTEL, 0x9c03), board_ahci }, /* Lynx Point-LP AHCI */ ++ { PCI_VDEVICE(INTEL, 0x9c04), board_ahci }, /* Lynx Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c05), board_ahci }, /* Lynx Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c06), board_ahci }, /* Lynx Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c07), board_ahci }, /* Lynx Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c0e), board_ahci }, /* Lynx Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c0f), board_ahci }, /* Lynx Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f22), board_ahci }, /* Avoton AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1f23), board_ahci }, /* Avoton AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1f24), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f25), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f26), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */ ++ { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ ++ { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */ ++ { PCI_VDEVICE(INTEL, 0x8d04), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x8d06), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x8d0e), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x8d62), board_ahci }, /* Wellsburg AHCI */ ++ { PCI_VDEVICE(INTEL, 0x8d64), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x8d66), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x8d6e), board_ahci }, /* Wellsburg RAID */ ++ { PCI_VDEVICE(INTEL, 0x23a3), board_ahci }, /* Coleto Creek AHCI */ ++ { PCI_VDEVICE(INTEL, 0x9c83), board_ahci }, /* Wildcat Point-LP AHCI */ ++ { PCI_VDEVICE(INTEL, 0x9c85), board_ahci }, /* Wildcat Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c87), board_ahci }, /* Wildcat Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x9c8f), board_ahci }, /* Wildcat Point-LP RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c82), board_ahci }, /* 9 Series AHCI */ ++ { PCI_VDEVICE(INTEL, 0x8c83), board_ahci }, /* 9 Series AHCI */ ++ { PCI_VDEVICE(INTEL, 0x8c84), board_ahci }, /* 9 Series RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c85), board_ahci }, /* 9 Series RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c86), board_ahci }, /* 9 Series RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */ ++ { PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */ ++ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */ ++ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */ ++ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */ ++ { PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */ ++ { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */ ++ ++ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ ++ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, ++ PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr }, ++ /* JMicron 362B and 362C have an AHCI function with IDE class code */ ++ { PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr }, ++ { PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr }, ++ ++ /* ATI */ ++ { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ ++ { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb700 }, /* ATI SB700/800 */ ++ { PCI_VDEVICE(ATI, 0x4391), board_ahci_sb700 }, /* ATI SB700/800 */ ++ { PCI_VDEVICE(ATI, 0x4392), board_ahci_sb700 }, /* ATI SB700/800 */ ++ { PCI_VDEVICE(ATI, 0x4393), board_ahci_sb700 }, /* ATI SB700/800 */ ++ { PCI_VDEVICE(ATI, 0x4394), board_ahci_sb700 }, /* ATI SB700/800 */ ++ { PCI_VDEVICE(ATI, 0x4395), board_ahci_sb700 }, /* ATI SB700/800 */ ++ ++ /* AMD */ ++ { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */ ++ { PCI_VDEVICE(AMD, 0x7900), board_ahci }, /* AMD CZ */ ++ /* AMD is using RAID class only for ahci controllers */ ++ { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, ++ PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci }, ++ ++ /* VIA */ ++ { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ ++ { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */ ++ ++ /* NVIDIA */ ++ { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */ ++ { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_mcp67 }, /* MCP67 */ ++ { PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0581), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0582), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0583), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0584), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0585), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0586), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0587), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0588), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x0589), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x058a), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x058b), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x058c), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x058d), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x058e), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x058f), board_ahci_mcp_linux }, /* Linux ID */ ++ { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_mcp73 }, /* MCP73 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci_mcp77 }, /* MCP77 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci_mcp79 }, /* MCP79 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci_mcp89 }, /* MCP89 */ ++ { PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci_mcp89 }, /* MCP89 */ ++ ++ /* SiS */ ++ { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */ ++ { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 968 */ ++ { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */ ++ ++ /* ST Microelectronics */ ++ { PCI_VDEVICE(STMICRO, 0xCC06), board_ahci }, /* ST ConneXt */ ++ ++ /* Marvell */ ++ { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */ ++ { PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv }, /* 6121 */ ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9123), ++ .class = PCI_CLASS_STORAGE_SATA_AHCI, ++ .class_mask = 0xffffff, ++ .driver_data = board_ahci_yes_fbs }, /* 88se9128 */ ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9125), ++ .driver_data = board_ahci_yes_fbs }, /* 88se9125 */ ++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_MARVELL_EXT, 0x9178, ++ PCI_VENDOR_ID_MARVELL_EXT, 0x9170), ++ .driver_data = board_ahci_yes_fbs }, /* 88se9170 */ ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x917a), ++ .driver_data = board_ahci_yes_fbs }, /* 88se9172 */ ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9172), ++ .driver_data = board_ahci_yes_fbs }, /* 88se9182 */ ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9182), ++ .driver_data = board_ahci_yes_fbs }, /* 88se9172 */ ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9192), ++ .driver_data = board_ahci_yes_fbs }, /* 88se9172 on some Gigabyte */ ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0), ++ .driver_data = board_ahci_yes_fbs }, ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3), ++ .driver_data = board_ahci_yes_fbs }, ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230), ++ .driver_data = board_ahci_yes_fbs }, ++ { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642), ++ .driver_data = board_ahci_yes_fbs }, ++ ++ /* Promise */ ++ { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */ ++ { PCI_VDEVICE(PROMISE, 0x3781), board_ahci }, /* FastTrak TX8660 ahci-mode */ ++ ++ /* Asmedia */ ++ { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */ ++ { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci }, /* ASM1060 */ ++ { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */ ++ { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */ ++ ++ /* ++ * Samsung SSDs found on some macbooks. NCQ times out if MSI is ++ * enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731 ++ */ ++ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi }, ++ ++ /* Enmotus */ ++ { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, ++ ++ /* Generic, PCI class code for AHCI */ ++ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, ++ PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci }, ++ ++ { } /* terminate list */ ++}; ++ ++ ++static struct pci_driver ahci_pci_driver = { ++ .name = DRV_NAME, ++ .id_table = ahci_pci_tbl, ++ .probe = ahci_init_one, ++ .remove = ata_pci_remove_one, ++#ifdef CONFIG_PM ++ .suspend = ahci_pci_device_suspend, ++ .resume = ahci_pci_device_resume, ++#endif ++}; ++ ++#if defined(CONFIG_PATA_MARVELL) || defined(CONFIG_PATA_MARVELL_MODULE) ++static int marvell_enable; ++#else ++static int marvell_enable = 1; ++#endif ++module_param(marvell_enable, int, 0644); ++MODULE_PARM_DESC(marvell_enable, "Marvell SATA via AHCI (1 = enabled)"); ++ ++ ++static void ahci_pci_save_initial_config(struct pci_dev *pdev, ++ struct ahci_host_priv *hpriv) ++{ ++ unsigned int force_port_map = 0; ++ unsigned int mask_port_map = 0; ++ ++ if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) { ++ dev_info(&pdev->dev, "JMB361 has only one port\n"); ++ force_port_map = 1; ++ } ++ ++ /* ++ * Temporary Marvell 6145 hack: PATA port presence ++ * is asserted through the standard AHCI port ++ * presence register, as bit 4 (counting from 0) ++ */ ++ if (hpriv->flags & AHCI_HFLAG_MV_PATA) { ++ if (pdev->device == 0x6121) ++ mask_port_map = 0x3; ++ else ++ mask_port_map = 0xf; ++ dev_info(&pdev->dev, ++ "Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n"); ++ } ++ ++ ahci_save_initial_config(&pdev->dev, hpriv, force_port_map, ++ mask_port_map); ++} ++ ++static int ahci_pci_reset_controller(struct ata_host *host) ++{ ++ struct pci_dev *pdev = to_pci_dev(host->dev); ++ ++ ahci_reset_controller(host); ++ ++ if (pdev->vendor == PCI_VENDOR_ID_INTEL) { ++ struct ahci_host_priv *hpriv = host->private_data; ++ u16 tmp16; ++ ++ /* configure PCS */ ++ pci_read_config_word(pdev, 0x92, &tmp16); ++ if ((tmp16 & hpriv->port_map) != hpriv->port_map) { ++ tmp16 |= hpriv->port_map; ++ pci_write_config_word(pdev, 0x92, tmp16); ++ } ++ } ++ ++ return 0; ++} ++ ++static void ahci_pci_init_controller(struct ata_host *host) ++{ ++ struct ahci_host_priv *hpriv = host->private_data; ++ struct pci_dev *pdev = to_pci_dev(host->dev); ++ void __iomem *port_mmio; ++ u32 tmp; ++ int mv; ++ ++ if (hpriv->flags & AHCI_HFLAG_MV_PATA) { ++ if (pdev->device == 0x6121) ++ mv = 2; ++ else ++ mv = 4; ++ port_mmio = __ahci_port_base(host, mv); ++ ++ writel(0, port_mmio + PORT_IRQ_MASK); ++ ++ /* clear port IRQ */ ++ tmp = readl(port_mmio + PORT_IRQ_STAT); ++ VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); ++ if (tmp) ++ writel(tmp, port_mmio + PORT_IRQ_STAT); ++ } ++ ++ ahci_init_controller(host); ++} ++ ++static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, ++ unsigned long deadline) ++{ ++ struct ata_port *ap = link->ap; ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ bool online; ++ int rc; ++ ++ DPRINTK("ENTER\n"); ++ ++ ahci_stop_engine(ap); ++ ++ rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), ++ deadline, &online, NULL); ++ ++ hpriv->start_engine(ap); ++ ++ DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); ++ ++ /* vt8251 doesn't clear BSY on signature FIS reception, ++ * request follow-up softreset. ++ */ ++ return online ? -EAGAIN : rc; ++} ++ ++static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, ++ unsigned long deadline) ++{ ++ struct ata_port *ap = link->ap; ++ struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; ++ struct ata_taskfile tf; ++ bool online; ++ int rc; ++ ++ ahci_stop_engine(ap); ++ ++ /* clear D2H reception area to properly wait for D2H FIS */ ++ ata_tf_init(link->device, &tf); ++ tf.command = ATA_BUSY; ++ ata_tf_to_fis(&tf, 0, 0, d2h_fis); ++ ++ rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), ++ deadline, &online, NULL); ++ ++ hpriv->start_engine(ap); ++ ++ /* The pseudo configuration device on SIMG4726 attached to ++ * ASUS P5W-DH Deluxe doesn't send signature FIS after ++ * hardreset if no device is attached to the first downstream ++ * port && the pseudo device locks up on SRST w/ PMP==0. To ++ * work around this, wait for !BSY only briefly. If BSY isn't ++ * cleared, perform CLO and proceed to IDENTIFY (achieved by ++ * ATA_LFLAG_NO_SRST and ATA_LFLAG_ASSUME_ATA). ++ * ++ * Wait for two seconds. Devices attached to downstream port ++ * which can't process the following IDENTIFY after this will ++ * have to be reset again. For most cases, this should ++ * suffice while making probing snappish enough. ++ */ ++ if (online) { ++ rc = ata_wait_after_reset(link, jiffies + 2 * HZ, ++ ahci_check_ready); ++ if (rc) ++ ahci_kick_engine(ap); ++ } ++ return rc; ++} ++ ++#ifdef CONFIG_PM ++static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) ++{ ++ struct ata_host *host = pci_get_drvdata(pdev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ void __iomem *mmio = hpriv->mmio; ++ u32 ctl; ++ ++ if (mesg.event & PM_EVENT_SUSPEND && ++ hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { ++ dev_err(&pdev->dev, ++ "BIOS update required for suspend/resume\n"); ++ return -EIO; ++ } ++ ++ if (mesg.event & PM_EVENT_SLEEP) { ++ /* AHCI spec rev1.1 section 8.3.3: ++ * Software must disable interrupts prior to requesting a ++ * transition of the HBA to D3 state. ++ */ ++ ctl = readl(mmio + HOST_CTL); ++ ctl &= ~HOST_IRQ_EN; ++ writel(ctl, mmio + HOST_CTL); ++ readl(mmio + HOST_CTL); /* flush */ ++ } ++ ++ return ata_pci_device_suspend(pdev, mesg); ++} ++ ++static int ahci_pci_device_resume(struct pci_dev *pdev) ++{ ++ struct ata_host *host = pci_get_drvdata(pdev); ++ int rc; ++ ++ rc = ata_pci_device_do_resume(pdev); ++ if (rc) ++ return rc; ++ ++ /* Apple BIOS helpfully mangles the registers on resume */ ++ if (is_mcp89_apple(pdev)) ++ ahci_mcp89_apple_enable(pdev); ++ ++ if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_pci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_pci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++#endif ++ ++static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) ++{ ++ int rc; ++ ++ /* ++ * If the device fixup already set the dma_mask to some non-standard ++ * value, don't extend it here. This happens on STA2X11, for example. ++ */ ++ if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32)) ++ return 0; ++ ++ if (using_dac && ++ !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { ++ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); ++ if (rc) { ++ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (rc) { ++ dev_err(&pdev->dev, ++ "64-bit DMA enable failed\n"); ++ return rc; ++ } ++ } ++ } else { ++ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (rc) { ++ dev_err(&pdev->dev, "32-bit DMA enable failed\n"); ++ return rc; ++ } ++ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (rc) { ++ dev_err(&pdev->dev, ++ "32-bit consistent DMA enable failed\n"); ++ return rc; ++ } ++ } ++ return 0; ++} ++ ++static void ahci_pci_print_info(struct ata_host *host) ++{ ++ struct pci_dev *pdev = to_pci_dev(host->dev); ++ u16 cc; ++ const char *scc_s; ++ ++ pci_read_config_word(pdev, 0x0a, &cc); ++ if (cc == PCI_CLASS_STORAGE_IDE) ++ scc_s = "IDE"; ++ else if (cc == PCI_CLASS_STORAGE_SATA) ++ scc_s = "SATA"; ++ else if (cc == PCI_CLASS_STORAGE_RAID) ++ scc_s = "RAID"; ++ else ++ scc_s = "unknown"; ++ ++ ahci_print_info(host, scc_s); ++} ++ ++/* On ASUS P5W DH Deluxe, the second port of PCI device 00:1f.2 is ++ * hardwired to on-board SIMG 4726. The chipset is ICH8 and doesn't ++ * support PMP and the 4726 either directly exports the device ++ * attached to the first downstream port or acts as a hardware storage ++ * controller and emulate a single ATA device (can be RAID 0/1 or some ++ * other configuration). ++ * ++ * When there's no device attached to the first downstream port of the ++ * 4726, "Config Disk" appears, which is a pseudo ATA device to ++ * configure the 4726. However, ATA emulation of the device is very ++ * lame. It doesn't send signature D2H Reg FIS after the initial ++ * hardreset, pukes on SRST w/ PMP==0 and has bunch of other issues. ++ * ++ * The following function works around the problem by always using ++ * hardreset on the port and not depending on receiving signature FIS ++ * afterward. If signature FIS isn't received soon, ATA class is ++ * assumed without follow-up softreset. ++ */ ++static void ahci_p5wdh_workaround(struct ata_host *host) ++{ ++ static struct dmi_system_id sysids[] = { ++ { ++ .ident = "P5W DH Deluxe", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, ++ "ASUSTEK COMPUTER INC"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "P5W DH Deluxe"), ++ }, ++ }, ++ { } ++ }; ++ struct pci_dev *pdev = to_pci_dev(host->dev); ++ ++ if (pdev->bus->number == 0 && pdev->devfn == PCI_DEVFN(0x1f, 2) && ++ dmi_check_system(sysids)) { ++ struct ata_port *ap = host->ports[1]; ++ ++ dev_info(&pdev->dev, ++ "enabling ASUS P5W DH Deluxe on-board SIMG4726 workaround\n"); ++ ++ ap->ops = &ahci_p5wdh_ops; ++ ap->link.flags |= ATA_LFLAG_NO_SRST | ATA_LFLAG_ASSUME_ATA; ++ } ++} ++ ++/* ++ * Macbook7,1 firmware forcibly disables MCP89 AHCI and changes PCI ID when ++ * booting in BIOS compatibility mode. We restore the registers but not ID. ++ */ ++static void ahci_mcp89_apple_enable(struct pci_dev *pdev) ++{ ++ u32 val; ++ ++ printk(KERN_INFO "ahci: enabling MCP89 AHCI mode\n"); ++ ++ pci_read_config_dword(pdev, 0xf8, &val); ++ val |= 1 << 0x1b; ++ /* the following changes the device ID, but appears not to affect function */ ++ /* val = (val & ~0xf0000000) | 0x80000000; */ ++ pci_write_config_dword(pdev, 0xf8, val); ++ ++ pci_read_config_dword(pdev, 0x54c, &val); ++ val |= 1 << 0xc; ++ pci_write_config_dword(pdev, 0x54c, val); ++ ++ pci_read_config_dword(pdev, 0x4a4, &val); ++ val &= 0xff; ++ val |= 0x01060100; ++ pci_write_config_dword(pdev, 0x4a4, val); ++ ++ pci_read_config_dword(pdev, 0x54c, &val); ++ val &= ~(1 << 0xc); ++ pci_write_config_dword(pdev, 0x54c, val); ++ ++ pci_read_config_dword(pdev, 0xf8, &val); ++ val &= ~(1 << 0x1b); ++ pci_write_config_dword(pdev, 0xf8, val); ++} ++ ++static bool is_mcp89_apple(struct pci_dev *pdev) ++{ ++ return pdev->vendor == PCI_VENDOR_ID_NVIDIA && ++ pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA && ++ pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE && ++ pdev->subsystem_device == 0xcb89; ++} ++ ++/* only some SB600 ahci controllers can do 64bit DMA */ ++static bool ahci_sb600_enable_64bit(struct pci_dev *pdev) ++{ ++ static const struct dmi_system_id sysids[] = { ++ /* ++ * The oldest version known to be broken is 0901 and ++ * working is 1501 which was released on 2007-10-26. ++ * Enable 64bit DMA on 1501 and anything newer. ++ * ++ * Please read bko#9412 for more info. ++ */ ++ { ++ .ident = "ASUS M2A-VM", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, ++ "ASUSTeK Computer INC."), ++ DMI_MATCH(DMI_BOARD_NAME, "M2A-VM"), ++ }, ++ .driver_data = "20071026", /* yyyymmdd */ ++ }, ++ /* ++ * All BIOS versions for the MSI K9A2 Platinum (MS-7376) ++ * support 64bit DMA. ++ * ++ * BIOS versions earlier than 1.5 had the Manufacturer DMI ++ * fields as "MICRO-STAR INTERANTIONAL CO.,LTD". ++ * This spelling mistake was fixed in BIOS version 1.5, so ++ * 1.5 and later have the Manufacturer as ++ * "MICRO-STAR INTERNATIONAL CO.,LTD". ++ * So try to match on DMI_BOARD_VENDOR of "MICRO-STAR INTER". ++ * ++ * BIOS versions earlier than 1.9 had a Board Product Name ++ * DMI field of "MS-7376". This was changed to be ++ * "K9A2 Platinum (MS-7376)" in version 1.9, but we can still ++ * match on DMI_BOARD_NAME of "MS-7376". ++ */ ++ { ++ .ident = "MSI K9A2 Platinum", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, ++ "MICRO-STAR INTER"), ++ DMI_MATCH(DMI_BOARD_NAME, "MS-7376"), ++ }, ++ }, ++ /* ++ * All BIOS versions for the MSI K9AGM2 (MS-7327) support ++ * 64bit DMA. ++ * ++ * This board also had the typo mentioned above in the ++ * Manufacturer DMI field (fixed in BIOS version 1.5), so ++ * match on DMI_BOARD_VENDOR of "MICRO-STAR INTER" again. ++ */ ++ { ++ .ident = "MSI K9AGM2", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, ++ "MICRO-STAR INTER"), ++ DMI_MATCH(DMI_BOARD_NAME, "MS-7327"), ++ }, ++ }, ++ /* ++ * All BIOS versions for the Asus M3A support 64bit DMA. ++ * (all release versions from 0301 to 1206 were tested) ++ */ ++ { ++ .ident = "ASUS M3A", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, ++ "ASUSTeK Computer INC."), ++ DMI_MATCH(DMI_BOARD_NAME, "M3A"), ++ }, ++ }, ++ { } ++ }; ++ const struct dmi_system_id *match; ++ int year, month, date; ++ char buf[9]; ++ ++ match = dmi_first_match(sysids); ++ if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(0x12, 0) || ++ !match) ++ return false; ++ ++ if (!match->driver_data) ++ goto enable_64bit; ++ ++ dmi_get_date(DMI_BIOS_DATE, &year, &month, &date); ++ snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date); ++ ++ if (strcmp(buf, match->driver_data) >= 0) ++ goto enable_64bit; ++ else { ++ dev_warn(&pdev->dev, ++ "%s: BIOS too old, forcing 32bit DMA, update BIOS\n", ++ match->ident); ++ return false; ++ } ++ ++enable_64bit: ++ dev_warn(&pdev->dev, "%s: enabling 64bit DMA\n", match->ident); ++ return true; ++} ++ ++static bool ahci_broken_system_poweroff(struct pci_dev *pdev) ++{ ++ static const struct dmi_system_id broken_systems[] = { ++ { ++ .ident = "HP Compaq nx6310", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6310"), ++ }, ++ /* PCI slot number of the controller */ ++ .driver_data = (void *)0x1FUL, ++ }, ++ { ++ .ident = "HP Compaq 6720s", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 6720s"), ++ }, ++ /* PCI slot number of the controller */ ++ .driver_data = (void *)0x1FUL, ++ }, ++ ++ { } /* terminate list */ ++ }; ++ const struct dmi_system_id *dmi = dmi_first_match(broken_systems); ++ ++ if (dmi) { ++ unsigned long slot = (unsigned long)dmi->driver_data; ++ /* apply the quirk only to on-board controllers */ ++ return slot == PCI_SLOT(pdev->devfn); ++ } ++ ++ return false; ++} ++ ++static bool ahci_broken_suspend(struct pci_dev *pdev) ++{ ++ static const struct dmi_system_id sysids[] = { ++ /* ++ * On HP dv[4-6] and HDX18 with earlier BIOSen, link ++ * to the harddisk doesn't become online after ++ * resuming from STR. Warn and fail suspend. ++ * ++ * http://bugzilla.kernel.org/show_bug.cgi?id=12276 ++ * ++ * Use dates instead of versions to match as HP is ++ * apparently recycling both product and version ++ * strings. ++ * ++ * http://bugzilla.kernel.org/show_bug.cgi?id=15462 ++ */ ++ { ++ .ident = "dv4", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, ++ "HP Pavilion dv4 Notebook PC"), ++ }, ++ .driver_data = "20090105", /* F.30 */ ++ }, ++ { ++ .ident = "dv5", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, ++ "HP Pavilion dv5 Notebook PC"), ++ }, ++ .driver_data = "20090506", /* F.16 */ ++ }, ++ { ++ .ident = "dv6", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, ++ "HP Pavilion dv6 Notebook PC"), ++ }, ++ .driver_data = "20090423", /* F.21 */ ++ }, ++ { ++ .ident = "HDX18", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, ++ "HP HDX18 Notebook PC"), ++ }, ++ .driver_data = "20090430", /* F.23 */ ++ }, ++ /* ++ * Acer eMachines G725 has the same problem. BIOS ++ * V1.03 is known to be broken. V3.04 is known to ++ * work. Between, there are V1.06, V2.06 and V3.03 ++ * that we don't have much idea about. For now, ++ * blacklist anything older than V3.04. ++ * ++ * http://bugzilla.kernel.org/show_bug.cgi?id=15104 ++ */ ++ { ++ .ident = "G725", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "eMachines"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "eMachines G725"), ++ }, ++ .driver_data = "20091216", /* V3.04 */ ++ }, ++ { } /* terminate list */ ++ }; ++ const struct dmi_system_id *dmi = dmi_first_match(sysids); ++ int year, month, date; ++ char buf[9]; ++ ++ if (!dmi || pdev->bus->number || pdev->devfn != PCI_DEVFN(0x1f, 2)) ++ return false; ++ ++ dmi_get_date(DMI_BIOS_DATE, &year, &month, &date); ++ snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date); ++ ++ return strcmp(buf, dmi->driver_data) < 0; ++} ++ ++static bool ahci_broken_online(struct pci_dev *pdev) ++{ ++#define ENCODE_BUSDEVFN(bus, slot, func) \ ++ (void *)(unsigned long)(((bus) << 8) | PCI_DEVFN((slot), (func))) ++ static const struct dmi_system_id sysids[] = { ++ /* ++ * There are several gigabyte boards which use ++ * SIMG5723s configured as hardware RAID. Certain ++ * 5723 firmware revisions shipped there keep the link ++ * online but fail to answer properly to SRST or ++ * IDENTIFY when no device is attached downstream ++ * causing libata to retry quite a few times leading ++ * to excessive detection delay. ++ * ++ * As these firmwares respond to the second reset try ++ * with invalid device signature, considering unknown ++ * sig as offline works around the problem acceptably. ++ */ ++ { ++ .ident = "EP45-DQ6", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, ++ "Gigabyte Technology Co., Ltd."), ++ DMI_MATCH(DMI_BOARD_NAME, "EP45-DQ6"), ++ }, ++ .driver_data = ENCODE_BUSDEVFN(0x0a, 0x00, 0), ++ }, ++ { ++ .ident = "EP45-DS5", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, ++ "Gigabyte Technology Co., Ltd."), ++ DMI_MATCH(DMI_BOARD_NAME, "EP45-DS5"), ++ }, ++ .driver_data = ENCODE_BUSDEVFN(0x03, 0x00, 0), ++ }, ++ { } /* terminate list */ ++ }; ++#undef ENCODE_BUSDEVFN ++ const struct dmi_system_id *dmi = dmi_first_match(sysids); ++ unsigned int val; ++ ++ if (!dmi) ++ return false; ++ ++ val = (unsigned long)dmi->driver_data; ++ ++ return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff); ++} ++ ++static bool ahci_broken_devslp(struct pci_dev *pdev) ++{ ++ /* device with broken DEVSLP but still showing SDS capability */ ++ static const struct pci_device_id ids[] = { ++ { PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */ ++ {} ++ }; ++ ++ return pci_match_id(ids, pdev); ++} ++ ++#ifdef CONFIG_ATA_ACPI ++static void ahci_gtf_filter_workaround(struct ata_host *host) ++{ ++ static const struct dmi_system_id sysids[] = { ++ /* ++ * Aspire 3810T issues a bunch of SATA enable commands ++ * via _GTF including an invalid one and one which is ++ * rejected by the device. Among the successful ones ++ * is FPDMA non-zero offset enable which when enabled ++ * only on the drive side leads to NCQ command ++ * failures. Filter it out. ++ */ ++ { ++ .ident = "Aspire 3810T", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3810T"), ++ }, ++ .driver_data = (void *)ATA_ACPI_FILTER_FPDMA_OFFSET, ++ }, ++ { } ++ }; ++ const struct dmi_system_id *dmi = dmi_first_match(sysids); ++ unsigned int filter; ++ int i; ++ ++ if (!dmi) ++ return; ++ ++ filter = (unsigned long)dmi->driver_data; ++ dev_info(host->dev, "applying extra ACPI _GTF filter 0x%x for %s\n", ++ filter, dmi->ident); ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ata_port *ap = host->ports[i]; ++ struct ata_link *link; ++ struct ata_device *dev; ++ ++ ata_for_each_link(link, ap, EDGE) ++ ata_for_each_dev(dev, link, ALL) ++ dev->gtf_filter |= filter; ++ } ++} ++#else ++static inline void ahci_gtf_filter_workaround(struct ata_host *host) ++{} ++#endif ++ ++static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, ++ struct ahci_host_priv *hpriv) ++{ ++ int rc, nvec; ++ ++ if (hpriv->flags & AHCI_HFLAG_NO_MSI) ++ goto intx; ++ ++ rc = pci_msi_vec_count(pdev); ++ if (rc < 0) ++ goto intx; ++ ++ /* ++ * If number of MSIs is less than number of ports then Sharing Last ++ * Message mode could be enforced. In this case assume that advantage ++ * of multipe MSIs is negated and use single MSI mode instead. ++ */ ++ if (rc < n_ports) ++ goto single_msi; ++ ++ nvec = rc; ++ rc = pci_enable_msi_block(pdev, nvec); ++ if (rc < 0) ++ goto intx; ++ else if (rc > 0) ++ goto single_msi; ++ ++ /* fallback to single MSI mode if the controller enforced MRSM mode */ ++ if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) { ++ pci_disable_msi(pdev); ++ printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n"); ++ goto single_msi; ++ } ++ ++ return nvec; ++ ++single_msi: ++ rc = pci_enable_msi(pdev); ++ if (rc) ++ goto intx; ++ return 1; ++ ++intx: ++ pci_intx(pdev, 1); ++ return 0; ++} ++ ++/** ++ * ahci_host_activate - start AHCI host, request IRQs and register it ++ * @host: target ATA host ++ * @irq: base IRQ number to request ++ * @n_msis: number of MSIs allocated for this host ++ * @irq_handler: irq_handler used when requesting IRQs ++ * @irq_flags: irq_flags used when requesting IRQs ++ * ++ * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1 ++ * when multiple MSIs were allocated. That is one MSI per port, starting ++ * from @irq. ++ * ++ * LOCKING: ++ * Inherited from calling layer (may sleep). ++ * ++ * RETURNS: ++ * 0 on success, -errno otherwise. ++ */ ++int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis) ++{ ++ int i, rc; ++ ++ /* Sharing Last Message among several ports is not supported */ ++ if (n_msis < host->n_ports) ++ return -EINVAL; ++ ++ rc = ata_host_start(host); ++ if (rc) ++ return rc; ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ahci_port_priv *pp = host->ports[i]->private_data; ++ ++ /* Do not receive interrupts sent by dummy ports */ ++ if (!pp) { ++ disable_irq(irq + i); ++ continue; ++ } ++ ++ rc = devm_request_threaded_irq(host->dev, irq + i, ++ ahci_hw_interrupt, ++ ahci_thread_fn, IRQF_SHARED, ++ pp->irq_desc, host->ports[i]); ++ if (rc) ++ goto out_free_irqs; ++ } ++ ++ for (i = 0; i < host->n_ports; i++) ++ ata_port_desc(host->ports[i], "irq %d", irq + i); ++ ++ rc = ata_host_register(host, &ahci_sht); ++ if (rc) ++ goto out_free_all_irqs; ++ ++ return 0; ++ ++out_free_all_irqs: ++ i = host->n_ports; ++out_free_irqs: ++ for (i--; i >= 0; i--) ++ devm_free_irq(host->dev, irq + i, host->ports[i]); ++ ++ return rc; ++} ++ ++static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ++{ ++ unsigned int board_id = ent->driver_data; ++ struct ata_port_info pi = ahci_port_info[board_id]; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct device *dev = &pdev->dev; ++ struct ahci_host_priv *hpriv; ++ struct ata_host *host; ++ int n_ports, n_msis, i, rc; ++ int ahci_pci_bar = AHCI_PCI_BAR_STANDARD; ++ ++ VPRINTK("ENTER\n"); ++ ++ WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS); ++ ++ ata_print_version_once(&pdev->dev, DRV_VERSION); ++ ++ /* The AHCI driver can only drive the SATA ports, the PATA driver ++ can drive them all so if both drivers are selected make sure ++ AHCI stays out of the way */ ++ if (pdev->vendor == PCI_VENDOR_ID_MARVELL && !marvell_enable) ++ return -ENODEV; ++ ++ /* Apple BIOS on MCP89 prevents us using AHCI */ ++ if (is_mcp89_apple(pdev)) ++ ahci_mcp89_apple_enable(pdev); ++ ++ /* Promise's PDC42819 is a SAS/SATA controller that has an AHCI mode. ++ * At the moment, we can only use the AHCI mode. Let the users know ++ * that for SAS drives they're out of luck. ++ */ ++ if (pdev->vendor == PCI_VENDOR_ID_PROMISE) ++ dev_info(&pdev->dev, ++ "PDC42819 can only drive SATA devices with this driver\n"); ++ ++ /* Both Connext and Enmotus devices use non-standard BARs */ ++ if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06) ++ ahci_pci_bar = AHCI_PCI_BAR_STA2X11; ++ else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000) ++ ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS; ++ ++ /* acquire resources */ ++ rc = pcim_enable_device(pdev); ++ if (rc) ++ return rc; ++ ++ if (pdev->vendor == PCI_VENDOR_ID_INTEL && ++ (pdev->device == 0x2652 || pdev->device == 0x2653)) { ++ u8 map; ++ ++ /* ICH6s share the same PCI ID for both piix and ahci ++ * modes. Enabling ahci mode while MAP indicates ++ * combined mode is a bad idea. Yield to ata_piix. ++ */ ++ pci_read_config_byte(pdev, ICH_MAP, &map); ++ if (map & 0x3) { ++ dev_info(&pdev->dev, ++ "controller is in combined mode, can't enable AHCI mode\n"); ++ return -ENODEV; ++ } ++ } ++ ++ /* AHCI controllers often implement SFF compatible interface. ++ * Grab all PCI BARs just in case. ++ */ ++ rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME); ++ if (rc == -EBUSY) ++ pcim_pin_device(pdev); ++ if (rc) ++ return rc; ++ ++ hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); ++ if (!hpriv) ++ return -ENOMEM; ++ hpriv->flags |= (unsigned long)pi.private_data; ++ ++ /* MCP65 revision A1 and A2 can't do MSI */ ++ if (board_id == board_ahci_mcp65 && ++ (pdev->revision == 0xa1 || pdev->revision == 0xa2)) ++ hpriv->flags |= AHCI_HFLAG_NO_MSI; ++ ++ /* SB800 does NOT need the workaround to ignore SERR_INTERNAL */ ++ if (board_id == board_ahci_sb700 && pdev->revision >= 0x40) ++ hpriv->flags &= ~AHCI_HFLAG_IGN_SERR_INTERNAL; ++ ++ /* only some SB600s can do 64bit DMA */ ++ if (ahci_sb600_enable_64bit(pdev)) ++ hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY; ++ ++ hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; ++ ++ /* must set flag prior to save config in order to take effect */ ++ if (ahci_broken_devslp(pdev)) ++ hpriv->flags |= AHCI_HFLAG_NO_DEVSLP; ++ ++ /* save initial config */ ++ ahci_pci_save_initial_config(pdev, hpriv); ++ ++ /* prepare host */ ++ if (hpriv->cap & HOST_CAP_NCQ) { ++ pi.flags |= ATA_FLAG_NCQ; ++ /* ++ * Auto-activate optimization is supposed to be ++ * supported on all AHCI controllers indicating NCQ ++ * capability, but it seems to be broken on some ++ * chipsets including NVIDIAs. ++ */ ++ if (!(hpriv->flags & AHCI_HFLAG_NO_FPDMA_AA)) ++ pi.flags |= ATA_FLAG_FPDMA_AA; ++ ++ /* ++ * All AHCI controllers should be forward-compatible ++ * with the new auxiliary field. This code should be ++ * conditionalized if any buggy AHCI controllers are ++ * encountered. ++ */ ++ pi.flags |= ATA_FLAG_FPDMA_AUX; ++ } ++ ++ if (hpriv->cap & HOST_CAP_PMP) ++ pi.flags |= ATA_FLAG_PMP; ++ ++ ahci_set_em_messages(hpriv, &pi); ++ ++ if (ahci_broken_system_poweroff(pdev)) { ++ pi.flags |= ATA_FLAG_NO_POWEROFF_SPINDOWN; ++ dev_info(&pdev->dev, ++ "quirky BIOS, skipping spindown on poweroff\n"); ++ } ++ ++ if (ahci_broken_suspend(pdev)) { ++ hpriv->flags |= AHCI_HFLAG_NO_SUSPEND; ++ dev_warn(&pdev->dev, ++ "BIOS update required for suspend/resume\n"); ++ } ++ ++ if (ahci_broken_online(pdev)) { ++ hpriv->flags |= AHCI_HFLAG_SRST_TOUT_IS_OFFLINE; ++ dev_info(&pdev->dev, ++ "online status unreliable, applying workaround\n"); ++ } ++ ++ /* CAP.NP sometimes indicate the index of the last enabled ++ * port, at other times, that of the last possible port, so ++ * determining the maximum port number requires looking at ++ * both CAP.NP and port_map. ++ */ ++ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); ++ ++ n_msis = ahci_init_interrupts(pdev, n_ports, hpriv); ++ if (n_msis > 1) ++ hpriv->flags |= AHCI_HFLAG_MULTI_MSI; ++ ++ host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); ++ if (!host) ++ return -ENOMEM; ++ host->private_data = hpriv; ++ ++ if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) ++ host->flags |= ATA_HOST_PARALLEL_SCAN; ++ else ++ dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n"); ++ ++ if (pi.flags & ATA_FLAG_EM) ++ ahci_reset_em(host); ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ata_port *ap = host->ports[i]; ++ ++ ata_port_pbar_desc(ap, ahci_pci_bar, -1, "abar"); ++ ata_port_pbar_desc(ap, ahci_pci_bar, ++ 0x100 + ap->port_no * 0x80, "port"); ++ ++ /* set enclosure management message type */ ++ if (ap->flags & ATA_FLAG_EM) ++ ap->em_message_type = hpriv->em_msg_type; ++ ++ ++ /* disabled/not-implemented port */ ++ if (!(hpriv->port_map & (1 << i))) ++ ap->ops = &ata_dummy_port_ops; ++ } ++ ++ /* apply workaround for ASUS P5W DH Deluxe mainboard */ ++ ahci_p5wdh_workaround(host); ++ ++ /* apply gtf filter quirk */ ++ ahci_gtf_filter_workaround(host); ++ ++ /* initialize adapter */ ++ rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); ++ if (rc) ++ return rc; ++ ++ rc = ahci_pci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_pci_init_controller(host); ++ ahci_pci_print_info(host); ++ ++ pci_set_master(pdev); ++ ++ if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) ++ return ahci_host_activate(host, pdev->irq, n_msis); ++ ++ return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_sht); ++} ++ ++module_pci_driver(ahci_pci_driver); ++ ++MODULE_AUTHOR("Jeff Garzik"); ++MODULE_DESCRIPTION("AHCI SATA low-level driver"); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(pci, ahci_pci_tbl); ++MODULE_VERSION(DRV_VERSION); +diff -Nur linux-3.14.36/drivers/ata/ahci.h linux-openelec/drivers/ata/ahci.h +--- linux-3.14.36/drivers/ata/ahci.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/ahci.h 2015-05-06 12:05:42.000000000 -0500 +@@ -37,6 +37,8 @@ + + #include + #include ++#include ++#include + + /* Enclosure Management Control */ + #define EM_CTRL_MSG_TYPE 0x000f0000 +@@ -51,6 +53,7 @@ + + enum { + AHCI_MAX_PORTS = 32, ++ AHCI_MAX_CLKS = 3, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, +@@ -233,6 +236,8 @@ + port start (wait until + error-handling stage) */ + AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */ ++ AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */ ++ AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */ + + /* ap->flags bits */ + +@@ -322,8 +327,17 @@ + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ +- struct clk *clk; /* Only for platforms supporting clk */ ++ bool got_runtime_pm; /* Did we do pm_runtime_get? */ ++ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ ++ struct regulator *target_pwr; /* Optional */ ++ struct phy *phy; /* If platform uses phy */ + void *plat_data; /* Other platform data */ ++ /* ++ * Optional ahci_start_engine override, if not set this gets set to the ++ * default ahci_start_engine during ahci_save_initial_config, this can ++ * be overridden anytime before the host is activated. ++ */ ++ void (*start_engine)(struct ata_port *ap); + }; + + extern int ahci_ignore_sss; +diff -Nur linux-3.14.36/drivers/ata/ahci_imx.c linux-openelec/drivers/ata/ahci_imx.c +--- linux-3.14.36/drivers/ata/ahci_imx.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/ahci_imx.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,12 +26,29 @@ + #include + #include + #include ++#include + #include "ahci.h" + + enum { +- PORT_PHY_CTL = 0x178, /* Port0 PHY Control */ +- PORT_PHY_CTL_PDDQ_LOC = 0x100000, /* PORT_PHY_CTL bits */ +- HOST_TIMER1MS = 0xe0, /* Timer 1-ms */ ++ /* Timer 1-ms Register */ ++ IMX_TIMER1MS = 0x00e0, ++ /* Port0 PHY Control Register */ ++ IMX_P0PHYCR = 0x0178, ++ IMX_P0PHYCR_TEST_PDDQ = 1 << 20, ++ IMX_P0PHYCR_CR_READ = 1 << 19, ++ IMX_P0PHYCR_CR_WRITE = 1 << 18, ++ IMX_P0PHYCR_CR_CAP_DATA = 1 << 17, ++ IMX_P0PHYCR_CR_CAP_ADDR = 1 << 16, ++ /* Port0 PHY Status Register */ ++ IMX_P0PHYSR = 0x017c, ++ IMX_P0PHYSR_CR_ACK = 1 << 18, ++ IMX_P0PHYSR_CR_DATA_OUT = 0xffff << 0, ++ /* Lane0 Output Status Register */ ++ IMX_LANE0_OUT_STAT = 0x2003, ++ IMX_LANE0_OUT_STAT_RX_PLL_STATE = 1 << 1, ++ /* Clock Reset Register */ ++ IMX_CLOCK_RESET = 0x7f3f, ++ IMX_CLOCK_RESET_RESET = 1 << 0, + }; + + enum ahci_imx_type { +@@ -42,62 +59,230 @@ + struct imx_ahci_priv { + struct platform_device *ahci_pdev; + enum ahci_imx_type type; +- +- /* i.MX53 clock */ +- struct clk *sata_gate_clk; +- /* Common clock */ +- struct clk *sata_ref_clk; + struct clk *ahb_clk; +- + struct regmap *gpr; + bool no_device; + bool first_time; ++ u32 phy_params; + }; + + static int ahci_imx_hotplug; + module_param_named(hotplug, ahci_imx_hotplug, int, 0644); + MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); + +-static int imx_sata_clock_enable(struct device *dev) ++static void ahci_imx_host_stop(struct ata_host *host); ++ ++static int imx_phy_crbit_assert(void __iomem *mmio, u32 bit, bool assert) ++{ ++ int timeout = 10; ++ u32 crval; ++ u32 srval; ++ ++ /* Assert or deassert the bit */ ++ crval = readl(mmio + IMX_P0PHYCR); ++ if (assert) ++ crval |= bit; ++ else ++ crval &= ~bit; ++ writel(crval, mmio + IMX_P0PHYCR); ++ ++ /* Wait for the cr_ack signal */ ++ do { ++ srval = readl(mmio + IMX_P0PHYSR); ++ if ((assert ? srval : ~srval) & IMX_P0PHYSR_CR_ACK) ++ break; ++ usleep_range(100, 200); ++ } while (--timeout); ++ ++ return timeout ? 0 : -ETIMEDOUT; ++} ++ ++static int imx_phy_reg_addressing(u16 addr, void __iomem *mmio) + { +- struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); ++ u32 crval = addr; + int ret; + +- if (imxpriv->type == AHCI_IMX53) { +- ret = clk_prepare_enable(imxpriv->sata_gate_clk); +- if (ret < 0) { +- dev_err(dev, "prepare-enable sata_gate clock err:%d\n", +- ret); +- return ret; +- } ++ /* Supply the address on cr_data_in */ ++ writel(crval, mmio + IMX_P0PHYCR); ++ ++ /* Assert the cr_cap_addr signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, true); ++ if (ret) ++ return ret; ++ ++ /* Deassert cr_cap_addr */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, false); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int imx_phy_reg_write(u16 val, void __iomem *mmio) ++{ ++ u32 crval = val; ++ int ret; ++ ++ /* Supply the data on cr_data_in */ ++ writel(crval, mmio + IMX_P0PHYCR); ++ ++ /* Assert the cr_cap_data signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, true); ++ if (ret) ++ return ret; ++ ++ /* Deassert cr_cap_data */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, false); ++ if (ret) ++ return ret; ++ ++ if (val & IMX_CLOCK_RESET_RESET) { ++ /* ++ * In case we're resetting the phy, it's unable to acknowledge, ++ * so we return immediately here. ++ */ ++ crval |= IMX_P0PHYCR_CR_WRITE; ++ writel(crval, mmio + IMX_P0PHYCR); ++ goto out; + } + +- ret = clk_prepare_enable(imxpriv->sata_ref_clk); +- if (ret < 0) { +- dev_err(dev, "prepare-enable sata_ref clock err:%d\n", +- ret); +- goto clk_err; ++ /* Assert the cr_write signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, true); ++ if (ret) ++ return ret; ++ ++ /* Deassert cr_write */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, false); ++ if (ret) ++ return ret; ++ ++out: ++ return 0; ++} ++ ++static int imx_phy_reg_read(u16 *val, void __iomem *mmio) ++{ ++ int ret; ++ ++ /* Assert the cr_read signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, true); ++ if (ret) ++ return ret; ++ ++ /* Capture the data from cr_data_out[] */ ++ *val = readl(mmio + IMX_P0PHYSR) & IMX_P0PHYSR_CR_DATA_OUT; ++ ++ /* Deassert cr_read */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, false); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int imx_sata_phy_reset(struct ahci_host_priv *hpriv) ++{ ++ void __iomem *mmio = hpriv->mmio; ++ int timeout = 10; ++ u16 val; ++ int ret; ++ ++ /* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */ ++ ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio); ++ if (ret) ++ return ret; ++ ret = imx_phy_reg_write(IMX_CLOCK_RESET_RESET, mmio); ++ if (ret) ++ return ret; ++ ++ /* Wait for PHY RX_PLL to be stable */ ++ do { ++ usleep_range(100, 200); ++ ret = imx_phy_reg_addressing(IMX_LANE0_OUT_STAT, mmio); ++ if (ret) ++ return ret; ++ ret = imx_phy_reg_read(&val, mmio); ++ if (ret) ++ return ret; ++ if (val & IMX_LANE0_OUT_STAT_RX_PLL_STATE) ++ break; ++ } while (--timeout); ++ ++ return timeout ? 0 : -ETIMEDOUT; ++} ++ ++static int imx_sata_enable(struct ahci_host_priv *hpriv) ++{ ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; ++ struct device *dev = &imxpriv->ahci_pdev->dev; ++ int ret; ++ ++ if (imxpriv->no_device) ++ return 0; ++ ++ if (hpriv->target_pwr) { ++ ret = regulator_enable(hpriv->target_pwr); ++ if (ret) ++ return ret; + } + ++ request_bus_freq(BUS_FREQ_HIGH); ++ ++ ret = ahci_platform_enable_clks(hpriv); ++ if (ret < 0) ++ goto disable_regulator; ++ + if (imxpriv->type == AHCI_IMX6Q) { ++ /* ++ * set PHY Paremeters, two steps to configure the GPR13, ++ * one write for rest of parameters, mask of first write ++ * is 0x07ffffff, and the other one write for setting ++ * the mpll_clk_en. ++ */ ++ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, ++ IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | ++ IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | ++ IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | ++ IMX6Q_GPR13_SATA_SPD_MODE_MASK | ++ IMX6Q_GPR13_SATA_MPLL_SS_EN | ++ IMX6Q_GPR13_SATA_TX_ATTEN_MASK | ++ IMX6Q_GPR13_SATA_TX_BOOST_MASK | ++ IMX6Q_GPR13_SATA_TX_LVL_MASK | ++ IMX6Q_GPR13_SATA_MPLL_CLK_EN | ++ IMX6Q_GPR13_SATA_TX_EDGE_RATE, ++ imxpriv->phy_params); + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + IMX6Q_GPR13_SATA_MPLL_CLK_EN); ++ ++ usleep_range(100, 200); ++ ++ ret = imx_sata_phy_reset(hpriv); ++ if (ret) { ++ dev_err(dev, "failed to reset phy: %d\n", ret); ++ goto disable_regulator; ++ } + } + + usleep_range(1000, 2000); + + return 0; + +-clk_err: +- if (imxpriv->type == AHCI_IMX53) +- clk_disable_unprepare(imxpriv->sata_gate_clk); ++disable_regulator: ++ release_bus_freq(BUS_FREQ_HIGH); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ + return ret; + } + +-static void imx_sata_clock_disable(struct device *dev) ++static void imx_sata_disable(struct ahci_host_priv *hpriv) + { +- struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; ++ ++ if (imxpriv->no_device) ++ return; + + if (imxpriv->type == AHCI_IMX6Q) { + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, +@@ -105,10 +290,12 @@ + !IMX6Q_GPR13_SATA_MPLL_CLK_EN); + } + +- clk_disable_unprepare(imxpriv->sata_ref_clk); ++ ahci_platform_disable_clks(hpriv); + +- if (imxpriv->type == AHCI_IMX53) +- clk_disable_unprepare(imxpriv->sata_gate_clk); ++ release_bus_freq(BUS_FREQ_HIGH); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); + } + + static void ahci_imx_error_handler(struct ata_port *ap) +@@ -118,7 +305,7 @@ + struct ata_host *host = dev_get_drvdata(ap->dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; +- struct imx_ahci_priv *imxpriv = dev_get_drvdata(ap->dev->parent); ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; + + ahci_error_handler(ap); + +@@ -134,17 +321,23 @@ + * without full reset once the pddq mode is enabled making it + * impossible to use as part of libata LPM. + */ +- reg_val = readl(mmio + PORT_PHY_CTL); +- writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL); +- imx_sata_clock_disable(ap->dev); ++ reg_val = readl(mmio + IMX_P0PHYCR); ++ writel(reg_val | IMX_P0PHYCR_TEST_PDDQ, mmio + IMX_P0PHYCR); ++ imx_sata_disable(hpriv); + imxpriv->no_device = true; ++ ++ dev_info(ap->dev, "no device found, disabling link.\n"); ++ dev_info(ap->dev, "pass " MODULE_PARAM_PREFIX ++ ".hotplug=1 to enable hotplug\n"); + } + + static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) + { + struct ata_port *ap = link->ap; +- struct imx_ahci_priv *imxpriv = dev_get_drvdata(ap->dev->parent); ++ struct ata_host *host = dev_get_drvdata(ap->dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; + int ret = -EIO; + + if (imxpriv->type == AHCI_IMX53) +@@ -156,7 +349,8 @@ + } + + static struct ata_port_operations ahci_imx_ops = { +- .inherits = &ahci_platform_ops, ++ .inherits = &ahci_ops, ++ .host_stop = ahci_imx_host_stop, + .error_handler = ahci_imx_error_handler, + .softreset = ahci_imx_softreset, + }; +@@ -168,234 +362,306 @@ + .port_ops = &ahci_imx_ops, + }; + +-static int imx_sata_init(struct device *dev, void __iomem *mmio) +-{ +- int ret = 0; +- unsigned int reg_val; +- struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); +- +- ret = imx_sata_clock_enable(dev); +- if (ret < 0) +- return ret; ++static const struct of_device_id imx_ahci_of_match[] = { ++ { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, ++ { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, imx_ahci_of_match); + +- /* +- * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, +- * and IP vendor specific register HOST_TIMER1MS. +- * Configure CAP_SSS (support stagered spin up). +- * Implement the port0. +- * Get the ahb clock rate, and configure the TIMER1MS register. +- */ +- reg_val = readl(mmio + HOST_CAP); +- if (!(reg_val & HOST_CAP_SSS)) { +- reg_val |= HOST_CAP_SSS; +- writel(reg_val, mmio + HOST_CAP); +- } +- reg_val = readl(mmio + HOST_PORTS_IMPL); +- if (!(reg_val & 0x1)) { +- reg_val |= 0x1; +- writel(reg_val, mmio + HOST_PORTS_IMPL); +- } ++struct reg_value { ++ u32 of_value; ++ u32 reg_value; ++}; + +- reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; +- writel(reg_val, mmio + HOST_TIMER1MS); ++struct reg_property { ++ const char *name; ++ const struct reg_value *values; ++ size_t num_values; ++ u32 def_value; ++ u32 set_value; ++}; + +- return 0; +-} ++static const struct reg_value gpr13_tx_level[] = { ++ { 937, IMX6Q_GPR13_SATA_TX_LVL_0_937_V }, ++ { 947, IMX6Q_GPR13_SATA_TX_LVL_0_947_V }, ++ { 957, IMX6Q_GPR13_SATA_TX_LVL_0_957_V }, ++ { 966, IMX6Q_GPR13_SATA_TX_LVL_0_966_V }, ++ { 976, IMX6Q_GPR13_SATA_TX_LVL_0_976_V }, ++ { 986, IMX6Q_GPR13_SATA_TX_LVL_0_986_V }, ++ { 996, IMX6Q_GPR13_SATA_TX_LVL_0_996_V }, ++ { 1005, IMX6Q_GPR13_SATA_TX_LVL_1_005_V }, ++ { 1015, IMX6Q_GPR13_SATA_TX_LVL_1_015_V }, ++ { 1025, IMX6Q_GPR13_SATA_TX_LVL_1_025_V }, ++ { 1035, IMX6Q_GPR13_SATA_TX_LVL_1_035_V }, ++ { 1045, IMX6Q_GPR13_SATA_TX_LVL_1_045_V }, ++ { 1054, IMX6Q_GPR13_SATA_TX_LVL_1_054_V }, ++ { 1064, IMX6Q_GPR13_SATA_TX_LVL_1_064_V }, ++ { 1074, IMX6Q_GPR13_SATA_TX_LVL_1_074_V }, ++ { 1084, IMX6Q_GPR13_SATA_TX_LVL_1_084_V }, ++ { 1094, IMX6Q_GPR13_SATA_TX_LVL_1_094_V }, ++ { 1104, IMX6Q_GPR13_SATA_TX_LVL_1_104_V }, ++ { 1113, IMX6Q_GPR13_SATA_TX_LVL_1_113_V }, ++ { 1123, IMX6Q_GPR13_SATA_TX_LVL_1_123_V }, ++ { 1133, IMX6Q_GPR13_SATA_TX_LVL_1_133_V }, ++ { 1143, IMX6Q_GPR13_SATA_TX_LVL_1_143_V }, ++ { 1152, IMX6Q_GPR13_SATA_TX_LVL_1_152_V }, ++ { 1162, IMX6Q_GPR13_SATA_TX_LVL_1_162_V }, ++ { 1172, IMX6Q_GPR13_SATA_TX_LVL_1_172_V }, ++ { 1182, IMX6Q_GPR13_SATA_TX_LVL_1_182_V }, ++ { 1191, IMX6Q_GPR13_SATA_TX_LVL_1_191_V }, ++ { 1201, IMX6Q_GPR13_SATA_TX_LVL_1_201_V }, ++ { 1211, IMX6Q_GPR13_SATA_TX_LVL_1_211_V }, ++ { 1221, IMX6Q_GPR13_SATA_TX_LVL_1_221_V }, ++ { 1230, IMX6Q_GPR13_SATA_TX_LVL_1_230_V }, ++ { 1240, IMX6Q_GPR13_SATA_TX_LVL_1_240_V } ++}; + +-static void imx_sata_exit(struct device *dev) +-{ +- imx_sata_clock_disable(dev); +-} ++static const struct reg_value gpr13_tx_boost[] = { ++ { 0, IMX6Q_GPR13_SATA_TX_BOOST_0_00_DB }, ++ { 370, IMX6Q_GPR13_SATA_TX_BOOST_0_37_DB }, ++ { 740, IMX6Q_GPR13_SATA_TX_BOOST_0_74_DB }, ++ { 1110, IMX6Q_GPR13_SATA_TX_BOOST_1_11_DB }, ++ { 1480, IMX6Q_GPR13_SATA_TX_BOOST_1_48_DB }, ++ { 1850, IMX6Q_GPR13_SATA_TX_BOOST_1_85_DB }, ++ { 2220, IMX6Q_GPR13_SATA_TX_BOOST_2_22_DB }, ++ { 2590, IMX6Q_GPR13_SATA_TX_BOOST_2_59_DB }, ++ { 2960, IMX6Q_GPR13_SATA_TX_BOOST_2_96_DB }, ++ { 3330, IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB }, ++ { 3700, IMX6Q_GPR13_SATA_TX_BOOST_3_70_DB }, ++ { 4070, IMX6Q_GPR13_SATA_TX_BOOST_4_07_DB }, ++ { 4440, IMX6Q_GPR13_SATA_TX_BOOST_4_44_DB }, ++ { 4810, IMX6Q_GPR13_SATA_TX_BOOST_4_81_DB }, ++ { 5280, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB }, ++ { 5750, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB } ++}; + +-static int imx_ahci_suspend(struct device *dev) +-{ +- struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); ++static const struct reg_value gpr13_tx_atten[] = { ++ { 8, IMX6Q_GPR13_SATA_TX_ATTEN_8_16 }, ++ { 9, IMX6Q_GPR13_SATA_TX_ATTEN_9_16 }, ++ { 10, IMX6Q_GPR13_SATA_TX_ATTEN_10_16 }, ++ { 12, IMX6Q_GPR13_SATA_TX_ATTEN_12_16 }, ++ { 14, IMX6Q_GPR13_SATA_TX_ATTEN_14_16 }, ++ { 16, IMX6Q_GPR13_SATA_TX_ATTEN_16_16 }, ++}; + +- /* +- * If no_device is set, The CLKs had been gated off in the +- * initialization so don't do it again here. +- */ +- if (!imxpriv->no_device) +- imx_sata_clock_disable(dev); ++static const struct reg_value gpr13_rx_eq[] = { ++ { 500, IMX6Q_GPR13_SATA_RX_EQ_VAL_0_5_DB }, ++ { 1000, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_0_DB }, ++ { 1500, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_5_DB }, ++ { 2000, IMX6Q_GPR13_SATA_RX_EQ_VAL_2_0_DB }, ++ { 2500, IMX6Q_GPR13_SATA_RX_EQ_VAL_2_5_DB }, ++ { 3000, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB }, ++ { 3500, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_5_DB }, ++ { 4000, IMX6Q_GPR13_SATA_RX_EQ_VAL_4_0_DB }, ++}; + +- return 0; +-} ++static const struct reg_property gpr13_props[] = { ++ { ++ .name = "fsl,transmit-level-mV", ++ .values = gpr13_tx_level, ++ .num_values = ARRAY_SIZE(gpr13_tx_level), ++ .def_value = IMX6Q_GPR13_SATA_TX_LVL_1_025_V, ++ }, { ++ .name = "fsl,transmit-boost-mdB", ++ .values = gpr13_tx_boost, ++ .num_values = ARRAY_SIZE(gpr13_tx_boost), ++ .def_value = IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB, ++ }, { ++ .name = "fsl,transmit-atten-16ths", ++ .values = gpr13_tx_atten, ++ .num_values = ARRAY_SIZE(gpr13_tx_atten), ++ .def_value = IMX6Q_GPR13_SATA_TX_ATTEN_9_16, ++ }, { ++ .name = "fsl,receive-eq-mdB", ++ .values = gpr13_rx_eq, ++ .num_values = ARRAY_SIZE(gpr13_rx_eq), ++ .def_value = IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB, ++ }, { ++ .name = "fsl,no-spread-spectrum", ++ .def_value = IMX6Q_GPR13_SATA_MPLL_SS_EN, ++ .set_value = 0, ++ }, ++}; + +-static int imx_ahci_resume(struct device *dev) ++static u32 imx_ahci_parse_props(struct device *dev, ++ const struct reg_property *prop, size_t num) + { +- struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); +- int ret = 0; +- +- if (!imxpriv->no_device) +- ret = imx_sata_clock_enable(dev); ++ struct device_node *np = dev->of_node; ++ u32 reg_value = 0; ++ int i, j; ++ ++ for (i = 0; i < num; i++, prop++) { ++ u32 of_val; ++ ++ if (prop->num_values == 0) { ++ if (of_property_read_bool(np, prop->name)) ++ reg_value |= prop->set_value; ++ else ++ reg_value |= prop->def_value; ++ continue; ++ } + +- return ret; +-} ++ if (of_property_read_u32(np, prop->name, &of_val)) { ++ dev_info(dev, "%s not specified, using %08x\n", ++ prop->name, prop->def_value); ++ reg_value |= prop->def_value; ++ continue; ++ } + +-static struct ahci_platform_data imx_sata_pdata = { +- .init = imx_sata_init, +- .exit = imx_sata_exit, +- .ata_port_info = &ahci_imx_port_info, +- .suspend = imx_ahci_suspend, +- .resume = imx_ahci_resume, ++ for (j = 0; j < prop->num_values; j++) { ++ if (prop->values[j].of_value == of_val) { ++ dev_info(dev, "%s value %u, using %08x\n", ++ prop->name, of_val, prop->values[j].reg_value); ++ reg_value |= prop->values[j].reg_value; ++ break; ++ } ++ } + +-}; ++ if (j == prop->num_values) { ++ dev_err(dev, "DT property %s is not a valid value\n", ++ prop->name); ++ reg_value |= prop->def_value; ++ } ++ } + +-static const struct of_device_id imx_ahci_of_match[] = { +- { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, +- { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, imx_ahci_of_match); ++ return reg_value; ++} + + static int imx_ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +- struct resource *mem, *irq, res[2]; + const struct of_device_id *of_id; +- enum ahci_imx_type type; +- const struct ahci_platform_data *pdata = NULL; ++ struct ahci_host_priv *hpriv; + struct imx_ahci_priv *imxpriv; +- struct device *ahci_dev; +- struct platform_device *ahci_pdev; ++ unsigned int reg_val; + int ret; + + of_id = of_match_device(imx_ahci_of_match, dev); + if (!of_id) + return -EINVAL; + +- type = (enum ahci_imx_type)of_id->data; +- pdata = &imx_sata_pdata; +- + imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); +- if (!imxpriv) { +- dev_err(dev, "can't alloc ahci_host_priv\n"); ++ if (!imxpriv) + return -ENOMEM; +- } +- +- ahci_pdev = platform_device_alloc("ahci", -1); +- if (!ahci_pdev) +- return -ENODEV; +- +- ahci_dev = &ahci_pdev->dev; +- ahci_dev->parent = dev; + ++ imxpriv->ahci_pdev = pdev; + imxpriv->no_device = false; + imxpriv->first_time = true; +- imxpriv->type = type; +- ++ imxpriv->type = (enum ahci_imx_type)of_id->data; + imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(imxpriv->ahb_clk)) { + dev_err(dev, "can't get ahb clock.\n"); +- ret = PTR_ERR(imxpriv->ahb_clk); +- goto err_out; +- } +- +- if (type == AHCI_IMX53) { +- imxpriv->sata_gate_clk = devm_clk_get(dev, "sata_gate"); +- if (IS_ERR(imxpriv->sata_gate_clk)) { +- dev_err(dev, "can't get sata_gate clock.\n"); +- ret = PTR_ERR(imxpriv->sata_gate_clk); +- goto err_out; +- } +- } +- +- imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); +- if (IS_ERR(imxpriv->sata_ref_clk)) { +- dev_err(dev, "can't get sata_ref clock.\n"); +- ret = PTR_ERR(imxpriv->sata_ref_clk); +- goto err_out; ++ return PTR_ERR(imxpriv->ahb_clk); + } + +- imxpriv->ahci_pdev = ahci_pdev; +- platform_set_drvdata(pdev, imxpriv); +- +- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +- if (!mem || !irq) { +- dev_err(dev, "no mmio/irq resource\n"); +- ret = -ENOMEM; +- goto err_out; +- } +- +- res[0] = *mem; +- res[1] = *irq; +- +- ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32); +- ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask; +- ahci_dev->of_node = dev->of_node; ++ if (imxpriv->type == AHCI_IMX6Q) { ++ u32 reg_value; + +- if (type == AHCI_IMX6Q) { + imxpriv->gpr = syscon_regmap_lookup_by_compatible( + "fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imxpriv->gpr)) { + dev_err(dev, + "failed to find fsl,imx6q-iomux-gpr regmap\n"); +- ret = PTR_ERR(imxpriv->gpr); +- goto err_out; ++ return PTR_ERR(imxpriv->gpr); + } + +- /* +- * Set PHY Paremeters, two steps to configure the GPR13, +- * one write for rest of parameters, mask of first write +- * is 0x07fffffe, and the other one write for setting +- * the mpll_clk_en happens in imx_sata_clock_enable(). +- */ +- regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, +- IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | +- IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | +- IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | +- IMX6Q_GPR13_SATA_SPD_MODE_MASK | +- IMX6Q_GPR13_SATA_MPLL_SS_EN | +- IMX6Q_GPR13_SATA_TX_ATTEN_MASK | +- IMX6Q_GPR13_SATA_TX_BOOST_MASK | +- IMX6Q_GPR13_SATA_TX_LVL_MASK | +- IMX6Q_GPR13_SATA_MPLL_CLK_EN | +- IMX6Q_GPR13_SATA_TX_EDGE_RATE, +- IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB | ++ reg_value = imx_ahci_parse_props(dev, gpr13_props, ++ ARRAY_SIZE(gpr13_props)); ++ ++ imxpriv->phy_params = + IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M | + IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F | + IMX6Q_GPR13_SATA_SPD_MODE_3P0G | +- IMX6Q_GPR13_SATA_MPLL_SS_EN | +- IMX6Q_GPR13_SATA_TX_ATTEN_9_16 | +- IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB | +- IMX6Q_GPR13_SATA_TX_LVL_1_025_V); ++ reg_value; + } + +- ret = platform_device_add_resources(ahci_pdev, res, 2); ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ hpriv->plat_data = imxpriv; ++ ++ ret = imx_sata_enable(hpriv); + if (ret) +- goto err_out; ++ return ret; + +- ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata)); ++ /* ++ * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, ++ * and IP vendor specific register IMX_TIMER1MS. ++ * Configure CAP_SSS (support stagered spin up). ++ * Implement the port0. ++ * Get the ahb clock rate, and configure the TIMER1MS register. ++ */ ++ reg_val = readl(hpriv->mmio + HOST_CAP); ++ if (!(reg_val & HOST_CAP_SSS)) { ++ reg_val |= HOST_CAP_SSS; ++ writel(reg_val, hpriv->mmio + HOST_CAP); ++ } ++ reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL); ++ if (!(reg_val & 0x1)) { ++ reg_val |= 0x1; ++ writel(reg_val, hpriv->mmio + HOST_PORTS_IMPL); ++ } ++ ++ reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; ++ writel(reg_val, hpriv->mmio + IMX_TIMER1MS); ++ ++ ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, ++ 0, 0, 0); + if (ret) +- goto err_out; ++ imx_sata_disable(hpriv); ++ ++ return ret; ++} + +- ret = platform_device_add(ahci_pdev); +- if (ret) { +-err_out: +- platform_device_put(ahci_pdev); ++static void ahci_imx_host_stop(struct ata_host *host) ++{ ++ struct ahci_host_priv *hpriv = host->private_data; ++ ++ imx_sata_disable(hpriv); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int imx_ahci_suspend(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int ret; ++ ++ ret = ahci_platform_suspend_host(dev); ++ if (ret) + return ret; +- } ++ ++ imx_sata_disable(hpriv); + + return 0; + } + +-static int imx_ahci_remove(struct platform_device *pdev) ++static int imx_ahci_resume(struct device *dev) + { +- struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev); +- struct platform_device *ahci_pdev = imxpriv->ahci_pdev; ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int ret; + +- platform_device_unregister(ahci_pdev); +- return 0; ++ ret = imx_sata_enable(hpriv); ++ if (ret) ++ return ret; ++ ++ return ahci_platform_resume_host(dev); + } ++#endif ++ ++static SIMPLE_DEV_PM_OPS(ahci_imx_pm_ops, imx_ahci_suspend, imx_ahci_resume); + + static struct platform_driver imx_ahci_driver = { + .probe = imx_ahci_probe, +- .remove = imx_ahci_remove, ++ .remove = ata_platform_remove_one, + .driver = { + .name = "ahci-imx", + .owner = THIS_MODULE, + .of_match_table = imx_ahci_of_match, ++ .pm = &ahci_imx_pm_ops, + }, + }; + module_platform_driver(imx_ahci_driver); +diff -Nur linux-3.14.36/drivers/ata/ahci_platform.c linux-openelec/drivers/ata/ahci_platform.c +--- linux-3.14.36/drivers/ata/ahci_platform.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/ahci_platform.c 2015-05-06 12:05:42.000000000 -0500 +@@ -12,135 +12,36 @@ + * any later version. + */ + +-#include + #include +-#include + #include + #include +-#include +-#include + #include + #include + #include + #include + #include "ahci.h" + +-static void ahci_host_stop(struct ata_host *host); +- +-enum ahci_type { +- AHCI, /* standard platform ahci */ +- IMX53_AHCI, /* ahci on i.mx53 */ +- STRICT_AHCI, /* delayed DMA engine start */ +-}; +- +-static struct platform_device_id ahci_devtype[] = { +- { +- .name = "ahci", +- .driver_data = AHCI, +- }, { +- .name = "imx53-ahci", +- .driver_data = IMX53_AHCI, +- }, { +- .name = "strict-ahci", +- .driver_data = STRICT_AHCI, +- }, { +- /* sentinel */ +- } +-}; +-MODULE_DEVICE_TABLE(platform, ahci_devtype); +- +-struct ata_port_operations ahci_platform_ops = { +- .inherits = &ahci_ops, +- .host_stop = ahci_host_stop, +-}; +-EXPORT_SYMBOL_GPL(ahci_platform_ops); +- +-static struct ata_port_operations ahci_platform_retry_srst_ops = { +- .inherits = &ahci_pmp_retry_srst_ops, +- .host_stop = ahci_host_stop, +-}; +- +-static const struct ata_port_info ahci_port_info[] = { +- /* by features */ +- [AHCI] = { +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_ops, +- }, +- [IMX53_AHCI] = { +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_retry_srst_ops, +- }, +- [STRICT_AHCI] = { +- AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_platform_ops, +- }, +-}; +- +-static struct scsi_host_template ahci_platform_sht = { +- AHCI_SHT("ahci_platform"), ++static const struct ata_port_info ahci_port_info = { ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_platform_ops, + }; + + static int ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct platform_device_id *id = platform_get_device_id(pdev); +- struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; +- const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; +- struct ata_host *host; +- struct resource *mem; +- int irq; +- int n_ports; +- int i; + int rc; + +- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!mem) { +- dev_err(dev, "no mmio space\n"); +- return -EINVAL; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq <= 0) { +- dev_err(dev, "no irq\n"); +- return -EINVAL; +- } +- +- if (pdata && pdata->ata_port_info) +- pi = *pdata->ata_port_info; +- +- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); +- if (!hpriv) { +- dev_err(dev, "can't alloc ahci_host_priv\n"); +- return -ENOMEM; +- } +- +- hpriv->flags |= (unsigned long)pi.private_data; ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); + +- hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); +- if (!hpriv->mmio) { +- dev_err(dev, "can't map %pR\n", mem); +- return -ENOMEM; +- } +- +- hpriv->clk = clk_get(dev, NULL); +- if (IS_ERR(hpriv->clk)) { +- dev_err(dev, "can't get clock\n"); +- } else { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- goto free_clk; +- } +- } ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; + + /* + * Some platforms might need to prepare for mmio region access, +@@ -151,69 +52,10 @@ + if (pdata && pdata->init) { + rc = pdata->init(dev, hpriv->mmio); + if (rc) +- goto disable_unprepare_clk; +- } +- +- ahci_save_initial_config(dev, hpriv, +- pdata ? pdata->force_port_map : 0, +- pdata ? pdata->mask_port_map : 0); +- +- /* prepare host */ +- if (hpriv->cap & HOST_CAP_NCQ) +- pi.flags |= ATA_FLAG_NCQ; +- +- if (hpriv->cap & HOST_CAP_PMP) +- pi.flags |= ATA_FLAG_PMP; +- +- ahci_set_em_messages(hpriv, &pi); +- +- /* CAP.NP sometimes indicate the index of the last enabled +- * port, at other times, that of the last possible port, so +- * determining the maximum port number requires looking at +- * both CAP.NP and port_map. +- */ +- n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); +- +- host = ata_host_alloc_pinfo(dev, ppi, n_ports); +- if (!host) { +- rc = -ENOMEM; +- goto pdata_exit; ++ goto disable_resources; + } + +- host->private_data = hpriv; +- +- if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) +- host->flags |= ATA_HOST_PARALLEL_SCAN; +- else +- dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); +- +- if (pi.flags & ATA_FLAG_EM) +- ahci_reset_em(host); +- +- for (i = 0; i < host->n_ports; i++) { +- struct ata_port *ap = host->ports[i]; +- +- ata_port_desc(ap, "mmio %pR", mem); +- ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); +- +- /* set enclosure management message type */ +- if (ap->flags & ATA_FLAG_EM) +- ap->em_message_type = hpriv->em_msg_type; +- +- /* disabled/not-implemented port */ +- if (!(hpriv->port_map & (1 << i))) +- ap->ops = &ata_dummy_port_ops; +- } +- +- rc = ahci_reset_controller(host); +- if (rc) +- goto pdata_exit; +- +- ahci_init_controller(host); +- ahci_print_info(host, "platform"); +- +- rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, +- &ahci_platform_sht); ++ rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0, 0); + if (rc) + goto pdata_exit; + +@@ -221,115 +63,19 @@ + pdata_exit: + if (pdata && pdata->exit) + pdata->exit(dev); +-disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); +-free_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_put(hpriv->clk); +- return rc; +-} +- +-static void ahci_host_stop(struct ata_host *host) +-{ +- struct device *dev = host->dev; +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- +- if (pdata && pdata->exit) +- pdata->exit(dev); +- +- if (!IS_ERR(hpriv->clk)) { +- clk_disable_unprepare(hpriv->clk); +- clk_put(hpriv->clk); +- } +-} +- +-#ifdef CONFIG_PM_SLEEP +-static int ahci_suspend(struct device *dev) +-{ +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- void __iomem *mmio = hpriv->mmio; +- u32 ctl; +- int rc; +- +- if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { +- dev_err(dev, "firmware update required for suspend/resume\n"); +- return -EIO; +- } +- +- /* +- * AHCI spec rev1.1 section 8.3.3: +- * Software must disable interrupts prior to requesting a +- * transition of the HBA to D3 state. +- */ +- ctl = readl(mmio + HOST_CTL); +- ctl &= ~HOST_IRQ_EN; +- writel(ctl, mmio + HOST_CTL); +- readl(mmio + HOST_CTL); /* flush */ +- +- rc = ata_host_suspend(host, PMSG_SUSPEND); +- if (rc) +- return rc; +- +- if (pdata && pdata->suspend) +- return pdata->suspend(dev); +- +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); +- +- return 0; +-} +- +-static int ahci_resume(struct device *dev) +-{ +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- struct ata_host *host = dev_get_drvdata(dev); +- struct ahci_host_priv *hpriv = host->private_data; +- int rc; +- +- if (!IS_ERR(hpriv->clk)) { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- return rc; +- } +- } +- +- if (pdata && pdata->resume) { +- rc = pdata->resume(dev); +- if (rc) +- goto disable_unprepare_clk; +- } +- +- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { +- rc = ahci_reset_controller(host); +- if (rc) +- goto disable_unprepare_clk; +- +- ahci_init_controller(host); +- } +- +- ata_host_resume(host); +- +- return 0; +- +-disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); +- ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + return rc; + } +-#endif + +-static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); ++static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, ++ ahci_platform_resume); + + static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, + { .compatible = "snps,exynos5440-ahci", }, + { .compatible = "ibm,476gtr-ahci", }, ++ { .compatible = "snps,dwc-ahci", }, + {}, + }; + MODULE_DEVICE_TABLE(of, ahci_of_match); +@@ -343,7 +89,6 @@ + .of_match_table = ahci_of_match, + .pm = &ahci_pm_ops, + }, +- .id_table = ahci_devtype, + }; + module_platform_driver(ahci_driver); + +diff -Nur linux-3.14.36/drivers/ata/ata_generic.c linux-openelec/drivers/ata/ata_generic.c +--- linux-3.14.36/drivers/ata/ata_generic.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/ata_generic.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/Kconfig linux-openelec/drivers/ata/Kconfig +--- linux-3.14.36/drivers/ata/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -99,7 +99,7 @@ + + config AHCI_IMX + tristate "Freescale i.MX AHCI SATA support" +- depends on SATA_AHCI_PLATFORM && MFD_SYSCON ++ depends on MFD_SYSCON + help + This option enables support for the Freescale i.MX SoC's + onboard AHCI SATA. +diff -Nur linux-3.14.36/drivers/ata/libahci.c linux-openelec/drivers/ata/libahci.c +--- linux-3.14.36/drivers/ata/libahci.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/libahci.c 2015-05-06 12:05:42.000000000 -0500 +@@ -35,7 +35,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -394,6 +393,9 @@ + * + * If inconsistent, config values are fixed up by this function. + * ++ * If it is not set already this function sets hpriv->start_engine to ++ * ahci_start_engine. ++ * + * LOCKING: + * None. + */ +@@ -450,11 +452,23 @@ + cap &= ~HOST_CAP_SNTF; + } + ++ if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) { ++ dev_info(dev, ++ "controller can't do DEVSLP, turning off\n"); ++ cap2 &= ~HOST_CAP2_SDS; ++ cap2 &= ~HOST_CAP2_SADM; ++ } ++ + if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) { + dev_info(dev, "controller can do FBS, turning on CAP_FBS\n"); + cap |= HOST_CAP_FBS; + } + ++ if ((cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_NO_FBS)) { ++ dev_info(dev, "controller can't do FBS, turning off CAP_FBS\n"); ++ cap &= ~HOST_CAP_FBS; ++ } ++ + if (force_port_map && port_map != force_port_map) { + dev_info(dev, "forcing port_map 0x%x -> 0x%x\n", + port_map, force_port_map); +@@ -500,6 +514,9 @@ + hpriv->cap = cap; + hpriv->cap2 = cap2; + hpriv->port_map = port_map; ++ ++ if (!hpriv->start_engine) ++ hpriv->start_engine = ahci_start_engine; + } + EXPORT_SYMBOL_GPL(ahci_save_initial_config); + +@@ -766,7 +783,7 @@ + + /* enable DMA */ + if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* turn on LEDs */ + if (ap->flags & ATA_FLAG_EM) { +@@ -1234,7 +1251,7 @@ + + /* restart engine */ + out_restart: +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + return rc; + } + EXPORT_SYMBOL_GPL(ahci_kick_engine); +@@ -1426,6 +1443,7 @@ + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -1443,7 +1461,7 @@ + rc = sata_link_hardreset(link, timing, deadline, &online, + ahci_check_ready); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +@@ -2007,10 +2025,12 @@ + + void ahci_error_handler(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { + /* restart engine */ + ahci_stop_engine(ap); +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + sata_pmp_error_handler(ap); +@@ -2031,6 +2051,7 @@ + + static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_device *dev = ap->link.device; + u32 devslp, dm, dito, mdat, deto; +@@ -2094,7 +2115,7 @@ + PORT_DEVSLP_ADSE); + writel(devslp, port_mmio + PORT_DEVSLP); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* enable device sleep feature for the drive */ + err_mask = ata_dev_set_feature(dev, +@@ -2106,6 +2127,7 @@ + + static void ahci_enable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2134,11 +2156,12 @@ + } else + dev_err(ap->host->dev, "Failed to enable FBS\n"); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_disable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2166,7 +2189,7 @@ + pp->fbs_enabled = false; + } + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_pmp_attach(struct ata_port *ap) +diff -Nur linux-3.14.36/drivers/ata/libahci_platform.c linux-openelec/drivers/ata/libahci_platform.c +--- linux-3.14.36/drivers/ata/libahci_platform.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/ata/libahci_platform.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,544 @@ ++/* ++ * AHCI SATA platform library ++ * ++ * Copyright 2004-2005 Red Hat, Inc. ++ * Jeff Garzik ++ * Copyright 2010 MontaVista Software, LLC. ++ * Anton Vorontsov ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ahci.h" ++ ++static void ahci_host_stop(struct ata_host *host); ++ ++struct ata_port_operations ahci_platform_ops = { ++ .inherits = &ahci_ops, ++ .host_stop = ahci_host_stop, ++}; ++EXPORT_SYMBOL_GPL(ahci_platform_ops); ++ ++static struct scsi_host_template ahci_platform_sht = { ++ AHCI_SHT("ahci_platform"), ++}; ++ ++/** ++ * ahci_platform_enable_clks - Enable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all the clks found in hpriv->clks, starting at ++ * index 0. If any clk fails to enable it disables all the clks already ++ * enabled in reverse order, and then returns an error. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c, rc; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { ++ rc = clk_prepare_enable(hpriv->clks[c]); ++ if (rc) ++ goto disable_unprepare_clk; ++ } ++ return 0; ++ ++disable_unprepare_clk: ++ while (--c >= 0) ++ clk_disable_unprepare(hpriv->clks[c]); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); ++ ++/** ++ * ahci_platform_disable_clks - Disable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all the clks found in hpriv->clks, in reverse ++ * order of ahci_platform_enable_clks (starting at the end of the array). ++ */ ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) ++ if (hpriv->clks[c]) ++ clk_disable_unprepare(hpriv->clks[c]); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); ++ ++/** ++ * ahci_platform_enable_resources - Enable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all ahci_platform managed resources in the ++ * following order: ++ * 1) Regulator ++ * 2) Clocks (through ahci_platform_enable_clks) ++ * 3) Phy ++ * ++ * If resource enabling fails at any point the previous enabled resources ++ * are disabled in reverse order. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) ++{ ++ int rc; ++ ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; ++ } ++ ++ rc = ahci_platform_enable_clks(hpriv); ++ if (rc) ++ goto disable_regulator; ++ ++ if (hpriv->phy) { ++ rc = phy_init(hpriv->phy); ++ if (rc) ++ goto disable_clks; ++ ++ rc = phy_power_on(hpriv->phy); ++ if (rc) { ++ phy_exit(hpriv->phy); ++ goto disable_clks; ++ } ++ } ++ ++ return 0; ++ ++disable_clks: ++ ahci_platform_disable_clks(hpriv); ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); ++ ++/** ++ * ahci_platform_disable_resources - Disable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all ahci_platform managed resources in the ++ * following order: ++ * 1) Phy ++ * 2) Clocks (through ahci_platform_disable_clks) ++ * 3) Regulator ++ */ ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ++{ ++ if (hpriv->phy) { ++ phy_power_off(hpriv->phy); ++ phy_exit(hpriv->phy); ++ } ++ ++ ahci_platform_disable_clks(hpriv); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); ++ ++static void ahci_platform_put_resources(struct device *dev, void *res) ++{ ++ struct ahci_host_priv *hpriv = res; ++ int c; ++ ++ if (hpriv->got_runtime_pm) { ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ } ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) ++ clk_put(hpriv->clks[c]); ++} ++ ++/** ++ * ahci_platform_get_resources - Get platform resources ++ * @pdev: platform device to get resources for ++ * ++ * This function allocates an ahci_host_priv struct, and gets the following ++ * resources, storing a reference to them inside the returned struct: ++ * ++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory) ++ * 2) regulator for controlling the targets power (optional) ++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, ++ * or for non devicetree enabled platforms a single clock ++ * 4) phy (optional) ++ * ++ * RETURNS: ++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ++ */ ++struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_host_priv *hpriv; ++ struct clk *clk; ++ int i, rc = -ENOMEM; ++ ++ if (!devres_open_group(dev, NULL, GFP_KERNEL)) ++ return ERR_PTR(-ENOMEM); ++ ++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), ++ GFP_KERNEL); ++ if (!hpriv) ++ goto err_out; ++ ++ devres_add(dev, hpriv); ++ ++ hpriv->mmio = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ if (IS_ERR(hpriv->mmio)) { ++ dev_err(dev, "no mmio space\n"); ++ rc = PTR_ERR(hpriv->mmio); ++ goto err_out; ++ } ++ ++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); ++ if (IS_ERR(hpriv->target_pwr)) { ++ rc = PTR_ERR(hpriv->target_pwr); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ hpriv->target_pwr = NULL; ++ } ++ ++ for (i = 0; i < AHCI_MAX_CLKS; i++) { ++ /* ++ * For now we must use clk_get(dev, NULL) for the first clock, ++ * because some platforms (da850, spear13xx) are not yet ++ * converted to use devicetree for clocks. For new platforms ++ * this is equivalent to of_clk_get(dev->of_node, 0). ++ */ ++ if (i == 0) ++ clk = clk_get(dev, NULL); ++ else ++ clk = of_clk_get(dev->of_node, i); ++ ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ break; ++ } ++ hpriv->clks[i] = clk; ++ } ++ ++ hpriv->phy = devm_phy_get(dev, "sata-phy"); ++ if (IS_ERR(hpriv->phy)) { ++ rc = PTR_ERR(hpriv->phy); ++ switch (rc) { ++ case -ENODEV: ++ case -ENOSYS: ++ /* continue normally */ ++ hpriv->phy = NULL; ++ break; ++ ++ case -EPROBE_DEFER: ++ goto err_out; ++ ++ default: ++ dev_err(dev, "couldn't get sata-phy\n"); ++ goto err_out; ++ } ++ } ++ ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ hpriv->got_runtime_pm = true; ++ ++ devres_remove_group(dev, NULL); ++ return hpriv; ++ ++err_out: ++ devres_release_group(dev, NULL); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_get_resources); ++ ++/** ++ * ahci_platform_init_host - Bring up an ahci-platform host ++ * @pdev: platform device pointer for the host ++ * @hpriv: ahci-host private data for the host ++ * @pi_template: template for the ata_port_info to use ++ * @host_flags: ahci host flags used in ahci_host_priv ++ * @force_port_map: param passed to ahci_save_initial_config ++ * @mask_port_map: param passed to ahci_save_initial_config ++ * ++ * This function does all the usual steps needed to bring up an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned long host_flags, ++ unsigned int force_port_map, ++ unsigned int mask_port_map) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_port_info pi = *pi_template; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct ata_host *host; ++ int i, irq, n_ports, rc; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "no irq\n"); ++ return -EINVAL; ++ } ++ ++ /* prepare host */ ++ pi.private_data = (void *)host_flags; ++ hpriv->flags |= host_flags; ++ ++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); ++ ++ if (hpriv->cap & HOST_CAP_NCQ) ++ pi.flags |= ATA_FLAG_NCQ; ++ ++ if (hpriv->cap & HOST_CAP_PMP) ++ pi.flags |= ATA_FLAG_PMP; ++ ++ ahci_set_em_messages(hpriv, &pi); ++ ++ /* CAP.NP sometimes indicate the index of the last enabled ++ * port, at other times, that of the last possible port, so ++ * determining the maximum port number requires looking at ++ * both CAP.NP and port_map. ++ */ ++ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); ++ ++ host = ata_host_alloc_pinfo(dev, ppi, n_ports); ++ if (!host) ++ return -ENOMEM; ++ ++ host->private_data = hpriv; ++ ++ if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) ++ host->flags |= ATA_HOST_PARALLEL_SCAN; ++ else ++ dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); ++ ++ if (pi.flags & ATA_FLAG_EM) ++ ahci_reset_em(host); ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ata_port *ap = host->ports[i]; ++ ++ ata_port_desc(ap, "mmio %pR", ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); ++ ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); ++ ++ /* set enclosure management message type */ ++ if (ap->flags & ATA_FLAG_EM) ++ ap->em_message_type = hpriv->em_msg_type; ++ ++ /* disabled/not-implemented port */ ++ if (!(hpriv->port_map & (1 << i))) ++ ap->ops = &ata_dummy_port_ops; ++ } ++ ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ ahci_print_info(host, "platform"); ++ ++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_platform_sht); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_init_host); ++ ++static void ahci_host_stop(struct ata_host *host) ++{ ++ struct device *dev = host->dev; ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ ++ if (pdata && pdata->exit) ++ pdata->exit(dev); ++ ++ ahci_platform_disable_resources(hpriv); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * ahci_platform_suspend_host - Suspend an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to suspend an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be disabled after calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ void __iomem *mmio = hpriv->mmio; ++ u32 ctl; ++ ++ if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { ++ dev_err(dev, "firmware update required for suspend/resume\n"); ++ return -EIO; ++ } ++ ++ /* ++ * AHCI spec rev1.1 section 8.3.3: ++ * Software must disable interrupts prior to requesting a ++ * transition of the HBA to D3 state. ++ */ ++ ctl = readl(mmio + HOST_CTL); ++ ctl &= ~HOST_IRQ_EN; ++ writel(ctl, mmio + HOST_CTL); ++ readl(mmio + HOST_CTL); /* flush */ ++ ++ return ata_host_suspend(host, PMSG_SUSPEND); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); ++ ++/** ++ * ahci_platform_resume_host - Resume an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to resume an ahci-platform ++ * host, note any necessary resources (ie clks, phy, etc.) must be ++ * initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ int rc; ++ ++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume_host); ++ ++/** ++ * ahci_platform_suspend - Suspend an ahci-platform device ++ * @dev: the platform device to suspend ++ * ++ * This function suspends the host associated with the device, followed by ++ * disabling all the resources of the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_suspend_host(dev); ++ if (rc) ++ return rc; ++ ++ if (pdata && pdata->suspend) { ++ rc = pdata->suspend(dev); ++ if (rc) ++ goto resume_host; ++ } ++ ++ ahci_platform_disable_resources(hpriv); ++ ++ return 0; ++ ++resume_host: ++ ahci_platform_resume_host(dev); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend); ++ ++/** ++ * ahci_platform_resume - Resume an ahci-platform device ++ * @dev: the platform device to resume ++ * ++ * This function enables all the resources of the device followed by ++ * resuming the host associated with the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ if (pdata && pdata->resume) { ++ rc = pdata->resume(dev); ++ if (rc) ++ goto disable_resources; ++ } ++ ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; ++ ++ /* We resumed so update PM runtime state */ ++ pm_runtime_disable(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ return 0; ++ ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume); ++#endif ++ ++MODULE_DESCRIPTION("AHCI SATA platform library"); ++MODULE_AUTHOR("Anton Vorontsov "); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/ata/libata-core.c linux-openelec/drivers/ata/libata-core.c +--- linux-3.14.36/drivers/ata/libata-core.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/libata-core.c 2015-07-24 18:03:28.456842002 -0500 +@@ -1524,7 +1524,7 @@ + * @dev: Device to which the command is sent + * @tf: Taskfile registers for the command and the result + * @cdb: CDB for packet command +- * @dma_dir: Data tranfer direction of the command ++ * @dma_dir: Data transfer direction of the command + * @sgl: sg list for the data buffer of the command + * @n_elem: Number of sg entries + * @timeout: Timeout in msecs (0 for default) +@@ -1712,7 +1712,7 @@ + * @dev: Device to which the command is sent + * @tf: Taskfile registers for the command and the result + * @cdb: CDB for packet command +- * @dma_dir: Data tranfer direction of the command ++ * @dma_dir: Data transfer direction of the command + * @buf: Data buffer of the command + * @buflen: Length of data buffer + * @timeout: Timeout in msecs (0 for default) +diff -Nur linux-3.14.36/drivers/ata/Makefile linux-openelec/drivers/ata/Makefile +--- linux-3.14.36/drivers/ata/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -4,13 +4,13 @@ + # non-SFF interface + obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o + obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o +-obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o ++obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o + obj-$(CONFIG_SATA_FSL) += sata_fsl.o + obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o + obj-$(CONFIG_SATA_SIL24) += sata_sil24.o + obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o + obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +-obj-$(CONFIG_AHCI_IMX) += ahci_imx.o ++obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o + + # SFF w/ custom DMA + obj-$(CONFIG_PDC_ADMA) += pdc_adma.o +diff -Nur linux-3.14.36/drivers/ata/pata_acpi.c linux-openelec/drivers/ata/pata_acpi.c +--- linux-3.14.36/drivers/ata/pata_acpi.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_acpi.c 2015-05-06 12:05:42.000000000 -0500 +@@ -7,7 +7,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_amd.c linux-openelec/drivers/ata/pata_amd.c +--- linux-3.14.36/drivers/ata/pata_amd.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_amd.c 2015-05-06 12:05:42.000000000 -0500 +@@ -17,7 +17,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_artop.c linux-openelec/drivers/ata/pata_artop.c +--- linux-3.14.36/drivers/ata/pata_artop.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_artop.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_at91.c linux-openelec/drivers/ata/pata_at91.c +--- linux-3.14.36/drivers/ata/pata_at91.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_at91.c 2015-05-06 12:05:42.000000000 -0500 +@@ -18,7 +18,6 @@ + + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_atiixp.c linux-openelec/drivers/ata/pata_atiixp.c +--- linux-3.14.36/drivers/ata/pata_atiixp.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_atiixp.c 2015-05-06 12:05:42.000000000 -0500 +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_atp867x.c linux-openelec/drivers/ata/pata_atp867x.c +--- linux-3.14.36/drivers/ata/pata_atp867x.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_atp867x.c 2015-05-06 12:05:42.000000000 -0500 +@@ -29,7 +29,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_cmd640.c linux-openelec/drivers/ata/pata_cmd640.c +--- linux-3.14.36/drivers/ata/pata_cmd640.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_cmd640.c 2015-05-06 12:05:42.000000000 -0500 +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_cmd64x.c linux-openelec/drivers/ata/pata_cmd64x.c +--- linux-3.14.36/drivers/ata/pata_cmd64x.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_cmd64x.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,7 +26,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_cs5520.c linux-openelec/drivers/ata/pata_cs5520.c +--- linux-3.14.36/drivers/ata/pata_cs5520.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_cs5520.c 2015-05-06 12:05:42.000000000 -0500 +@@ -34,7 +34,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_cs5530.c linux-openelec/drivers/ata/pata_cs5530.c +--- linux-3.14.36/drivers/ata/pata_cs5530.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_cs5530.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,7 +26,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_cs5535.c linux-openelec/drivers/ata/pata_cs5535.c +--- linux-3.14.36/drivers/ata/pata_cs5535.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_cs5535.c 2015-05-06 12:05:42.000000000 -0500 +@@ -31,7 +31,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_cs5536.c linux-openelec/drivers/ata/pata_cs5536.c +--- linux-3.14.36/drivers/ata/pata_cs5536.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_cs5536.c 2015-05-06 12:05:42.000000000 -0500 +@@ -33,7 +33,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_cypress.c linux-openelec/drivers/ata/pata_cypress.c +--- linux-3.14.36/drivers/ata/pata_cypress.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_cypress.c 2015-05-06 12:05:42.000000000 -0500 +@@ -11,7 +11,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_efar.c linux-openelec/drivers/ata/pata_efar.c +--- linux-3.14.36/drivers/ata/pata_efar.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_efar.c 2015-05-06 12:05:42.000000000 -0500 +@@ -14,7 +14,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_ep93xx.c linux-openelec/drivers/ata/pata_ep93xx.c +--- linux-3.14.36/drivers/ata/pata_ep93xx.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_ep93xx.c 2015-05-06 12:05:42.000000000 -0500 +@@ -34,7 +34,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_hpt366.c linux-openelec/drivers/ata/pata_hpt366.c +--- linux-3.14.36/drivers/ata/pata_hpt366.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_hpt366.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_hpt37x.c linux-openelec/drivers/ata/pata_hpt37x.c +--- linux-3.14.36/drivers/ata/pata_hpt37x.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_hpt37x.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_hpt3x2n.c linux-openelec/drivers/ata/pata_hpt3x2n.c +--- linux-3.14.36/drivers/ata/pata_hpt3x2n.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_hpt3x2n.c 2015-05-06 12:05:42.000000000 -0500 +@@ -20,7 +20,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_hpt3x3.c linux-openelec/drivers/ata/pata_hpt3x3.c +--- linux-3.14.36/drivers/ata/pata_hpt3x3.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_hpt3x3.c 2015-05-06 12:05:42.000000000 -0500 +@@ -16,7 +16,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_imx.c linux-openelec/drivers/ata/pata_imx.c +--- linux-3.14.36/drivers/ata/pata_imx.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_imx.c 2015-05-06 12:05:42.000000000 -0500 +@@ -15,7 +15,6 @@ + */ + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_it8213.c linux-openelec/drivers/ata/pata_it8213.c +--- linux-3.14.36/drivers/ata/pata_it8213.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_it8213.c 2015-05-06 12:05:42.000000000 -0500 +@@ -10,7 +10,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_it821x.c linux-openelec/drivers/ata/pata_it821x.c +--- linux-3.14.36/drivers/ata/pata_it821x.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_it821x.c 2015-05-06 12:05:42.000000000 -0500 +@@ -72,7 +72,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_jmicron.c linux-openelec/drivers/ata/pata_jmicron.c +--- linux-3.14.36/drivers/ata/pata_jmicron.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_jmicron.c 2015-05-06 12:05:42.000000000 -0500 +@@ -10,7 +10,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_marvell.c linux-openelec/drivers/ata/pata_marvell.c +--- linux-3.14.36/drivers/ata/pata_marvell.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_marvell.c 2015-05-06 12:05:42.000000000 -0500 +@@ -11,7 +11,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_mpiix.c linux-openelec/drivers/ata/pata_mpiix.c +--- linux-3.14.36/drivers/ata/pata_mpiix.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_mpiix.c 2015-05-06 12:05:42.000000000 -0500 +@@ -28,7 +28,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_netcell.c linux-openelec/drivers/ata/pata_netcell.c +--- linux-3.14.36/drivers/ata/pata_netcell.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_netcell.c 2015-05-06 12:05:42.000000000 -0500 +@@ -7,7 +7,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_ninja32.c linux-openelec/drivers/ata/pata_ninja32.c +--- linux-3.14.36/drivers/ata/pata_ninja32.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_ninja32.c 2015-05-06 12:05:42.000000000 -0500 +@@ -37,7 +37,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_ns87410.c linux-openelec/drivers/ata/pata_ns87410.c +--- linux-3.14.36/drivers/ata/pata_ns87410.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_ns87410.c 2015-05-06 12:05:42.000000000 -0500 +@@ -20,7 +20,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_ns87415.c linux-openelec/drivers/ata/pata_ns87415.c +--- linux-3.14.36/drivers/ata/pata_ns87415.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_ns87415.c 2015-05-06 12:05:42.000000000 -0500 +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_oldpiix.c linux-openelec/drivers/ata/pata_oldpiix.c +--- linux-3.14.36/drivers/ata/pata_oldpiix.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_oldpiix.c 2015-05-06 12:05:42.000000000 -0500 +@@ -16,7 +16,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_opti.c linux-openelec/drivers/ata/pata_opti.c +--- linux-3.14.36/drivers/ata/pata_opti.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_opti.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,7 +26,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_optidma.c linux-openelec/drivers/ata/pata_optidma.c +--- linux-3.14.36/drivers/ata/pata_optidma.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_optidma.c 2015-05-06 12:05:42.000000000 -0500 +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_pcmcia.c linux-openelec/drivers/ata/pata_pcmcia.c +--- linux-3.14.36/drivers/ata/pata_pcmcia.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_pcmcia.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,7 +26,6 @@ + + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_pdc2027x.c linux-openelec/drivers/ata/pata_pdc2027x.c +--- linux-3.14.36/drivers/ata/pata_pdc2027x.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_pdc2027x.c 2015-05-06 12:05:42.000000000 -0500 +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_pdc202xx_old.c linux-openelec/drivers/ata/pata_pdc202xx_old.c +--- linux-3.14.36/drivers/ata/pata_pdc202xx_old.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_pdc202xx_old.c 2015-05-06 12:05:42.000000000 -0500 +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_piccolo.c linux-openelec/drivers/ata/pata_piccolo.c +--- linux-3.14.36/drivers/ata/pata_piccolo.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_piccolo.c 2015-05-06 12:05:42.000000000 -0500 +@@ -18,7 +18,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_platform.c linux-openelec/drivers/ata/pata_platform.c +--- linux-3.14.36/drivers/ata/pata_platform.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_platform.c 2015-05-06 12:05:42.000000000 -0500 +@@ -13,7 +13,6 @@ + */ + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_pxa.c linux-openelec/drivers/ata/pata_pxa.c +--- linux-3.14.36/drivers/ata/pata_pxa.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_pxa.c 2015-05-06 12:05:42.000000000 -0500 +@@ -20,7 +20,6 @@ + + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_radisys.c linux-openelec/drivers/ata/pata_radisys.c +--- linux-3.14.36/drivers/ata/pata_radisys.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_radisys.c 2015-05-06 12:05:42.000000000 -0500 +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_rdc.c linux-openelec/drivers/ata/pata_rdc.c +--- linux-3.14.36/drivers/ata/pata_rdc.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_rdc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -24,7 +24,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_rz1000.c linux-openelec/drivers/ata/pata_rz1000.c +--- linux-3.14.36/drivers/ata/pata_rz1000.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_rz1000.c 2015-05-06 12:05:42.000000000 -0500 +@@ -14,7 +14,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_sc1200.c linux-openelec/drivers/ata/pata_sc1200.c +--- linux-3.14.36/drivers/ata/pata_sc1200.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_sc1200.c 2015-05-06 12:05:42.000000000 -0500 +@@ -32,7 +32,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_scc.c linux-openelec/drivers/ata/pata_scc.c +--- linux-3.14.36/drivers/ata/pata_scc.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_scc.c 2015-07-24 18:03:28.456842002 -0500 +@@ -35,7 +35,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_scc.c.orig linux-openelec/drivers/ata/pata_scc.c.orig +--- linux-3.14.36/drivers/ata/pata_scc.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/ata/pata_scc.c.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1111 @@ ++/* ++ * Support for IDE interfaces on Celleb platform ++ * ++ * (C) Copyright 2006 TOSHIBA CORPORATION ++ * ++ * This code is based on drivers/ata/ata_piix.c: ++ * Copyright 2003-2005 Red Hat Inc ++ * Copyright 2003-2005 Jeff Garzik ++ * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer ++ * Copyright (C) 1998-2000 Andre Hedrick ++ * Copyright (C) 2003 Red Hat Inc ++ * ++ * and drivers/ata/ahci.c: ++ * Copyright 2004-2005 Red Hat, Inc. ++ * ++ * and drivers/ata/libata-core.c: ++ * Copyright 2003-2004 Red Hat, Inc. All rights reserved. ++ * Copyright 2003-2004 Jeff Garzik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRV_NAME "pata_scc" ++#define DRV_VERSION "0.3" ++ ++#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4 ++ ++/* PCI BARs */ ++#define SCC_CTRL_BAR 0 ++#define SCC_BMID_BAR 1 ++ ++/* offset of CTRL registers */ ++#define SCC_CTL_PIOSHT 0x000 ++#define SCC_CTL_PIOCT 0x004 ++#define SCC_CTL_MDMACT 0x008 ++#define SCC_CTL_MCRCST 0x00C ++#define SCC_CTL_SDMACT 0x010 ++#define SCC_CTL_SCRCST 0x014 ++#define SCC_CTL_UDENVT 0x018 ++#define SCC_CTL_TDVHSEL 0x020 ++#define SCC_CTL_MODEREG 0x024 ++#define SCC_CTL_ECMODE 0xF00 ++#define SCC_CTL_MAEA0 0xF50 ++#define SCC_CTL_MAEC0 0xF54 ++#define SCC_CTL_CCKCTRL 0xFF0 ++ ++/* offset of BMID registers */ ++#define SCC_DMA_CMD 0x000 ++#define SCC_DMA_STATUS 0x004 ++#define SCC_DMA_TABLE_OFS 0x008 ++#define SCC_DMA_INTMASK 0x010 ++#define SCC_DMA_INTST 0x014 ++#define SCC_DMA_PTERADD 0x018 ++#define SCC_REG_CMD_ADDR 0x020 ++#define SCC_REG_DATA 0x000 ++#define SCC_REG_ERR 0x004 ++#define SCC_REG_FEATURE 0x004 ++#define SCC_REG_NSECT 0x008 ++#define SCC_REG_LBAL 0x00C ++#define SCC_REG_LBAM 0x010 ++#define SCC_REG_LBAH 0x014 ++#define SCC_REG_DEVICE 0x018 ++#define SCC_REG_STATUS 0x01C ++#define SCC_REG_CMD 0x01C ++#define SCC_REG_ALTSTATUS 0x020 ++ ++/* register value */ ++#define TDVHSEL_MASTER 0x00000001 ++#define TDVHSEL_SLAVE 0x00000004 ++ ++#define MODE_JCUSFEN 0x00000080 ++ ++#define ECMODE_VALUE 0x01 ++ ++#define CCKCTRL_ATARESET 0x00040000 ++#define CCKCTRL_BUFCNT 0x00020000 ++#define CCKCTRL_CRST 0x00010000 ++#define CCKCTRL_OCLKEN 0x00000100 ++#define CCKCTRL_ATACLKOEN 0x00000002 ++#define CCKCTRL_LCLKEN 0x00000001 ++ ++#define QCHCD_IOS_SS 0x00000001 ++ ++#define QCHSD_STPDIAG 0x00020000 ++ ++#define INTMASK_MSK 0xD1000012 ++#define INTSTS_SERROR 0x80000000 ++#define INTSTS_PRERR 0x40000000 ++#define INTSTS_RERR 0x10000000 ++#define INTSTS_ICERR 0x01000000 ++#define INTSTS_BMSINT 0x00000010 ++#define INTSTS_BMHE 0x00000008 ++#define INTSTS_IOIRQS 0x00000004 ++#define INTSTS_INTRQ 0x00000002 ++#define INTSTS_ACTEINT 0x00000001 ++ ++ ++/* PIO transfer mode table */ ++/* JCHST */ ++static const unsigned long JCHSTtbl[2][7] = { ++ {0x0E, 0x05, 0x02, 0x03, 0x02, 0x00, 0x00}, /* 100MHz */ ++ {0x13, 0x07, 0x04, 0x04, 0x03, 0x00, 0x00} /* 133MHz */ ++}; ++ ++/* JCHHT */ ++static const unsigned long JCHHTtbl[2][7] = { ++ {0x0E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, /* 100MHz */ ++ {0x13, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00} /* 133MHz */ ++}; ++ ++/* JCHCT */ ++static const unsigned long JCHCTtbl[2][7] = { ++ {0x1D, 0x1D, 0x1C, 0x0B, 0x06, 0x00, 0x00}, /* 100MHz */ ++ {0x27, 0x26, 0x26, 0x0E, 0x09, 0x00, 0x00} /* 133MHz */ ++}; ++ ++/* DMA transfer mode table */ ++/* JCHDCTM/JCHDCTS */ ++static const unsigned long JCHDCTxtbl[2][7] = { ++ {0x0A, 0x06, 0x04, 0x03, 0x01, 0x00, 0x00}, /* 100MHz */ ++ {0x0E, 0x09, 0x06, 0x04, 0x02, 0x01, 0x00} /* 133MHz */ ++}; ++ ++/* JCSTWTM/JCSTWTS */ ++static const unsigned long JCSTWTxtbl[2][7] = { ++ {0x06, 0x04, 0x03, 0x02, 0x02, 0x02, 0x00}, /* 100MHz */ ++ {0x09, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02} /* 133MHz */ ++}; ++ ++/* JCTSS */ ++static const unsigned long JCTSStbl[2][7] = { ++ {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}, /* 100MHz */ ++ {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05} /* 133MHz */ ++}; ++ ++/* JCENVT */ ++static const unsigned long JCENVTtbl[2][7] = { ++ {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}, /* 100MHz */ ++ {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} /* 133MHz */ ++}; ++ ++/* JCACTSELS/JCACTSELM */ ++static const unsigned long JCACTSELtbl[2][7] = { ++ {0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}, /* 100MHz */ ++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} /* 133MHz */ ++}; ++ ++static const struct pci_device_id scc_pci_tbl[] = { ++ { PCI_VDEVICE(TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA), 0}, ++ { } /* terminate list */ ++}; ++ ++/** ++ * scc_set_piomode - Initialize host controller PATA PIO timings ++ * @ap: Port whose timings we are configuring ++ * @adev: um ++ * ++ * Set PIO mode for device. ++ * ++ * LOCKING: ++ * None (inherited from caller). ++ */ ++ ++static void scc_set_piomode (struct ata_port *ap, struct ata_device *adev) ++{ ++ unsigned int pio = adev->pio_mode - XFER_PIO_0; ++ void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR]; ++ void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL; ++ void __iomem *piosht_port = ctrl_base + SCC_CTL_PIOSHT; ++ void __iomem *pioct_port = ctrl_base + SCC_CTL_PIOCT; ++ unsigned long reg; ++ int offset; ++ ++ reg = in_be32(cckctrl_port); ++ if (reg & CCKCTRL_ATACLKOEN) ++ offset = 1; /* 133MHz */ ++ else ++ offset = 0; /* 100MHz */ ++ ++ reg = JCHSTtbl[offset][pio] << 16 | JCHHTtbl[offset][pio]; ++ out_be32(piosht_port, reg); ++ reg = JCHCTtbl[offset][pio]; ++ out_be32(pioct_port, reg); ++} ++ ++/** ++ * scc_set_dmamode - Initialize host controller PATA DMA timings ++ * @ap: Port whose timings we are configuring ++ * @adev: um ++ * ++ * Set UDMA mode for device. ++ * ++ * LOCKING: ++ * None (inherited from caller). ++ */ ++ ++static void scc_set_dmamode (struct ata_port *ap, struct ata_device *adev) ++{ ++ unsigned int udma = adev->dma_mode; ++ unsigned int is_slave = (adev->devno != 0); ++ u8 speed = udma; ++ void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR]; ++ void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL; ++ void __iomem *mdmact_port = ctrl_base + SCC_CTL_MDMACT; ++ void __iomem *mcrcst_port = ctrl_base + SCC_CTL_MCRCST; ++ void __iomem *sdmact_port = ctrl_base + SCC_CTL_SDMACT; ++ void __iomem *scrcst_port = ctrl_base + SCC_CTL_SCRCST; ++ void __iomem *udenvt_port = ctrl_base + SCC_CTL_UDENVT; ++ void __iomem *tdvhsel_port = ctrl_base + SCC_CTL_TDVHSEL; ++ int offset, idx; ++ ++ if (in_be32(cckctrl_port) & CCKCTRL_ATACLKOEN) ++ offset = 1; /* 133MHz */ ++ else ++ offset = 0; /* 100MHz */ ++ ++ if (speed >= XFER_UDMA_0) ++ idx = speed - XFER_UDMA_0; ++ else ++ return; ++ ++ if (is_slave) { ++ out_be32(sdmact_port, JCHDCTxtbl[offset][idx]); ++ out_be32(scrcst_port, JCSTWTxtbl[offset][idx]); ++ out_be32(tdvhsel_port, ++ (in_be32(tdvhsel_port) & ~TDVHSEL_SLAVE) | (JCACTSELtbl[offset][idx] << 2)); ++ } else { ++ out_be32(mdmact_port, JCHDCTxtbl[offset][idx]); ++ out_be32(mcrcst_port, JCSTWTxtbl[offset][idx]); ++ out_be32(tdvhsel_port, ++ (in_be32(tdvhsel_port) & ~TDVHSEL_MASTER) | JCACTSELtbl[offset][idx]); ++ } ++ out_be32(udenvt_port, ++ JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx]); ++} ++ ++unsigned long scc_mode_filter(struct ata_device *adev, unsigned long mask) ++{ ++ /* errata A308 workaround: limit ATAPI UDMA mode to UDMA4 */ ++ if (adev->class == ATA_DEV_ATAPI && ++ (mask & (0xE0 << ATA_SHIFT_UDMA))) { ++ printk(KERN_INFO "%s: limit ATAPI UDMA to UDMA4\n", DRV_NAME); ++ mask &= ~(0xE0 << ATA_SHIFT_UDMA); ++ } ++ return mask; ++} ++ ++/** ++ * scc_tf_load - send taskfile registers to host controller ++ * @ap: Port to which output is sent ++ * @tf: ATA taskfile register set ++ * ++ * Note: Original code is ata_sff_tf_load(). ++ */ ++ ++static void scc_tf_load (struct ata_port *ap, const struct ata_taskfile *tf) ++{ ++ struct ata_ioports *ioaddr = &ap->ioaddr; ++ unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; ++ ++ if (tf->ctl != ap->last_ctl) { ++ out_be32(ioaddr->ctl_addr, tf->ctl); ++ ap->last_ctl = tf->ctl; ++ ata_wait_idle(ap); ++ } ++ ++ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { ++ out_be32(ioaddr->feature_addr, tf->hob_feature); ++ out_be32(ioaddr->nsect_addr, tf->hob_nsect); ++ out_be32(ioaddr->lbal_addr, tf->hob_lbal); ++ out_be32(ioaddr->lbam_addr, tf->hob_lbam); ++ out_be32(ioaddr->lbah_addr, tf->hob_lbah); ++ VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n", ++ tf->hob_feature, ++ tf->hob_nsect, ++ tf->hob_lbal, ++ tf->hob_lbam, ++ tf->hob_lbah); ++ } ++ ++ if (is_addr) { ++ out_be32(ioaddr->feature_addr, tf->feature); ++ out_be32(ioaddr->nsect_addr, tf->nsect); ++ out_be32(ioaddr->lbal_addr, tf->lbal); ++ out_be32(ioaddr->lbam_addr, tf->lbam); ++ out_be32(ioaddr->lbah_addr, tf->lbah); ++ VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", ++ tf->feature, ++ tf->nsect, ++ tf->lbal, ++ tf->lbam, ++ tf->lbah); ++ } ++ ++ if (tf->flags & ATA_TFLAG_DEVICE) { ++ out_be32(ioaddr->device_addr, tf->device); ++ VPRINTK("device 0x%X\n", tf->device); ++ } ++ ++ ata_wait_idle(ap); ++} ++ ++/** ++ * scc_check_status - Read device status reg & clear interrupt ++ * @ap: port where the device is ++ * ++ * Note: Original code is ata_check_status(). ++ */ ++ ++static u8 scc_check_status (struct ata_port *ap) ++{ ++ return in_be32(ap->ioaddr.status_addr); ++} ++ ++/** ++ * scc_tf_read - input device's ATA taskfile shadow registers ++ * @ap: Port from which input is read ++ * @tf: ATA taskfile register set for storing input ++ * ++ * Note: Original code is ata_sff_tf_read(). ++ */ ++ ++static void scc_tf_read (struct ata_port *ap, struct ata_taskfile *tf) ++{ ++ struct ata_ioports *ioaddr = &ap->ioaddr; ++ ++ tf->command = scc_check_status(ap); ++ tf->feature = in_be32(ioaddr->error_addr); ++ tf->nsect = in_be32(ioaddr->nsect_addr); ++ tf->lbal = in_be32(ioaddr->lbal_addr); ++ tf->lbam = in_be32(ioaddr->lbam_addr); ++ tf->lbah = in_be32(ioaddr->lbah_addr); ++ tf->device = in_be32(ioaddr->device_addr); ++ ++ if (tf->flags & ATA_TFLAG_LBA48) { ++ out_be32(ioaddr->ctl_addr, tf->ctl | ATA_HOB); ++ tf->hob_feature = in_be32(ioaddr->error_addr); ++ tf->hob_nsect = in_be32(ioaddr->nsect_addr); ++ tf->hob_lbal = in_be32(ioaddr->lbal_addr); ++ tf->hob_lbam = in_be32(ioaddr->lbam_addr); ++ tf->hob_lbah = in_be32(ioaddr->lbah_addr); ++ out_be32(ioaddr->ctl_addr, tf->ctl); ++ ap->last_ctl = tf->ctl; ++ } ++} ++ ++/** ++ * scc_exec_command - issue ATA command to host controller ++ * @ap: port to which command is being issued ++ * @tf: ATA taskfile register set ++ * ++ * Note: Original code is ata_sff_exec_command(). ++ */ ++ ++static void scc_exec_command (struct ata_port *ap, ++ const struct ata_taskfile *tf) ++{ ++ DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command); ++ ++ out_be32(ap->ioaddr.command_addr, tf->command); ++ ata_sff_pause(ap); ++} ++ ++/** ++ * scc_check_altstatus - Read device alternate status reg ++ * @ap: port where the device is ++ */ ++ ++static u8 scc_check_altstatus (struct ata_port *ap) ++{ ++ return in_be32(ap->ioaddr.altstatus_addr); ++} ++ ++/** ++ * scc_dev_select - Select device 0/1 on ATA bus ++ * @ap: ATA channel to manipulate ++ * @device: ATA device (numbered from zero) to select ++ * ++ * Note: Original code is ata_sff_dev_select(). ++ */ ++ ++static void scc_dev_select (struct ata_port *ap, unsigned int device) ++{ ++ u8 tmp; ++ ++ if (device == 0) ++ tmp = ATA_DEVICE_OBS; ++ else ++ tmp = ATA_DEVICE_OBS | ATA_DEV1; ++ ++ out_be32(ap->ioaddr.device_addr, tmp); ++ ata_sff_pause(ap); ++} ++ ++/** ++ * scc_set_devctl - Write device control reg ++ * @ap: port where the device is ++ * @ctl: value to write ++ */ ++ ++static void scc_set_devctl(struct ata_port *ap, u8 ctl) ++{ ++ out_be32(ap->ioaddr.ctl_addr, ctl); ++} ++ ++/** ++ * scc_bmdma_setup - Set up PCI IDE BMDMA transaction ++ * @qc: Info associated with this ATA transaction. ++ * ++ * Note: Original code is ata_bmdma_setup(). ++ */ ++ ++static void scc_bmdma_setup (struct ata_queued_cmd *qc) ++{ ++ struct ata_port *ap = qc->ap; ++ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); ++ u8 dmactl; ++ void __iomem *mmio = ap->ioaddr.bmdma_addr; ++ ++ /* load PRD table addr */ ++ out_be32(mmio + SCC_DMA_TABLE_OFS, ap->bmdma_prd_dma); ++ ++ /* specify data direction, triple-check start bit is clear */ ++ dmactl = in_be32(mmio + SCC_DMA_CMD); ++ dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); ++ if (!rw) ++ dmactl |= ATA_DMA_WR; ++ out_be32(mmio + SCC_DMA_CMD, dmactl); ++ ++ /* issue r/w command */ ++ ap->ops->sff_exec_command(ap, &qc->tf); ++} ++ ++/** ++ * scc_bmdma_start - Start a PCI IDE BMDMA transaction ++ * @qc: Info associated with this ATA transaction. ++ * ++ * Note: Original code is ata_bmdma_start(). ++ */ ++ ++static void scc_bmdma_start (struct ata_queued_cmd *qc) ++{ ++ struct ata_port *ap = qc->ap; ++ u8 dmactl; ++ void __iomem *mmio = ap->ioaddr.bmdma_addr; ++ ++ /* start host DMA transaction */ ++ dmactl = in_be32(mmio + SCC_DMA_CMD); ++ out_be32(mmio + SCC_DMA_CMD, dmactl | ATA_DMA_START); ++} ++ ++/** ++ * scc_devchk - PATA device presence detection ++ * @ap: ATA channel to examine ++ * @device: Device to examine (starting at zero) ++ * ++ * Note: Original code is ata_devchk(). ++ */ ++ ++static unsigned int scc_devchk (struct ata_port *ap, ++ unsigned int device) ++{ ++ struct ata_ioports *ioaddr = &ap->ioaddr; ++ u8 nsect, lbal; ++ ++ ap->ops->sff_dev_select(ap, device); ++ ++ out_be32(ioaddr->nsect_addr, 0x55); ++ out_be32(ioaddr->lbal_addr, 0xaa); ++ ++ out_be32(ioaddr->nsect_addr, 0xaa); ++ out_be32(ioaddr->lbal_addr, 0x55); ++ ++ out_be32(ioaddr->nsect_addr, 0x55); ++ out_be32(ioaddr->lbal_addr, 0xaa); ++ ++ nsect = in_be32(ioaddr->nsect_addr); ++ lbal = in_be32(ioaddr->lbal_addr); ++ ++ if ((nsect == 0x55) && (lbal == 0xaa)) ++ return 1; /* we found a device */ ++ ++ return 0; /* nothing found */ ++} ++ ++/** ++ * scc_wait_after_reset - wait for devices to become ready after reset ++ * ++ * Note: Original code is ata_sff_wait_after_reset ++ */ ++ ++static int scc_wait_after_reset(struct ata_link *link, unsigned int devmask, ++ unsigned long deadline) ++{ ++ struct ata_port *ap = link->ap; ++ struct ata_ioports *ioaddr = &ap->ioaddr; ++ unsigned int dev0 = devmask & (1 << 0); ++ unsigned int dev1 = devmask & (1 << 1); ++ int rc, ret = 0; ++ ++ /* Spec mandates ">= 2ms" before checking status. We wait ++ * 150ms, because that was the magic delay used for ATAPI ++ * devices in Hale Landis's ATADRVR, for the period of time ++ * between when the ATA command register is written, and then ++ * status is checked. Because waiting for "a while" before ++ * checking status is fine, post SRST, we perform this magic ++ * delay here as well. ++ * ++ * Old drivers/ide uses the 2mS rule and then waits for ready. ++ */ ++ ata_msleep(ap, 150); ++ ++ /* always check readiness of the master device */ ++ rc = ata_sff_wait_ready(link, deadline); ++ /* -ENODEV means the odd clown forgot the D7 pulldown resistor ++ * and TF status is 0xff, bail out on it too. ++ */ ++ if (rc) ++ return rc; ++ ++ /* if device 1 was found in ata_devchk, wait for register ++ * access briefly, then wait for BSY to clear. ++ */ ++ if (dev1) { ++ int i; ++ ++ ap->ops->sff_dev_select(ap, 1); ++ ++ /* Wait for register access. Some ATAPI devices fail ++ * to set nsect/lbal after reset, so don't waste too ++ * much time on it. We're gonna wait for !BSY anyway. ++ */ ++ for (i = 0; i < 2; i++) { ++ u8 nsect, lbal; ++ ++ nsect = in_be32(ioaddr->nsect_addr); ++ lbal = in_be32(ioaddr->lbal_addr); ++ if ((nsect == 1) && (lbal == 1)) ++ break; ++ ata_msleep(ap, 50); /* give drive a breather */ ++ } ++ ++ rc = ata_sff_wait_ready(link, deadline); ++ if (rc) { ++ if (rc != -ENODEV) ++ return rc; ++ ret = rc; ++ } ++ } ++ ++ /* is all this really necessary? */ ++ ap->ops->sff_dev_select(ap, 0); ++ if (dev1) ++ ap->ops->sff_dev_select(ap, 1); ++ if (dev0) ++ ap->ops->sff_dev_select(ap, 0); ++ ++ return ret; ++} ++ ++/** ++ * scc_bus_softreset - PATA device software reset ++ * ++ * Note: Original code is ata_bus_softreset(). ++ */ ++ ++static unsigned int scc_bus_softreset(struct ata_port *ap, unsigned int devmask, ++ unsigned long deadline) ++{ ++ struct ata_ioports *ioaddr = &ap->ioaddr; ++ ++ DPRINTK("ata%u: bus reset via SRST\n", ap->print_id); ++ ++ /* software reset. causes dev0 to be selected */ ++ out_be32(ioaddr->ctl_addr, ap->ctl); ++ udelay(20); ++ out_be32(ioaddr->ctl_addr, ap->ctl | ATA_SRST); ++ udelay(20); ++ out_be32(ioaddr->ctl_addr, ap->ctl); ++ ++ scc_wait_after_reset(&ap->link, devmask, deadline); ++ ++ return 0; ++} ++ ++/** ++ * scc_softreset - reset host port via ATA SRST ++ * @ap: port to reset ++ * @classes: resulting classes of attached devices ++ * @deadline: deadline jiffies for the operation ++ * ++ * Note: Original code is ata_sff_softreset(). ++ */ ++ ++static int scc_softreset(struct ata_link *link, unsigned int *classes, ++ unsigned long deadline) ++{ ++ struct ata_port *ap = link->ap; ++ unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; ++ unsigned int devmask = 0, err_mask; ++ u8 err; ++ ++ DPRINTK("ENTER\n"); ++ ++ /* determine if device 0/1 are present */ ++ if (scc_devchk(ap, 0)) ++ devmask |= (1 << 0); ++ if (slave_possible && scc_devchk(ap, 1)) ++ devmask |= (1 << 1); ++ ++ /* select device 0 again */ ++ ap->ops->sff_dev_select(ap, 0); ++ ++ /* issue bus reset */ ++ DPRINTK("about to softreset, devmask=%x\n", devmask); ++ err_mask = scc_bus_softreset(ap, devmask, deadline); ++ if (err_mask) { ++ ata_port_err(ap, "SRST failed (err_mask=0x%x)\n", err_mask); ++ return -EIO; ++ } ++ ++ /* determine by signature whether we have ATA or ATAPI devices */ ++ classes[0] = ata_sff_dev_classify(&ap->link.device[0], ++ devmask & (1 << 0), &err); ++ if (slave_possible && err != 0x81) ++ classes[1] = ata_sff_dev_classify(&ap->link.device[1], ++ devmask & (1 << 1), &err); ++ ++ DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]); ++ return 0; ++} ++ ++/** ++ * scc_bmdma_stop - Stop PCI IDE BMDMA transfer ++ * @qc: Command we are ending DMA for ++ */ ++ ++static void scc_bmdma_stop (struct ata_queued_cmd *qc) ++{ ++ struct ata_port *ap = qc->ap; ++ void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR]; ++ void __iomem *bmid_base = ap->host->iomap[SCC_BMID_BAR]; ++ u32 reg; ++ ++ while (1) { ++ reg = in_be32(bmid_base + SCC_DMA_INTST); ++ ++ if (reg & INTSTS_SERROR) { ++ printk(KERN_WARNING "%s: SERROR\n", DRV_NAME); ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_SERROR|INTSTS_BMSINT); ++ out_be32(bmid_base + SCC_DMA_CMD, ++ in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); ++ continue; ++ } ++ ++ if (reg & INTSTS_PRERR) { ++ u32 maea0, maec0; ++ maea0 = in_be32(ctrl_base + SCC_CTL_MAEA0); ++ maec0 = in_be32(ctrl_base + SCC_CTL_MAEC0); ++ printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", DRV_NAME, maea0, maec0); ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_PRERR|INTSTS_BMSINT); ++ out_be32(bmid_base + SCC_DMA_CMD, ++ in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); ++ continue; ++ } ++ ++ if (reg & INTSTS_RERR) { ++ printk(KERN_WARNING "%s: Response Error\n", DRV_NAME); ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_RERR|INTSTS_BMSINT); ++ out_be32(bmid_base + SCC_DMA_CMD, ++ in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); ++ continue; ++ } ++ ++ if (reg & INTSTS_ICERR) { ++ out_be32(bmid_base + SCC_DMA_CMD, ++ in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); ++ printk(KERN_WARNING "%s: Illegal Configuration\n", DRV_NAME); ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ICERR|INTSTS_BMSINT); ++ continue; ++ } ++ ++ if (reg & INTSTS_BMSINT) { ++ unsigned int classes; ++ unsigned long deadline = ata_deadline(jiffies, ATA_TMOUT_BOOT); ++ printk(KERN_WARNING "%s: Internal Bus Error\n", DRV_NAME); ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMSINT); ++ /* TBD: SW reset */ ++ scc_softreset(&ap->link, &classes, deadline); ++ continue; ++ } ++ ++ if (reg & INTSTS_BMHE) { ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMHE); ++ continue; ++ } ++ ++ if (reg & INTSTS_ACTEINT) { ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ACTEINT); ++ continue; ++ } ++ ++ if (reg & INTSTS_IOIRQS) { ++ out_be32(bmid_base + SCC_DMA_INTST, INTSTS_IOIRQS); ++ continue; ++ } ++ break; ++ } ++ ++ /* clear start/stop bit */ ++ out_be32(bmid_base + SCC_DMA_CMD, ++ in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); ++ ++ /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ ++ ata_sff_dma_pause(ap); /* dummy read */ ++} ++ ++/** ++ * scc_bmdma_status - Read PCI IDE BMDMA status ++ * @ap: Port associated with this ATA transaction. ++ */ ++ ++static u8 scc_bmdma_status (struct ata_port *ap) ++{ ++ void __iomem *mmio = ap->ioaddr.bmdma_addr; ++ u8 host_stat = in_be32(mmio + SCC_DMA_STATUS); ++ u32 int_status = in_be32(mmio + SCC_DMA_INTST); ++ struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->link.active_tag); ++ static int retry = 0; ++ ++ /* return if IOS_SS is cleared */ ++ if (!(in_be32(mmio + SCC_DMA_CMD) & ATA_DMA_START)) ++ return host_stat; ++ ++ /* errata A252,A308 workaround: Step4 */ ++ if ((scc_check_altstatus(ap) & ATA_ERR) ++ && (int_status & INTSTS_INTRQ)) ++ return (host_stat | ATA_DMA_INTR); ++ ++ /* errata A308 workaround Step5 */ ++ if (int_status & INTSTS_IOIRQS) { ++ host_stat |= ATA_DMA_INTR; ++ ++ /* We don't check ATAPI DMA because it is limited to UDMA4 */ ++ if ((qc->tf.protocol == ATA_PROT_DMA && ++ qc->dev->xfer_mode > XFER_UDMA_4)) { ++ if (!(int_status & INTSTS_ACTEINT)) { ++ printk(KERN_WARNING "ata%u: operation failed (transfer data loss)\n", ++ ap->print_id); ++ host_stat |= ATA_DMA_ERR; ++ if (retry++) ++ ap->udma_mask &= ~(1 << qc->dev->xfer_mode); ++ } else ++ retry = 0; ++ } ++ } ++ ++ return host_stat; ++} ++ ++/** ++ * scc_data_xfer - Transfer data by PIO ++ * @dev: device for this I/O ++ * @buf: data buffer ++ * @buflen: buffer length ++ * @rw: read/write ++ * ++ * Note: Original code is ata_sff_data_xfer(). ++ */ ++ ++static unsigned int scc_data_xfer (struct ata_device *dev, unsigned char *buf, ++ unsigned int buflen, int rw) ++{ ++ struct ata_port *ap = dev->link->ap; ++ unsigned int words = buflen >> 1; ++ unsigned int i; ++ __le16 *buf16 = (__le16 *) buf; ++ void __iomem *mmio = ap->ioaddr.data_addr; ++ ++ /* Transfer multiple of 2 bytes */ ++ if (rw == READ) ++ for (i = 0; i < words; i++) ++ buf16[i] = cpu_to_le16(in_be32(mmio)); ++ else ++ for (i = 0; i < words; i++) ++ out_be32(mmio, le16_to_cpu(buf16[i])); ++ ++ /* Transfer trailing 1 byte, if any. */ ++ if (unlikely(buflen & 0x01)) { ++ __le16 align_buf[1] = { 0 }; ++ unsigned char *trailing_buf = buf + buflen - 1; ++ ++ if (rw == READ) { ++ align_buf[0] = cpu_to_le16(in_be32(mmio)); ++ memcpy(trailing_buf, align_buf, 1); ++ } else { ++ memcpy(align_buf, trailing_buf, 1); ++ out_be32(mmio, le16_to_cpu(align_buf[0])); ++ } ++ words++; ++ } ++ ++ return words << 1; ++} ++ ++/** ++ * scc_postreset - standard postreset callback ++ * @ap: the target ata_port ++ * @classes: classes of attached devices ++ * ++ * Note: Original code is ata_sff_postreset(). ++ */ ++ ++static void scc_postreset(struct ata_link *link, unsigned int *classes) ++{ ++ struct ata_port *ap = link->ap; ++ ++ DPRINTK("ENTER\n"); ++ ++ /* is double-select really necessary? */ ++ if (classes[0] != ATA_DEV_NONE) ++ ap->ops->sff_dev_select(ap, 1); ++ if (classes[1] != ATA_DEV_NONE) ++ ap->ops->sff_dev_select(ap, 0); ++ ++ /* bail out if no device is present */ ++ if (classes[0] == ATA_DEV_NONE && classes[1] == ATA_DEV_NONE) { ++ DPRINTK("EXIT, no device\n"); ++ return; ++ } ++ ++ /* set up device control */ ++ out_be32(ap->ioaddr.ctl_addr, ap->ctl); ++ ++ DPRINTK("EXIT\n"); ++} ++ ++/** ++ * scc_irq_clear - Clear PCI IDE BMDMA interrupt. ++ * @ap: Port associated with this ATA transaction. ++ * ++ * Note: Original code is ata_bmdma_irq_clear(). ++ */ ++ ++static void scc_irq_clear (struct ata_port *ap) ++{ ++ void __iomem *mmio = ap->ioaddr.bmdma_addr; ++ ++ if (!mmio) ++ return; ++ ++ out_be32(mmio + SCC_DMA_STATUS, in_be32(mmio + SCC_DMA_STATUS)); ++} ++ ++/** ++ * scc_port_start - Set port up for dma. ++ * @ap: Port to initialize ++ * ++ * Allocate space for PRD table using ata_bmdma_port_start(). ++ * Set PRD table address for PTERADD. (PRD Transfer End Read) ++ */ ++ ++static int scc_port_start (struct ata_port *ap) ++{ ++ void __iomem *mmio = ap->ioaddr.bmdma_addr; ++ int rc; ++ ++ rc = ata_bmdma_port_start(ap); ++ if (rc) ++ return rc; ++ ++ out_be32(mmio + SCC_DMA_PTERADD, ap->bmdma_prd_dma); ++ return 0; ++} ++ ++/** ++ * scc_port_stop - Undo scc_port_start() ++ * @ap: Port to shut down ++ * ++ * Reset PTERADD. ++ */ ++ ++static void scc_port_stop (struct ata_port *ap) ++{ ++ void __iomem *mmio = ap->ioaddr.bmdma_addr; ++ ++ out_be32(mmio + SCC_DMA_PTERADD, 0); ++} ++ ++static struct scsi_host_template scc_sht = { ++ ATA_BMDMA_SHT(DRV_NAME), ++}; ++ ++static struct ata_port_operations scc_pata_ops = { ++ .inherits = &ata_bmdma_port_ops, ++ ++ .set_piomode = scc_set_piomode, ++ .set_dmamode = scc_set_dmamode, ++ .mode_filter = scc_mode_filter, ++ ++ .sff_tf_load = scc_tf_load, ++ .sff_tf_read = scc_tf_read, ++ .sff_exec_command = scc_exec_command, ++ .sff_check_status = scc_check_status, ++ .sff_check_altstatus = scc_check_altstatus, ++ .sff_dev_select = scc_dev_select, ++ .sff_set_devctl = scc_set_devctl, ++ ++ .bmdma_setup = scc_bmdma_setup, ++ .bmdma_start = scc_bmdma_start, ++ .bmdma_stop = scc_bmdma_stop, ++ .bmdma_status = scc_bmdma_status, ++ .sff_data_xfer = scc_data_xfer, ++ ++ .cable_detect = ata_cable_80wire, ++ .softreset = scc_softreset, ++ .postreset = scc_postreset, ++ ++ .sff_irq_clear = scc_irq_clear, ++ ++ .port_start = scc_port_start, ++ .port_stop = scc_port_stop, ++}; ++ ++static struct ata_port_info scc_port_info[] = { ++ { ++ .flags = ATA_FLAG_SLAVE_POSS, ++ .pio_mask = ATA_PIO4, ++ /* No MWDMA */ ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &scc_pata_ops, ++ }, ++}; ++ ++/** ++ * scc_reset_controller - initialize SCC PATA controller. ++ */ ++ ++static int scc_reset_controller(struct ata_host *host) ++{ ++ void __iomem *ctrl_base = host->iomap[SCC_CTRL_BAR]; ++ void __iomem *bmid_base = host->iomap[SCC_BMID_BAR]; ++ void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL; ++ void __iomem *mode_port = ctrl_base + SCC_CTL_MODEREG; ++ void __iomem *ecmode_port = ctrl_base + SCC_CTL_ECMODE; ++ void __iomem *intmask_port = bmid_base + SCC_DMA_INTMASK; ++ void __iomem *dmastatus_port = bmid_base + SCC_DMA_STATUS; ++ u32 reg = 0; ++ ++ out_be32(cckctrl_port, reg); ++ reg |= CCKCTRL_ATACLKOEN; ++ out_be32(cckctrl_port, reg); ++ reg |= CCKCTRL_LCLKEN | CCKCTRL_OCLKEN; ++ out_be32(cckctrl_port, reg); ++ reg |= CCKCTRL_CRST; ++ out_be32(cckctrl_port, reg); ++ ++ for (;;) { ++ reg = in_be32(cckctrl_port); ++ if (reg & CCKCTRL_CRST) ++ break; ++ udelay(5000); ++ } ++ ++ reg |= CCKCTRL_ATARESET; ++ out_be32(cckctrl_port, reg); ++ out_be32(ecmode_port, ECMODE_VALUE); ++ out_be32(mode_port, MODE_JCUSFEN); ++ out_be32(intmask_port, INTMASK_MSK); ++ ++ if (in_be32(dmastatus_port) & QCHSD_STPDIAG) { ++ printk(KERN_WARNING "%s: failed to detect 80c cable. (PDIAG# is high)\n", DRV_NAME); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/** ++ * scc_setup_ports - initialize ioaddr with SCC PATA port offsets. ++ * @ioaddr: IO address structure to be initialized ++ * @base: base address of BMID region ++ */ ++ ++static void scc_setup_ports (struct ata_ioports *ioaddr, void __iomem *base) ++{ ++ ioaddr->cmd_addr = base + SCC_REG_CMD_ADDR; ++ ioaddr->altstatus_addr = ioaddr->cmd_addr + SCC_REG_ALTSTATUS; ++ ioaddr->ctl_addr = ioaddr->cmd_addr + SCC_REG_ALTSTATUS; ++ ioaddr->bmdma_addr = base; ++ ioaddr->data_addr = ioaddr->cmd_addr + SCC_REG_DATA; ++ ioaddr->error_addr = ioaddr->cmd_addr + SCC_REG_ERR; ++ ioaddr->feature_addr = ioaddr->cmd_addr + SCC_REG_FEATURE; ++ ioaddr->nsect_addr = ioaddr->cmd_addr + SCC_REG_NSECT; ++ ioaddr->lbal_addr = ioaddr->cmd_addr + SCC_REG_LBAL; ++ ioaddr->lbam_addr = ioaddr->cmd_addr + SCC_REG_LBAM; ++ ioaddr->lbah_addr = ioaddr->cmd_addr + SCC_REG_LBAH; ++ ioaddr->device_addr = ioaddr->cmd_addr + SCC_REG_DEVICE; ++ ioaddr->status_addr = ioaddr->cmd_addr + SCC_REG_STATUS; ++ ioaddr->command_addr = ioaddr->cmd_addr + SCC_REG_CMD; ++} ++ ++static int scc_host_init(struct ata_host *host) ++{ ++ struct pci_dev *pdev = to_pci_dev(host->dev); ++ int rc; ++ ++ rc = scc_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); ++ if (rc) ++ return rc; ++ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); ++ if (rc) ++ return rc; ++ ++ scc_setup_ports(&host->ports[0]->ioaddr, host->iomap[SCC_BMID_BAR]); ++ ++ pci_set_master(pdev); ++ ++ return 0; ++} ++ ++/** ++ * scc_init_one - Register SCC PATA device with kernel services ++ * @pdev: PCI device to register ++ * @ent: Entry in scc_pci_tbl matching with @pdev ++ * ++ * LOCKING: ++ * Inherited from PCI layer (may sleep). ++ * ++ * RETURNS: ++ * Zero on success, or -ERRNO value. ++ */ ++ ++static int scc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) ++{ ++ unsigned int board_idx = (unsigned int) ent->driver_data; ++ const struct ata_port_info *ppi[] = { &scc_port_info[board_idx], NULL }; ++ struct ata_host *host; ++ int rc; ++ ++ ata_print_version_once(&pdev->dev, DRV_VERSION); ++ ++ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 1); ++ if (!host) ++ return -ENOMEM; ++ ++ rc = pcim_enable_device(pdev); ++ if (rc) ++ return rc; ++ ++ rc = pcim_iomap_regions(pdev, (1 << SCC_CTRL_BAR) | (1 << SCC_BMID_BAR), DRV_NAME); ++ if (rc == -EBUSY) ++ pcim_pin_device(pdev); ++ if (rc) ++ return rc; ++ host->iomap = pcim_iomap_table(pdev); ++ ++ ata_port_pbar_desc(host->ports[0], SCC_CTRL_BAR, -1, "ctrl"); ++ ata_port_pbar_desc(host->ports[0], SCC_BMID_BAR, -1, "bmid"); ++ ++ rc = scc_host_init(host); ++ if (rc) ++ return rc; ++ ++ return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt, ++ IRQF_SHARED, &scc_sht); ++} ++ ++static struct pci_driver scc_pci_driver = { ++ .name = DRV_NAME, ++ .id_table = scc_pci_tbl, ++ .probe = scc_init_one, ++ .remove = ata_pci_remove_one, ++#ifdef CONFIG_PM ++ .suspend = ata_pci_device_suspend, ++ .resume = ata_pci_device_resume, ++#endif ++}; ++ ++module_pci_driver(scc_pci_driver); ++ ++MODULE_AUTHOR("Toshiba corp"); ++MODULE_DESCRIPTION("SCSI low-level driver for Toshiba SCC PATA controller"); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(pci, scc_pci_tbl); ++MODULE_VERSION(DRV_VERSION); +diff -Nur linux-3.14.36/drivers/ata/pata_sch.c linux-openelec/drivers/ata/pata_sch.c +--- linux-3.14.36/drivers/ata/pata_sch.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_sch.c 2015-05-06 12:05:42.000000000 -0500 +@@ -27,7 +27,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_serverworks.c linux-openelec/drivers/ata/pata_serverworks.c +--- linux-3.14.36/drivers/ata/pata_serverworks.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_serverworks.c 2015-07-24 18:03:28.780842002 -0500 +@@ -34,7 +34,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_serverworks.c.orig linux-openelec/drivers/ata/pata_serverworks.c.orig +--- linux-3.14.36/drivers/ata/pata_serverworks.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/ata/pata_serverworks.c.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,483 @@ ++/* ++ * pata_serverworks.c - Serverworks PATA for new ATA layer ++ * (C) 2005 Red Hat Inc ++ * (C) 2010 Bartlomiej Zolnierkiewicz ++ * ++ * based upon ++ * ++ * serverworks.c ++ * ++ * Copyright (C) 1998-2000 Michel Aubry ++ * Copyright (C) 1998-2000 Andrzej Krzysztofowicz ++ * Copyright (C) 1998-2000 Andre Hedrick ++ * Portions copyright (c) 2001 Sun Microsystems ++ * ++ * ++ * RCC/ServerWorks IDE driver for Linux ++ * ++ * OSB4: `Open South Bridge' IDE Interface (fn 1) ++ * supports UDMA mode 2 (33 MB/s) ++ * ++ * CSB5: `Champion South Bridge' IDE Interface (fn 1) ++ * all revisions support UDMA mode 4 (66 MB/s) ++ * revision A2.0 and up support UDMA mode 5 (100 MB/s) ++ * ++ * *** The CSB5 does not provide ANY register *** ++ * *** to detect 80-conductor cable presence. *** ++ * ++ * CSB6: `Champion South Bridge' IDE Interface (optional: third channel) ++ * ++ * Documentation: ++ * Available under NDA only. Errata info very hard to get. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRV_NAME "pata_serverworks" ++#define DRV_VERSION "0.4.3" ++ ++#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ ++#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ ++ ++/* Seagate Barracuda ATA IV Family drives in UDMA mode 5 ++ * can overrun their FIFOs when used with the CSB5 */ ++ ++static const char *csb_bad_ata100[] = { ++ "ST320011A", ++ "ST340016A", ++ "ST360021A", ++ "ST380021A", ++ NULL ++}; ++ ++/** ++ * oem_cable - Dell/Sun serverworks cable detection ++ * @ap: ATA port to do cable detect ++ * ++ * Dell PowerEdge and Sun Cobalt 'Alpine' hide the 40/80 pin select ++ * for their interfaces in the top two bits of the subsystem ID. ++ */ ++ ++static int oem_cable(struct ata_port *ap) ++{ ++ struct pci_dev *pdev = to_pci_dev(ap->host->dev); ++ ++ if (pdev->subsystem_device & (1 << (ap->port_no + 14))) ++ return ATA_CBL_PATA80; ++ return ATA_CBL_PATA40; ++} ++ ++struct sv_cable_table { ++ int device; ++ int subvendor; ++ int (*cable_detect)(struct ata_port *ap); ++}; ++ ++static struct sv_cable_table cable_detect[] = { ++ { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, oem_cable }, ++ { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, oem_cable }, ++ { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, oem_cable }, ++ { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, ata_cable_40wire }, ++ { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, ata_cable_unknown }, ++ { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, ata_cable_unknown }, ++ { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, ata_cable_unknown }, ++ { PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, ata_cable_unknown }, ++ { } ++}; ++ ++/** ++ * serverworks_cable_detect - cable detection ++ * @ap: ATA port ++ * ++ * Perform cable detection according to the device and subvendor ++ * identifications ++ */ ++ ++static int serverworks_cable_detect(struct ata_port *ap) ++{ ++ struct pci_dev *pdev = to_pci_dev(ap->host->dev); ++ struct sv_cable_table *cb = cable_detect; ++ ++ while(cb->device) { ++ if (cb->device == pdev->device && ++ (cb->subvendor == pdev->subsystem_vendor || ++ cb->subvendor == PCI_ANY_ID)) { ++ return cb->cable_detect(ap); ++ } ++ cb++; ++ } ++ ++ BUG(); ++ return -1; /* kill compiler warning */ ++} ++ ++/** ++ * serverworks_is_csb - Check for CSB or OSB ++ * @pdev: PCI device to check ++ * ++ * Returns true if the device being checked is known to be a CSB ++ * series device. ++ */ ++ ++static u8 serverworks_is_csb(struct pci_dev *pdev) ++{ ++ switch (pdev->device) { ++ case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: ++ case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: ++ case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: ++ case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: ++ return 1; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++/** ++ * serverworks_osb4_filter - mode selection filter ++ * @adev: ATA device ++ * @mask: Mask of proposed modes ++ * ++ * Filter the offered modes for the device to apply controller ++ * specific rules. OSB4 requires no UDMA for disks due to a FIFO ++ * bug we hit. ++ */ ++ ++static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask) ++{ ++ if (adev->class == ATA_DEV_ATA) ++ mask &= ~ATA_MASK_UDMA; ++ return mask; ++} ++ ++ ++/** ++ * serverworks_csb_filter - mode selection filter ++ * @adev: ATA device ++ * @mask: Mask of proposed modes ++ * ++ * Check the blacklist and disable UDMA5 if matched ++ */ ++ ++static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask) ++{ ++ const char *p; ++ char model_num[ATA_ID_PROD_LEN + 1]; ++ int i; ++ ++ /* Disk, UDMA */ ++ if (adev->class != ATA_DEV_ATA) ++ return mask; ++ ++ /* Actually do need to check */ ++ ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); ++ ++ for (i = 0; (p = csb_bad_ata100[i]) != NULL; i++) { ++ if (!strcmp(p, model_num)) ++ mask &= ~(0xE0 << ATA_SHIFT_UDMA); ++ } ++ return mask; ++} ++ ++/** ++ * serverworks_set_piomode - set initial PIO mode data ++ * @ap: ATA interface ++ * @adev: ATA device ++ * ++ * Program the OSB4/CSB5 timing registers for PIO. The PIO register ++ * load is done as a simple lookup. ++ */ ++static void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev) ++{ ++ static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 }; ++ int offset = 1 + 2 * ap->port_no - adev->devno; ++ int devbits = (2 * ap->port_no + adev->devno) * 4; ++ u16 csb5_pio; ++ struct pci_dev *pdev = to_pci_dev(ap->host->dev); ++ int pio = adev->pio_mode - XFER_PIO_0; ++ ++ pci_write_config_byte(pdev, 0x40 + offset, pio_mode[pio]); ++ ++ /* The OSB4 just requires the timing but the CSB series want the ++ mode number as well */ ++ if (serverworks_is_csb(pdev)) { ++ pci_read_config_word(pdev, 0x4A, &csb5_pio); ++ csb5_pio &= ~(0x0F << devbits); ++ pci_write_config_word(pdev, 0x4A, csb5_pio | (pio << devbits)); ++ } ++} ++ ++/** ++ * serverworks_set_dmamode - set initial DMA mode data ++ * @ap: ATA interface ++ * @adev: ATA device ++ * ++ * Program the MWDMA/UDMA modes for the serverworks OSB4/CSB5 ++ * chipset. The MWDMA mode values are pulled from a lookup table ++ * while the chipset uses mode number for UDMA. ++ */ ++ ++static void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev) ++{ ++ static const u8 dma_mode[] = { 0x77, 0x21, 0x20 }; ++ int offset = 1 + 2 * ap->port_no - adev->devno; ++ int devbits = 2 * ap->port_no + adev->devno; ++ u8 ultra; ++ u8 ultra_cfg; ++ struct pci_dev *pdev = to_pci_dev(ap->host->dev); ++ ++ pci_read_config_byte(pdev, 0x54, &ultra_cfg); ++ pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra); ++ ultra &= ~(0x0F << (adev->devno * 4)); ++ ++ if (adev->dma_mode >= XFER_UDMA_0) { ++ pci_write_config_byte(pdev, 0x44 + offset, 0x20); ++ ++ ultra |= (adev->dma_mode - XFER_UDMA_0) ++ << (adev->devno * 4); ++ ultra_cfg |= (1 << devbits); ++ } else { ++ pci_write_config_byte(pdev, 0x44 + offset, ++ dma_mode[adev->dma_mode - XFER_MW_DMA_0]); ++ ultra_cfg &= ~(1 << devbits); ++ } ++ pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra); ++ pci_write_config_byte(pdev, 0x54, ultra_cfg); ++} ++ ++static struct scsi_host_template serverworks_sht = { ++ ATA_BMDMA_SHT(DRV_NAME), ++}; ++ ++static struct ata_port_operations serverworks_osb4_port_ops = { ++ .inherits = &ata_bmdma_port_ops, ++ .cable_detect = serverworks_cable_detect, ++ .mode_filter = serverworks_osb4_filter, ++ .set_piomode = serverworks_set_piomode, ++ .set_dmamode = serverworks_set_dmamode, ++}; ++ ++static struct ata_port_operations serverworks_csb_port_ops = { ++ .inherits = &serverworks_osb4_port_ops, ++ .mode_filter = serverworks_csb_filter, ++}; ++ ++static int serverworks_fixup_osb4(struct pci_dev *pdev) ++{ ++ u32 reg; ++ struct pci_dev *isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, ++ PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); ++ if (isa_dev) { ++ pci_read_config_dword(isa_dev, 0x64, ®); ++ reg &= ~0x00002000; /* disable 600ns interrupt mask */ ++ if (!(reg & 0x00004000)) ++ printk(KERN_DEBUG DRV_NAME ": UDMA not BIOS enabled.\n"); ++ reg |= 0x00004000; /* enable UDMA/33 support */ ++ pci_write_config_dword(isa_dev, 0x64, reg); ++ pci_dev_put(isa_dev); ++ return 0; ++ } ++ printk(KERN_WARNING DRV_NAME ": Unable to find bridge.\n"); ++ return -ENODEV; ++} ++ ++static int serverworks_fixup_csb(struct pci_dev *pdev) ++{ ++ u8 btr; ++ ++ /* Third Channel Test */ ++ if (!(PCI_FUNC(pdev->devfn) & 1)) { ++ struct pci_dev * findev = NULL; ++ u32 reg4c = 0; ++ findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, ++ PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); ++ if (findev) { ++ pci_read_config_dword(findev, 0x4C, ®4c); ++ reg4c &= ~0x000007FF; ++ reg4c |= 0x00000040; ++ reg4c |= 0x00000020; ++ pci_write_config_dword(findev, 0x4C, reg4c); ++ pci_dev_put(findev); ++ } ++ } else { ++ struct pci_dev * findev = NULL; ++ u8 reg41 = 0; ++ ++ findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, ++ PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL); ++ if (findev) { ++ pci_read_config_byte(findev, 0x41, ®41); ++ reg41 &= ~0x40; ++ pci_write_config_byte(findev, 0x41, reg41); ++ pci_dev_put(findev); ++ } ++ } ++ /* setup the UDMA Control register ++ * ++ * 1. clear bit 6 to enable DMA ++ * 2. enable DMA modes with bits 0-1 ++ * 00 : legacy ++ * 01 : udma2 ++ * 10 : udma2/udma4 ++ * 11 : udma2/udma4/udma5 ++ */ ++ pci_read_config_byte(pdev, 0x5A, &btr); ++ btr &= ~0x40; ++ if (!(PCI_FUNC(pdev->devfn) & 1)) ++ btr |= 0x2; ++ else ++ btr |= (pdev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; ++ pci_write_config_byte(pdev, 0x5A, btr); ++ ++ return btr; ++} ++ ++static void serverworks_fixup_ht1000(struct pci_dev *pdev) ++{ ++ u8 btr; ++ /* Setup HT1000 SouthBridge Controller - Single Channel Only */ ++ pci_read_config_byte(pdev, 0x5A, &btr); ++ btr &= ~0x40; ++ btr |= 0x3; ++ pci_write_config_byte(pdev, 0x5A, btr); ++} ++ ++static int serverworks_fixup(struct pci_dev *pdev) ++{ ++ int rc = 0; ++ ++ /* Force master latency timer to 64 PCI clocks */ ++ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40); ++ ++ switch (pdev->device) { ++ case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE: ++ rc = serverworks_fixup_osb4(pdev); ++ break; ++ case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: ++ ata_pci_bmdma_clear_simplex(pdev); ++ /* fall through */ ++ case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: ++ case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: ++ rc = serverworks_fixup_csb(pdev); ++ break; ++ case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: ++ serverworks_fixup_ht1000(pdev); ++ break; ++ } ++ ++ return rc; ++} ++ ++static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ static const struct ata_port_info info[4] = { ++ { /* OSB4 */ ++ .flags = ATA_FLAG_SLAVE_POSS, ++ .pio_mask = ATA_PIO4, ++ .mwdma_mask = ATA_MWDMA2, ++ .udma_mask = ATA_UDMA2, ++ .port_ops = &serverworks_osb4_port_ops ++ }, { /* OSB4 no UDMA */ ++ .flags = ATA_FLAG_SLAVE_POSS, ++ .pio_mask = ATA_PIO4, ++ .mwdma_mask = ATA_MWDMA2, ++ /* No UDMA */ ++ .port_ops = &serverworks_osb4_port_ops ++ }, { /* CSB5 */ ++ .flags = ATA_FLAG_SLAVE_POSS, ++ .pio_mask = ATA_PIO4, ++ .mwdma_mask = ATA_MWDMA2, ++ .udma_mask = ATA_UDMA4, ++ .port_ops = &serverworks_csb_port_ops ++ }, { /* CSB5 - later revisions*/ ++ .flags = ATA_FLAG_SLAVE_POSS, ++ .pio_mask = ATA_PIO4, ++ .mwdma_mask = ATA_MWDMA2, ++ .udma_mask = ATA_UDMA5, ++ .port_ops = &serverworks_csb_port_ops ++ } ++ }; ++ const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL }; ++ int rc; ++ ++ rc = pcim_enable_device(pdev); ++ if (rc) ++ return rc; ++ ++ rc = serverworks_fixup(pdev); ++ ++ /* OSB4 : South Bridge and IDE */ ++ if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { ++ /* Select non UDMA capable OSB4 if we can't do fixups */ ++ if (rc < 0) ++ ppi[0] = &info[1]; ++ } ++ /* setup CSB5/CSB6 : South Bridge and IDE option RAID */ ++ else if ((pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) || ++ (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || ++ (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) { ++ ++ /* If the returned btr is the newer revision then ++ select the right info block */ ++ if (rc == 3) ++ ppi[0] = &info[3]; ++ ++ /* Is this the 3rd channel CSB6 IDE ? */ ++ if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2) ++ ppi[1] = &ata_dummy_port_info; ++ } ++ ++ return ata_pci_bmdma_init_one(pdev, ppi, &serverworks_sht, NULL, 0); ++} ++ ++#ifdef CONFIG_PM ++static int serverworks_reinit_one(struct pci_dev *pdev) ++{ ++ struct ata_host *host = pci_get_drvdata(pdev); ++ int rc; ++ ++ rc = ata_pci_device_do_resume(pdev); ++ if (rc) ++ return rc; ++ ++ (void)serverworks_fixup(pdev); ++ ++ ata_host_resume(host); ++ return 0; ++} ++#endif ++ ++static const struct pci_device_id serverworks[] = { ++ { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, ++ { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, ++ { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, ++ { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, ++ { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, ++ ++ { }, ++}; ++ ++static struct pci_driver serverworks_pci_driver = { ++ .name = DRV_NAME, ++ .id_table = serverworks, ++ .probe = serverworks_init_one, ++ .remove = ata_pci_remove_one, ++#ifdef CONFIG_PM ++ .suspend = ata_pci_device_suspend, ++ .resume = serverworks_reinit_one, ++#endif ++}; ++ ++module_pci_driver(serverworks_pci_driver); ++ ++MODULE_AUTHOR("Alan Cox"); ++MODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6"); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(pci, serverworks); ++MODULE_VERSION(DRV_VERSION); +diff -Nur linux-3.14.36/drivers/ata/pata_sil680.c linux-openelec/drivers/ata/pata_sil680.c +--- linux-3.14.36/drivers/ata/pata_sil680.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_sil680.c 2015-05-06 12:05:42.000000000 -0500 +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_sis.c linux-openelec/drivers/ata/pata_sis.c +--- linux-3.14.36/drivers/ata/pata_sis.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_sis.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,7 +26,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_sl82c105.c linux-openelec/drivers/ata/pata_sl82c105.c +--- linux-3.14.36/drivers/ata/pata_sl82c105.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_sl82c105.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_triflex.c linux-openelec/drivers/ata/pata_triflex.c +--- linux-3.14.36/drivers/ata/pata_triflex.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_triflex.c 2015-05-06 12:05:42.000000000 -0500 +@@ -36,7 +36,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pata_via.c linux-openelec/drivers/ata/pata_via.c +--- linux-3.14.36/drivers/ata/pata_via.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pata_via.c 2015-05-06 12:05:42.000000000 -0500 +@@ -55,7 +55,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/pdc_adma.c linux-openelec/drivers/ata/pdc_adma.c +--- linux-3.14.36/drivers/ata/pdc_adma.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/pdc_adma.c 2015-05-06 12:05:42.000000000 -0500 +@@ -36,7 +36,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_dwc_460ex.c linux-openelec/drivers/ata/sata_dwc_460ex.c +--- linux-3.14.36/drivers/ata/sata_dwc_460ex.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_dwc_460ex.c 2015-07-24 18:03:29.512842002 -0500 +@@ -29,7 +29,6 @@ + + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_dwc_460ex.c.orig linux-openelec/drivers/ata/sata_dwc_460ex.c.orig +--- linux-3.14.36/drivers/ata/sata_dwc_460ex.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/ata/sata_dwc_460ex.c.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1823 @@ ++/* ++ * drivers/ata/sata_dwc_460ex.c ++ * ++ * Synopsys DesignWare Cores (DWC) SATA host driver ++ * ++ * Author: Mark Miesfeld ++ * ++ * Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese ++ * Copyright 2008 DENX Software Engineering ++ * ++ * Based on versions provided by AMCC and Synopsys which are: ++ * Copyright 2006 Applied Micro Circuits Corporation ++ * COPYRIGHT (C) 2005 SYNOPSYS, INC. ALL RIGHTS RESERVED ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#ifdef CONFIG_SATA_DWC_DEBUG ++#define DEBUG ++#endif ++ ++#ifdef CONFIG_SATA_DWC_VDEBUG ++#define VERBOSE_DEBUG ++#define DEBUG_NCQ ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libata.h" ++ ++#include ++#include ++ ++/* These two are defined in "libata.h" */ ++#undef DRV_NAME ++#undef DRV_VERSION ++ ++#define DRV_NAME "sata-dwc" ++#define DRV_VERSION "1.3" ++ ++/* SATA DMA driver Globals */ ++#define DMA_NUM_CHANS 1 ++#define DMA_NUM_CHAN_REGS 8 ++ ++/* SATA DMA Register definitions */ ++#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length*/ ++ ++struct dmareg { ++ u32 low; /* Low bits 0-31 */ ++ u32 high; /* High bits 32-63 */ ++}; ++ ++/* DMA Per Channel registers */ ++struct dma_chan_regs { ++ struct dmareg sar; /* Source Address */ ++ struct dmareg dar; /* Destination address */ ++ struct dmareg llp; /* Linked List Pointer */ ++ struct dmareg ctl; /* Control */ ++ struct dmareg sstat; /* Source Status not implemented in core */ ++ struct dmareg dstat; /* Destination Status not implemented in core*/ ++ struct dmareg sstatar; /* Source Status Address not impl in core */ ++ struct dmareg dstatar; /* Destination Status Address not implemente */ ++ struct dmareg cfg; /* Config */ ++ struct dmareg sgr; /* Source Gather */ ++ struct dmareg dsr; /* Destination Scatter */ ++}; ++ ++/* Generic Interrupt Registers */ ++struct dma_interrupt_regs { ++ struct dmareg tfr; /* Transfer Interrupt */ ++ struct dmareg block; /* Block Interrupt */ ++ struct dmareg srctran; /* Source Transfer Interrupt */ ++ struct dmareg dsttran; /* Dest Transfer Interrupt */ ++ struct dmareg error; /* Error */ ++}; ++ ++struct ahb_dma_regs { ++ struct dma_chan_regs chan_regs[DMA_NUM_CHAN_REGS]; ++ struct dma_interrupt_regs interrupt_raw; /* Raw Interrupt */ ++ struct dma_interrupt_regs interrupt_status; /* Interrupt Status */ ++ struct dma_interrupt_regs interrupt_mask; /* Interrupt Mask */ ++ struct dma_interrupt_regs interrupt_clear; /* Interrupt Clear */ ++ struct dmareg statusInt; /* Interrupt combined*/ ++ struct dmareg rq_srcreg; /* Src Trans Req */ ++ struct dmareg rq_dstreg; /* Dst Trans Req */ ++ struct dmareg rq_sgl_srcreg; /* Sngl Src Trans Req*/ ++ struct dmareg rq_sgl_dstreg; /* Sngl Dst Trans Req*/ ++ struct dmareg rq_lst_srcreg; /* Last Src Trans Req*/ ++ struct dmareg rq_lst_dstreg; /* Last Dst Trans Req*/ ++ struct dmareg dma_cfg; /* DMA Config */ ++ struct dmareg dma_chan_en; /* DMA Channel Enable*/ ++ struct dmareg dma_id; /* DMA ID */ ++ struct dmareg dma_test; /* DMA Test */ ++ struct dmareg res1; /* reserved */ ++ struct dmareg res2; /* reserved */ ++ /* ++ * DMA Comp Params ++ * Param 6 = dma_param[0], Param 5 = dma_param[1], ++ * Param 4 = dma_param[2] ... ++ */ ++ struct dmareg dma_params[6]; ++}; ++ ++/* Data structure for linked list item */ ++struct lli { ++ u32 sar; /* Source Address */ ++ u32 dar; /* Destination address */ ++ u32 llp; /* Linked List Pointer */ ++ struct dmareg ctl; /* Control */ ++ struct dmareg dstat; /* Destination Status */ ++}; ++ ++enum { ++ SATA_DWC_DMAC_LLI_SZ = (sizeof(struct lli)), ++ SATA_DWC_DMAC_LLI_NUM = 256, ++ SATA_DWC_DMAC_LLI_TBL_SZ = (SATA_DWC_DMAC_LLI_SZ * \ ++ SATA_DWC_DMAC_LLI_NUM), ++ SATA_DWC_DMAC_TWIDTH_BYTES = 4, ++ SATA_DWC_DMAC_CTRL_TSIZE_MAX = (0x00000800 * \ ++ SATA_DWC_DMAC_TWIDTH_BYTES), ++}; ++ ++/* DMA Register Operation Bits */ ++enum { ++ DMA_EN = 0x00000001, /* Enable AHB DMA */ ++ DMA_CTL_LLP_SRCEN = 0x10000000, /* Blk chain enable Src */ ++ DMA_CTL_LLP_DSTEN = 0x08000000, /* Blk chain enable Dst */ ++}; ++ ++#define DMA_CTL_BLK_TS(size) ((size) & 0x000000FFF) /* Blk Transfer size */ ++#define DMA_CHANNEL(ch) (0x00000001 << (ch)) /* Select channel */ ++ /* Enable channel */ ++#define DMA_ENABLE_CHAN(ch) ((0x00000001 << (ch)) | \ ++ ((0x000000001 << (ch)) << 8)) ++ /* Disable channel */ ++#define DMA_DISABLE_CHAN(ch) (0x00000000 | ((0x000000001 << (ch)) << 8)) ++ /* Transfer Type & Flow Controller */ ++#define DMA_CTL_TTFC(type) (((type) & 0x7) << 20) ++#define DMA_CTL_SMS(num) (((num) & 0x3) << 25) /* Src Master Select */ ++#define DMA_CTL_DMS(num) (((num) & 0x3) << 23)/* Dst Master Select */ ++ /* Src Burst Transaction Length */ ++#define DMA_CTL_SRC_MSIZE(size) (((size) & 0x7) << 14) ++ /* Dst Burst Transaction Length */ ++#define DMA_CTL_DST_MSIZE(size) (((size) & 0x7) << 11) ++ /* Source Transfer Width */ ++#define DMA_CTL_SRC_TRWID(size) (((size) & 0x7) << 4) ++ /* Destination Transfer Width */ ++#define DMA_CTL_DST_TRWID(size) (((size) & 0x7) << 1) ++ ++/* Assign HW handshaking interface (x) to destination / source peripheral */ ++#define DMA_CFG_HW_HS_DEST(int_num) (((int_num) & 0xF) << 11) ++#define DMA_CFG_HW_HS_SRC(int_num) (((int_num) & 0xF) << 7) ++#define DMA_CFG_HW_CH_PRIOR(int_num) (((int_num) & 0xF) << 5) ++#define DMA_LLP_LMS(addr, master) (((addr) & 0xfffffffc) | (master)) ++ ++/* ++ * This define is used to set block chaining disabled in the control low ++ * register. It is already in little endian format so it can be &'d dirctly. ++ * It is essentially: cpu_to_le32(~(DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN)) ++ */ ++enum { ++ DMA_CTL_LLP_DISABLE_LE32 = 0xffffffe7, ++ DMA_CTL_TTFC_P2M_DMAC = 0x00000002, /* Per to mem, DMAC cntr */ ++ DMA_CTL_TTFC_M2P_PER = 0x00000003, /* Mem to per, peripheral cntr */ ++ DMA_CTL_SINC_INC = 0x00000000, /* Source Address Increment */ ++ DMA_CTL_SINC_DEC = 0x00000200, ++ DMA_CTL_SINC_NOCHANGE = 0x00000400, ++ DMA_CTL_DINC_INC = 0x00000000, /* Destination Address Increment */ ++ DMA_CTL_DINC_DEC = 0x00000080, ++ DMA_CTL_DINC_NOCHANGE = 0x00000100, ++ DMA_CTL_INT_EN = 0x00000001, /* Interrupt Enable */ ++ ++/* Channel Configuration Register high bits */ ++ DMA_CFG_FCMOD_REQ = 0x00000001, /* Flow Control - request based */ ++ DMA_CFG_PROTCTL = (0x00000003 << 2),/* Protection Control */ ++ ++/* Channel Configuration Register low bits */ ++ DMA_CFG_RELD_DST = 0x80000000, /* Reload Dest / Src Addr */ ++ DMA_CFG_RELD_SRC = 0x40000000, ++ DMA_CFG_HS_SELSRC = 0x00000800, /* Software handshake Src/ Dest */ ++ DMA_CFG_HS_SELDST = 0x00000400, ++ DMA_CFG_FIFOEMPTY = (0x00000001 << 9), /* FIFO Empty bit */ ++ ++/* Channel Linked List Pointer Register */ ++ DMA_LLP_AHBMASTER1 = 0, /* List Master Select */ ++ DMA_LLP_AHBMASTER2 = 1, ++ ++ SATA_DWC_MAX_PORTS = 1, ++ ++ SATA_DWC_SCR_OFFSET = 0x24, ++ SATA_DWC_REG_OFFSET = 0x64, ++}; ++ ++/* DWC SATA Registers */ ++struct sata_dwc_regs { ++ u32 fptagr; /* 1st party DMA tag */ ++ u32 fpbor; /* 1st party DMA buffer offset */ ++ u32 fptcr; /* 1st party DMA Xfr count */ ++ u32 dmacr; /* DMA Control */ ++ u32 dbtsr; /* DMA Burst Transac size */ ++ u32 intpr; /* Interrupt Pending */ ++ u32 intmr; /* Interrupt Mask */ ++ u32 errmr; /* Error Mask */ ++ u32 llcr; /* Link Layer Control */ ++ u32 phycr; /* PHY Control */ ++ u32 physr; /* PHY Status */ ++ u32 rxbistpd; /* Recvd BIST pattern def register */ ++ u32 rxbistpd1; /* Recvd BIST data dword1 */ ++ u32 rxbistpd2; /* Recvd BIST pattern data dword2 */ ++ u32 txbistpd; /* Trans BIST pattern def register */ ++ u32 txbistpd1; /* Trans BIST data dword1 */ ++ u32 txbistpd2; /* Trans BIST data dword2 */ ++ u32 bistcr; /* BIST Control Register */ ++ u32 bistfctr; /* BIST FIS Count Register */ ++ u32 bistsr; /* BIST Status Register */ ++ u32 bistdecr; /* BIST Dword Error count register */ ++ u32 res[15]; /* Reserved locations */ ++ u32 testr; /* Test Register */ ++ u32 versionr; /* Version Register */ ++ u32 idr; /* ID Register */ ++ u32 unimpl[192]; /* Unimplemented */ ++ u32 dmadr[256]; /* FIFO Locations in DMA Mode */ ++}; ++ ++enum { ++ SCR_SCONTROL_DET_ENABLE = 0x00000001, ++ SCR_SSTATUS_DET_PRESENT = 0x00000001, ++ SCR_SERROR_DIAG_X = 0x04000000, ++/* DWC SATA Register Operations */ ++ SATA_DWC_TXFIFO_DEPTH = 0x01FF, ++ SATA_DWC_RXFIFO_DEPTH = 0x01FF, ++ SATA_DWC_DMACR_TMOD_TXCHEN = 0x00000004, ++ SATA_DWC_DMACR_TXCHEN = (0x00000001 | SATA_DWC_DMACR_TMOD_TXCHEN), ++ SATA_DWC_DMACR_RXCHEN = (0x00000002 | SATA_DWC_DMACR_TMOD_TXCHEN), ++ SATA_DWC_DMACR_TXRXCH_CLEAR = SATA_DWC_DMACR_TMOD_TXCHEN, ++ SATA_DWC_INTPR_DMAT = 0x00000001, ++ SATA_DWC_INTPR_NEWFP = 0x00000002, ++ SATA_DWC_INTPR_PMABRT = 0x00000004, ++ SATA_DWC_INTPR_ERR = 0x00000008, ++ SATA_DWC_INTPR_NEWBIST = 0x00000010, ++ SATA_DWC_INTPR_IPF = 0x10000000, ++ SATA_DWC_INTMR_DMATM = 0x00000001, ++ SATA_DWC_INTMR_NEWFPM = 0x00000002, ++ SATA_DWC_INTMR_PMABRTM = 0x00000004, ++ SATA_DWC_INTMR_ERRM = 0x00000008, ++ SATA_DWC_INTMR_NEWBISTM = 0x00000010, ++ SATA_DWC_LLCR_SCRAMEN = 0x00000001, ++ SATA_DWC_LLCR_DESCRAMEN = 0x00000002, ++ SATA_DWC_LLCR_RPDEN = 0x00000004, ++/* This is all error bits, zero's are reserved fields. */ ++ SATA_DWC_SERROR_ERR_BITS = 0x0FFF0F03 ++}; ++ ++#define SATA_DWC_SCR0_SPD_GET(v) (((v) >> 4) & 0x0000000F) ++#define SATA_DWC_DMACR_TX_CLEAR(v) (((v) & ~SATA_DWC_DMACR_TXCHEN) |\ ++ SATA_DWC_DMACR_TMOD_TXCHEN) ++#define SATA_DWC_DMACR_RX_CLEAR(v) (((v) & ~SATA_DWC_DMACR_RXCHEN) |\ ++ SATA_DWC_DMACR_TMOD_TXCHEN) ++#define SATA_DWC_DBTSR_MWR(size) (((size)/4) & SATA_DWC_TXFIFO_DEPTH) ++#define SATA_DWC_DBTSR_MRD(size) ((((size)/4) & SATA_DWC_RXFIFO_DEPTH)\ ++ << 16) ++struct sata_dwc_device { ++ struct device *dev; /* generic device struct */ ++ struct ata_probe_ent *pe; /* ptr to probe-ent */ ++ struct ata_host *host; ++ u8 *reg_base; ++ struct sata_dwc_regs *sata_dwc_regs; /* DW Synopsys SATA specific */ ++ int irq_dma; ++}; ++ ++#define SATA_DWC_QCMD_MAX 32 ++ ++struct sata_dwc_device_port { ++ struct sata_dwc_device *hsdev; ++ int cmd_issued[SATA_DWC_QCMD_MAX]; ++ struct lli *llit[SATA_DWC_QCMD_MAX]; /* DMA LLI table */ ++ dma_addr_t llit_dma[SATA_DWC_QCMD_MAX]; ++ u32 dma_chan[SATA_DWC_QCMD_MAX]; ++ int dma_pending[SATA_DWC_QCMD_MAX]; ++}; ++ ++/* ++ * Commonly used DWC SATA driver Macros ++ */ ++#define HSDEV_FROM_HOST(host) ((struct sata_dwc_device *)\ ++ (host)->private_data) ++#define HSDEV_FROM_AP(ap) ((struct sata_dwc_device *)\ ++ (ap)->host->private_data) ++#define HSDEVP_FROM_AP(ap) ((struct sata_dwc_device_port *)\ ++ (ap)->private_data) ++#define HSDEV_FROM_QC(qc) ((struct sata_dwc_device *)\ ++ (qc)->ap->host->private_data) ++#define HSDEV_FROM_HSDEVP(p) ((struct sata_dwc_device *)\ ++ (hsdevp)->hsdev) ++ ++enum { ++ SATA_DWC_CMD_ISSUED_NOT = 0, ++ SATA_DWC_CMD_ISSUED_PEND = 1, ++ SATA_DWC_CMD_ISSUED_EXEC = 2, ++ SATA_DWC_CMD_ISSUED_NODATA = 3, ++ ++ SATA_DWC_DMA_PENDING_NONE = 0, ++ SATA_DWC_DMA_PENDING_TX = 1, ++ SATA_DWC_DMA_PENDING_RX = 2, ++}; ++ ++struct sata_dwc_host_priv { ++ void __iomem *scr_addr_sstatus; ++ u32 sata_dwc_sactive_issued ; ++ u32 sata_dwc_sactive_queued ; ++ u32 dma_interrupt_count; ++ struct ahb_dma_regs *sata_dma_regs; ++ struct device *dwc_dev; ++ int dma_channel; ++}; ++struct sata_dwc_host_priv host_pvt; ++/* ++ * Prototypes ++ */ ++static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag); ++static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc, ++ u32 check_status); ++static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status); ++static void sata_dwc_port_stop(struct ata_port *ap); ++static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag); ++static int dma_dwc_init(struct sata_dwc_device *hsdev, int irq); ++static void dma_dwc_exit(struct sata_dwc_device *hsdev); ++static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems, ++ struct lli *lli, dma_addr_t dma_lli, ++ void __iomem *addr, int dir); ++static void dma_dwc_xfer_start(int dma_ch); ++ ++static const char *get_prot_descript(u8 protocol) ++{ ++ switch ((enum ata_tf_protocols)protocol) { ++ case ATA_PROT_NODATA: ++ return "ATA no data"; ++ case ATA_PROT_PIO: ++ return "ATA PIO"; ++ case ATA_PROT_DMA: ++ return "ATA DMA"; ++ case ATA_PROT_NCQ: ++ return "ATA NCQ"; ++ case ATAPI_PROT_NODATA: ++ return "ATAPI no data"; ++ case ATAPI_PROT_PIO: ++ return "ATAPI PIO"; ++ case ATAPI_PROT_DMA: ++ return "ATAPI DMA"; ++ default: ++ return "unknown"; ++ } ++} ++ ++static const char *get_dma_dir_descript(int dma_dir) ++{ ++ switch ((enum dma_data_direction)dma_dir) { ++ case DMA_BIDIRECTIONAL: ++ return "bidirectional"; ++ case DMA_TO_DEVICE: ++ return "to device"; ++ case DMA_FROM_DEVICE: ++ return "from device"; ++ default: ++ return "none"; ++ } ++} ++ ++static void sata_dwc_tf_dump(struct ata_taskfile *tf) ++{ ++ dev_vdbg(host_pvt.dwc_dev, "taskfile cmd: 0x%02x protocol: %s flags:" ++ "0x%lx device: %x\n", tf->command, ++ get_prot_descript(tf->protocol), tf->flags, tf->device); ++ dev_vdbg(host_pvt.dwc_dev, "feature: 0x%02x nsect: 0x%x lbal: 0x%x " ++ "lbam: 0x%x lbah: 0x%x\n", tf->feature, tf->nsect, tf->lbal, ++ tf->lbam, tf->lbah); ++ dev_vdbg(host_pvt.dwc_dev, "hob_feature: 0x%02x hob_nsect: 0x%x " ++ "hob_lbal: 0x%x hob_lbam: 0x%x hob_lbah: 0x%x\n", ++ tf->hob_feature, tf->hob_nsect, tf->hob_lbal, tf->hob_lbam, ++ tf->hob_lbah); ++} ++ ++/* ++ * Function: get_burst_length_encode ++ * arguments: datalength: length in bytes of data ++ * returns value to be programmed in register corresponding to data length ++ * This value is effectively the log(base 2) of the length ++ */ ++static int get_burst_length_encode(int datalength) ++{ ++ int items = datalength >> 2; /* div by 4 to get lword count */ ++ ++ if (items >= 64) ++ return 5; ++ ++ if (items >= 32) ++ return 4; ++ ++ if (items >= 16) ++ return 3; ++ ++ if (items >= 8) ++ return 2; ++ ++ if (items >= 4) ++ return 1; ++ ++ return 0; ++} ++ ++static void clear_chan_interrupts(int c) ++{ ++ out_le32(&(host_pvt.sata_dma_regs->interrupt_clear.tfr.low), ++ DMA_CHANNEL(c)); ++ out_le32(&(host_pvt.sata_dma_regs->interrupt_clear.block.low), ++ DMA_CHANNEL(c)); ++ out_le32(&(host_pvt.sata_dma_regs->interrupt_clear.srctran.low), ++ DMA_CHANNEL(c)); ++ out_le32(&(host_pvt.sata_dma_regs->interrupt_clear.dsttran.low), ++ DMA_CHANNEL(c)); ++ out_le32(&(host_pvt.sata_dma_regs->interrupt_clear.error.low), ++ DMA_CHANNEL(c)); ++} ++ ++/* ++ * Function: dma_request_channel ++ * arguments: None ++ * returns channel number if available else -1 ++ * This function assigns the next available DMA channel from the list to the ++ * requester ++ */ ++static int dma_request_channel(void) ++{ ++ /* Check if the channel is not currently in use */ ++ if (!(in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) & ++ DMA_CHANNEL(host_pvt.dma_channel))) ++ return host_pvt.dma_channel; ++ dev_err(host_pvt.dwc_dev, "%s Channel %d is currently in use\n", ++ __func__, host_pvt.dma_channel); ++ return -1; ++} ++ ++/* ++ * Function: dma_dwc_interrupt ++ * arguments: irq, dev_id, pt_regs ++ * returns channel number if available else -1 ++ * Interrupt Handler for DW AHB SATA DMA ++ */ ++static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) ++{ ++ int chan; ++ u32 tfr_reg, err_reg; ++ unsigned long flags; ++ struct sata_dwc_device *hsdev = ++ (struct sata_dwc_device *)hsdev_instance; ++ struct ata_host *host = (struct ata_host *)hsdev->host; ++ struct ata_port *ap; ++ struct sata_dwc_device_port *hsdevp; ++ u8 tag = 0; ++ unsigned int port = 0; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ap = host->ports[port]; ++ hsdevp = HSDEVP_FROM_AP(ap); ++ tag = ap->link.active_tag; ++ ++ tfr_reg = in_le32(&(host_pvt.sata_dma_regs->interrupt_status.tfr\ ++ .low)); ++ err_reg = in_le32(&(host_pvt.sata_dma_regs->interrupt_status.error\ ++ .low)); ++ ++ dev_dbg(ap->dev, "eot=0x%08x err=0x%08x pending=%d active port=%d\n", ++ tfr_reg, err_reg, hsdevp->dma_pending[tag], port); ++ ++ chan = host_pvt.dma_channel; ++ if (chan >= 0) { ++ /* Check for end-of-transfer interrupt. */ ++ if (tfr_reg & DMA_CHANNEL(chan)) { ++ /* ++ * Each DMA command produces 2 interrupts. Only ++ * complete the command after both interrupts have been ++ * seen. (See sata_dwc_isr()) ++ */ ++ host_pvt.dma_interrupt_count++; ++ sata_dwc_clear_dmacr(hsdevp, tag); ++ ++ if (hsdevp->dma_pending[tag] == ++ SATA_DWC_DMA_PENDING_NONE) { ++ dev_err(ap->dev, "DMA not pending eot=0x%08x " ++ "err=0x%08x tag=0x%02x pending=%d\n", ++ tfr_reg, err_reg, tag, ++ hsdevp->dma_pending[tag]); ++ } ++ ++ if ((host_pvt.dma_interrupt_count % 2) == 0) ++ sata_dwc_dma_xfer_complete(ap, 1); ++ ++ /* Clear the interrupt */ ++ out_le32(&(host_pvt.sata_dma_regs->interrupt_clear\ ++ .tfr.low), ++ DMA_CHANNEL(chan)); ++ } ++ ++ /* Check for error interrupt. */ ++ if (err_reg & DMA_CHANNEL(chan)) { ++ /* TODO Need error handler ! */ ++ dev_err(ap->dev, "error interrupt err_reg=0x%08x\n", ++ err_reg); ++ ++ /* Clear the interrupt. */ ++ out_le32(&(host_pvt.sata_dma_regs->interrupt_clear\ ++ .error.low), ++ DMA_CHANNEL(chan)); ++ } ++ } ++ spin_unlock_irqrestore(&host->lock, flags); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Function: dma_request_interrupts ++ * arguments: hsdev ++ * returns status ++ * This function registers ISR for a particular DMA channel interrupt ++ */ ++static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq) ++{ ++ int retval = 0; ++ int chan = host_pvt.dma_channel; ++ ++ if (chan >= 0) { ++ /* Unmask error interrupt */ ++ out_le32(&(host_pvt.sata_dma_regs)->interrupt_mask.error.low, ++ DMA_ENABLE_CHAN(chan)); ++ ++ /* Unmask end-of-transfer interrupt */ ++ out_le32(&(host_pvt.sata_dma_regs)->interrupt_mask.tfr.low, ++ DMA_ENABLE_CHAN(chan)); ++ } ++ ++ retval = request_irq(irq, dma_dwc_interrupt, 0, "SATA DMA", hsdev); ++ if (retval) { ++ dev_err(host_pvt.dwc_dev, "%s: could not get IRQ %d\n", ++ __func__, irq); ++ return -ENODEV; ++ } ++ ++ /* Mark this interrupt as requested */ ++ hsdev->irq_dma = irq; ++ return 0; ++} ++ ++/* ++ * Function: map_sg_to_lli ++ * The Synopsis driver has a comment proposing that better performance ++ * is possible by only enabling interrupts on the last item in the linked list. ++ * However, it seems that could be a problem if an error happened on one of the ++ * first items. The transfer would halt, but no error interrupt would occur. ++ * Currently this function sets interrupts enabled for each linked list item: ++ * DMA_CTL_INT_EN. ++ */ ++static int map_sg_to_lli(struct scatterlist *sg, int num_elems, ++ struct lli *lli, dma_addr_t dma_lli, ++ void __iomem *dmadr_addr, int dir) ++{ ++ int i, idx = 0; ++ int fis_len = 0; ++ dma_addr_t next_llp; ++ int bl; ++ int sms_val, dms_val; ++ ++ sms_val = 0; ++ dms_val = 1 + host_pvt.dma_channel; ++ dev_dbg(host_pvt.dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x" ++ " dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli, ++ (u32)dmadr_addr); ++ ++ bl = get_burst_length_encode(AHB_DMA_BRST_DFLT); ++ ++ for (i = 0; i < num_elems; i++, sg++) { ++ u32 addr, offset; ++ u32 sg_len, len; ++ ++ addr = (u32) sg_dma_address(sg); ++ sg_len = sg_dma_len(sg); ++ ++ dev_dbg(host_pvt.dwc_dev, "%s: elem=%d sg_addr=0x%x sg_len" ++ "=%d\n", __func__, i, addr, sg_len); ++ ++ while (sg_len) { ++ if (idx >= SATA_DWC_DMAC_LLI_NUM) { ++ /* The LLI table is not large enough. */ ++ dev_err(host_pvt.dwc_dev, "LLI table overrun " ++ "(idx=%d)\n", idx); ++ break; ++ } ++ len = (sg_len > SATA_DWC_DMAC_CTRL_TSIZE_MAX) ? ++ SATA_DWC_DMAC_CTRL_TSIZE_MAX : sg_len; ++ ++ offset = addr & 0xffff; ++ if ((offset + sg_len) > 0x10000) ++ len = 0x10000 - offset; ++ ++ /* ++ * Make sure a LLI block is not created that will span ++ * 8K max FIS boundary. If the block spans such a FIS ++ * boundary, there is a chance that a DMA burst will ++ * cross that boundary -- this results in an error in ++ * the host controller. ++ */ ++ if (fis_len + len > 8192) { ++ dev_dbg(host_pvt.dwc_dev, "SPLITTING: fis_len=" ++ "%d(0x%x) len=%d(0x%x)\n", fis_len, ++ fis_len, len, len); ++ len = 8192 - fis_len; ++ fis_len = 0; ++ } else { ++ fis_len += len; ++ } ++ if (fis_len == 8192) ++ fis_len = 0; ++ ++ /* ++ * Set DMA addresses and lower half of control register ++ * based on direction. ++ */ ++ if (dir == DMA_FROM_DEVICE) { ++ lli[idx].dar = cpu_to_le32(addr); ++ lli[idx].sar = cpu_to_le32((u32)dmadr_addr); ++ ++ lli[idx].ctl.low = cpu_to_le32( ++ DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) | ++ DMA_CTL_SMS(sms_val) | ++ DMA_CTL_DMS(dms_val) | ++ DMA_CTL_SRC_MSIZE(bl) | ++ DMA_CTL_DST_MSIZE(bl) | ++ DMA_CTL_SINC_NOCHANGE | ++ DMA_CTL_SRC_TRWID(2) | ++ DMA_CTL_DST_TRWID(2) | ++ DMA_CTL_INT_EN | ++ DMA_CTL_LLP_SRCEN | ++ DMA_CTL_LLP_DSTEN); ++ } else { /* DMA_TO_DEVICE */ ++ lli[idx].sar = cpu_to_le32(addr); ++ lli[idx].dar = cpu_to_le32((u32)dmadr_addr); ++ ++ lli[idx].ctl.low = cpu_to_le32( ++ DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) | ++ DMA_CTL_SMS(dms_val) | ++ DMA_CTL_DMS(sms_val) | ++ DMA_CTL_SRC_MSIZE(bl) | ++ DMA_CTL_DST_MSIZE(bl) | ++ DMA_CTL_DINC_NOCHANGE | ++ DMA_CTL_SRC_TRWID(2) | ++ DMA_CTL_DST_TRWID(2) | ++ DMA_CTL_INT_EN | ++ DMA_CTL_LLP_SRCEN | ++ DMA_CTL_LLP_DSTEN); ++ } ++ ++ dev_dbg(host_pvt.dwc_dev, "%s setting ctl.high len: " ++ "0x%08x val: 0x%08x\n", __func__, ++ len, DMA_CTL_BLK_TS(len / 4)); ++ ++ /* Program the LLI CTL high register */ ++ lli[idx].ctl.high = cpu_to_le32(DMA_CTL_BLK_TS\ ++ (len / 4)); ++ ++ /* Program the next pointer. The next pointer must be ++ * the physical address, not the virtual address. ++ */ ++ next_llp = (dma_lli + ((idx + 1) * sizeof(struct \ ++ lli))); ++ ++ /* The last 2 bits encode the list master select. */ ++ next_llp = DMA_LLP_LMS(next_llp, DMA_LLP_AHBMASTER2); ++ ++ lli[idx].llp = cpu_to_le32(next_llp); ++ idx++; ++ sg_len -= len; ++ addr += len; ++ } ++ } ++ ++ /* ++ * The last next ptr has to be zero and the last control low register ++ * has to have LLP_SRC_EN and LLP_DST_EN (linked list pointer source ++ * and destination enable) set back to 0 (disabled.) This is what tells ++ * the core that this is the last item in the linked list. ++ */ ++ if (idx) { ++ lli[idx-1].llp = 0x00000000; ++ lli[idx-1].ctl.low &= DMA_CTL_LLP_DISABLE_LE32; ++ ++ /* Flush cache to memory */ ++ dma_cache_sync(NULL, lli, (sizeof(struct lli) * idx), ++ DMA_BIDIRECTIONAL); ++ } ++ ++ return idx; ++} ++ ++/* ++ * Function: dma_dwc_xfer_start ++ * arguments: Channel number ++ * Return : None ++ * Enables the DMA channel ++ */ ++static void dma_dwc_xfer_start(int dma_ch) ++{ ++ /* Enable the DMA channel */ ++ out_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low), ++ in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) | ++ DMA_ENABLE_CHAN(dma_ch)); ++} ++ ++static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems, ++ struct lli *lli, dma_addr_t dma_lli, ++ void __iomem *addr, int dir) ++{ ++ int dma_ch; ++ int num_lli; ++ /* Acquire DMA channel */ ++ dma_ch = dma_request_channel(); ++ if (dma_ch == -1) { ++ dev_err(host_pvt.dwc_dev, "%s: dma channel unavailable\n", ++ __func__); ++ return -EAGAIN; ++ } ++ ++ /* Convert SG list to linked list of items (LLIs) for AHB DMA */ ++ num_lli = map_sg_to_lli(sg, num_elems, lli, dma_lli, addr, dir); ++ ++ dev_dbg(host_pvt.dwc_dev, "%s sg: 0x%p, count: %d lli: %p dma_lli:" ++ " 0x%0xlx addr: %p lli count: %d\n", __func__, sg, num_elems, ++ lli, (u32)dma_lli, addr, num_lli); ++ ++ clear_chan_interrupts(dma_ch); ++ ++ /* Program the CFG register. */ ++ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.high), ++ DMA_CFG_HW_HS_SRC(dma_ch) | DMA_CFG_HW_HS_DEST(dma_ch) | ++ DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ); ++ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.low), ++ DMA_CFG_HW_CH_PRIOR(dma_ch)); ++ ++ /* Program the address of the linked list */ ++ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].llp.low), ++ DMA_LLP_LMS(dma_lli, DMA_LLP_AHBMASTER2)); ++ ++ /* Program the CTL register with src enable / dst enable */ ++ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].ctl.low), ++ DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN); ++ return dma_ch; ++} ++ ++/* ++ * Function: dma_dwc_exit ++ * arguments: None ++ * returns status ++ * This function exits the SATA DMA driver ++ */ ++static void dma_dwc_exit(struct sata_dwc_device *hsdev) ++{ ++ dev_dbg(host_pvt.dwc_dev, "%s:\n", __func__); ++ if (host_pvt.sata_dma_regs) { ++ iounmap(host_pvt.sata_dma_regs); ++ host_pvt.sata_dma_regs = NULL; ++ } ++ ++ if (hsdev->irq_dma) { ++ free_irq(hsdev->irq_dma, hsdev); ++ hsdev->irq_dma = 0; ++ } ++} ++ ++/* ++ * Function: dma_dwc_init ++ * arguments: hsdev ++ * returns status ++ * This function initializes the SATA DMA driver ++ */ ++static int dma_dwc_init(struct sata_dwc_device *hsdev, int irq) ++{ ++ int err; ++ ++ err = dma_request_interrupts(hsdev, irq); ++ if (err) { ++ dev_err(host_pvt.dwc_dev, "%s: dma_request_interrupts returns" ++ " %d\n", __func__, err); ++ goto error_out; ++ } ++ ++ /* Enabe DMA */ ++ out_le32(&(host_pvt.sata_dma_regs->dma_cfg.low), DMA_EN); ++ ++ dev_notice(host_pvt.dwc_dev, "DMA initialized\n"); ++ dev_dbg(host_pvt.dwc_dev, "SATA DMA registers=0x%p\n", host_pvt.\ ++ sata_dma_regs); ++ ++ return 0; ++ ++error_out: ++ dma_dwc_exit(hsdev); ++ ++ return err; ++} ++ ++static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val) ++{ ++ if (scr > SCR_NOTIFICATION) { ++ dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n", ++ __func__, scr); ++ return -EINVAL; ++ } ++ ++ *val = in_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4)); ++ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", ++ __func__, link->ap->print_id, scr, *val); ++ ++ return 0; ++} ++ ++static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val) ++{ ++ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", ++ __func__, link->ap->print_id, scr, val); ++ if (scr > SCR_NOTIFICATION) { ++ dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n", ++ __func__, scr); ++ return -EINVAL; ++ } ++ out_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4), val); ++ ++ return 0; ++} ++ ++static u32 core_scr_read(unsigned int scr) ++{ ++ return in_le32((void __iomem *)(host_pvt.scr_addr_sstatus) +\ ++ (scr * 4)); ++} ++ ++static void core_scr_write(unsigned int scr, u32 val) ++{ ++ out_le32((void __iomem *)(host_pvt.scr_addr_sstatus) + (scr * 4), ++ val); ++} ++ ++static void clear_serror(void) ++{ ++ u32 val; ++ val = core_scr_read(SCR_ERROR); ++ core_scr_write(SCR_ERROR, val); ++ ++} ++ ++static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) ++{ ++ out_le32(&hsdev->sata_dwc_regs->intpr, ++ in_le32(&hsdev->sata_dwc_regs->intpr)); ++} ++ ++static u32 qcmd_tag_to_mask(u8 tag) ++{ ++ return 0x00000001 << (tag & 0x1f); ++} ++ ++/* See ahci.c */ ++static void sata_dwc_error_intr(struct ata_port *ap, ++ struct sata_dwc_device *hsdev, uint intpr) ++{ ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ struct ata_eh_info *ehi = &ap->link.eh_info; ++ unsigned int err_mask = 0, action = 0; ++ struct ata_queued_cmd *qc; ++ u32 serror; ++ u8 status, tag; ++ u32 err_reg; ++ ++ ata_ehi_clear_desc(ehi); ++ ++ serror = core_scr_read(SCR_ERROR); ++ status = ap->ops->sff_check_status(ap); ++ ++ err_reg = in_le32(&(host_pvt.sata_dma_regs->interrupt_status.error.\ ++ low)); ++ tag = ap->link.active_tag; ++ ++ dev_err(ap->dev, "%s SCR_ERROR=0x%08x intpr=0x%08x status=0x%08x " ++ "dma_intp=%d pending=%d issued=%d dma_err_status=0x%08x\n", ++ __func__, serror, intpr, status, host_pvt.dma_interrupt_count, ++ hsdevp->dma_pending[tag], hsdevp->cmd_issued[tag], err_reg); ++ ++ /* Clear error register and interrupt bit */ ++ clear_serror(); ++ clear_interrupt_bit(hsdev, SATA_DWC_INTPR_ERR); ++ ++ /* This is the only error happening now. TODO check for exact error */ ++ ++ err_mask |= AC_ERR_HOST_BUS; ++ action |= ATA_EH_RESET; ++ ++ /* Pass this on to EH */ ++ ehi->serror |= serror; ++ ehi->action |= action; ++ ++ qc = ata_qc_from_tag(ap, tag); ++ if (qc) ++ qc->err_mask |= err_mask; ++ else ++ ehi->err_mask |= err_mask; ++ ++ ata_port_abort(ap); ++} ++ ++/* ++ * Function : sata_dwc_isr ++ * arguments : irq, void *dev_instance, struct pt_regs *regs ++ * Return value : irqreturn_t - status of IRQ ++ * This Interrupt handler called via port ops registered function. ++ * .irq_handler = sata_dwc_isr ++ */ ++static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) ++{ ++ struct ata_host *host = (struct ata_host *)dev_instance; ++ struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host); ++ struct ata_port *ap; ++ struct ata_queued_cmd *qc; ++ unsigned long flags; ++ u8 status, tag; ++ int handled, num_processed, port = 0; ++ uint intpr, sactive, sactive2, tag_mask; ++ struct sata_dwc_device_port *hsdevp; ++ host_pvt.sata_dwc_sactive_issued = 0; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ /* Read the interrupt register */ ++ intpr = in_le32(&hsdev->sata_dwc_regs->intpr); ++ ++ ap = host->ports[port]; ++ hsdevp = HSDEVP_FROM_AP(ap); ++ ++ dev_dbg(ap->dev, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr, ++ ap->link.active_tag); ++ ++ /* Check for error interrupt */ ++ if (intpr & SATA_DWC_INTPR_ERR) { ++ sata_dwc_error_intr(ap, hsdev, intpr); ++ handled = 1; ++ goto DONE; ++ } ++ ++ /* Check for DMA SETUP FIS (FP DMA) interrupt */ ++ if (intpr & SATA_DWC_INTPR_NEWFP) { ++ clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP); ++ ++ tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr)); ++ dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag); ++ if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND) ++ dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag); ++ ++ host_pvt.sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag); ++ ++ qc = ata_qc_from_tag(ap, tag); ++ /* ++ * Start FP DMA for NCQ command. At this point the tag is the ++ * active tag. It is the tag that matches the command about to ++ * be completed. ++ */ ++ qc->ap->link.active_tag = tag; ++ sata_dwc_bmdma_start_by_tag(qc, tag); ++ ++ handled = 1; ++ goto DONE; ++ } ++ sactive = core_scr_read(SCR_ACTIVE); ++ tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive; ++ ++ /* If no sactive issued and tag_mask is zero then this is not NCQ */ ++ if (host_pvt.sata_dwc_sactive_issued == 0 && tag_mask == 0) { ++ if (ap->link.active_tag == ATA_TAG_POISON) ++ tag = 0; ++ else ++ tag = ap->link.active_tag; ++ qc = ata_qc_from_tag(ap, tag); ++ ++ /* DEV interrupt w/ no active qc? */ ++ if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) { ++ dev_err(ap->dev, "%s interrupt with no active qc " ++ "qc=%p\n", __func__, qc); ++ ap->ops->sff_check_status(ap); ++ handled = 1; ++ goto DONE; ++ } ++ status = ap->ops->sff_check_status(ap); ++ ++ qc->ap->link.active_tag = tag; ++ hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT; ++ ++ if (status & ATA_ERR) { ++ dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status); ++ sata_dwc_qc_complete(ap, qc, 1); ++ handled = 1; ++ goto DONE; ++ } ++ ++ dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n", ++ __func__, get_prot_descript(qc->tf.protocol)); ++DRVSTILLBUSY: ++ if (ata_is_dma(qc->tf.protocol)) { ++ /* ++ * Each DMA transaction produces 2 interrupts. The DMAC ++ * transfer complete interrupt and the SATA controller ++ * operation done interrupt. The command should be ++ * completed only after both interrupts are seen. ++ */ ++ host_pvt.dma_interrupt_count++; ++ if (hsdevp->dma_pending[tag] == \ ++ SATA_DWC_DMA_PENDING_NONE) { ++ dev_err(ap->dev, "%s: DMA not pending " ++ "intpr=0x%08x status=0x%08x pending" ++ "=%d\n", __func__, intpr, status, ++ hsdevp->dma_pending[tag]); ++ } ++ ++ if ((host_pvt.dma_interrupt_count % 2) == 0) ++ sata_dwc_dma_xfer_complete(ap, 1); ++ } else if (ata_is_pio(qc->tf.protocol)) { ++ ata_sff_hsm_move(ap, qc, status, 0); ++ handled = 1; ++ goto DONE; ++ } else { ++ if (unlikely(sata_dwc_qc_complete(ap, qc, 1))) ++ goto DRVSTILLBUSY; ++ } ++ ++ handled = 1; ++ goto DONE; ++ } ++ ++ /* ++ * This is a NCQ command. At this point we need to figure out for which ++ * tags we have gotten a completion interrupt. One interrupt may serve ++ * as completion for more than one operation when commands are queued ++ * (NCQ). We need to process each completed command. ++ */ ++ ++ /* process completed commands */ ++ sactive = core_scr_read(SCR_ACTIVE); ++ tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive; ++ ++ if (sactive != 0 || (host_pvt.sata_dwc_sactive_issued) > 1 || \ ++ tag_mask > 1) { ++ dev_dbg(ap->dev, "%s NCQ:sactive=0x%08x sactive_issued=0x%08x" ++ "tag_mask=0x%08x\n", __func__, sactive, ++ host_pvt.sata_dwc_sactive_issued, tag_mask); ++ } ++ ++ if ((tag_mask | (host_pvt.sata_dwc_sactive_issued)) != \ ++ (host_pvt.sata_dwc_sactive_issued)) { ++ dev_warn(ap->dev, "Bad tag mask? sactive=0x%08x " ++ "(host_pvt.sata_dwc_sactive_issued)=0x%08x tag_mask" ++ "=0x%08x\n", sactive, host_pvt.sata_dwc_sactive_issued, ++ tag_mask); ++ } ++ ++ /* read just to clear ... not bad if currently still busy */ ++ status = ap->ops->sff_check_status(ap); ++ dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status); ++ ++ tag = 0; ++ num_processed = 0; ++ while (tag_mask) { ++ num_processed++; ++ while (!(tag_mask & 0x00000001)) { ++ tag++; ++ tag_mask <<= 1; ++ } ++ ++ tag_mask &= (~0x00000001); ++ qc = ata_qc_from_tag(ap, tag); ++ ++ /* To be picked up by completion functions */ ++ qc->ap->link.active_tag = tag; ++ hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT; ++ ++ /* Let libata/scsi layers handle error */ ++ if (status & ATA_ERR) { ++ dev_dbg(ap->dev, "%s ATA_ERR (0x%x)\n", __func__, ++ status); ++ sata_dwc_qc_complete(ap, qc, 1); ++ handled = 1; ++ goto DONE; ++ } ++ ++ /* Process completed command */ ++ dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__, ++ get_prot_descript(qc->tf.protocol)); ++ if (ata_is_dma(qc->tf.protocol)) { ++ host_pvt.dma_interrupt_count++; ++ if (hsdevp->dma_pending[tag] == \ ++ SATA_DWC_DMA_PENDING_NONE) ++ dev_warn(ap->dev, "%s: DMA not pending?\n", ++ __func__); ++ if ((host_pvt.dma_interrupt_count % 2) == 0) ++ sata_dwc_dma_xfer_complete(ap, 1); ++ } else { ++ if (unlikely(sata_dwc_qc_complete(ap, qc, 1))) ++ goto STILLBUSY; ++ } ++ continue; ++ ++STILLBUSY: ++ ap->stats.idle_irq++; ++ dev_warn(ap->dev, "STILL BUSY IRQ ata%d: irq trap\n", ++ ap->print_id); ++ } /* while tag_mask */ ++ ++ /* ++ * Check to see if any commands completed while we were processing our ++ * initial set of completed commands (read status clears interrupts, ++ * so we might miss a completed command interrupt if one came in while ++ * we were processing --we read status as part of processing a completed ++ * command). ++ */ ++ sactive2 = core_scr_read(SCR_ACTIVE); ++ if (sactive2 != sactive) { ++ dev_dbg(ap->dev, "More completed - sactive=0x%x sactive2" ++ "=0x%x\n", sactive, sactive2); ++ } ++ handled = 1; ++ ++DONE: ++ spin_unlock_irqrestore(&host->lock, flags); ++ return IRQ_RETVAL(handled); ++} ++ ++static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag) ++{ ++ struct sata_dwc_device *hsdev = HSDEV_FROM_HSDEVP(hsdevp); ++ ++ if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_RX) { ++ out_le32(&(hsdev->sata_dwc_regs->dmacr), ++ SATA_DWC_DMACR_RX_CLEAR( ++ in_le32(&(hsdev->sata_dwc_regs->dmacr)))); ++ } else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) { ++ out_le32(&(hsdev->sata_dwc_regs->dmacr), ++ SATA_DWC_DMACR_TX_CLEAR( ++ in_le32(&(hsdev->sata_dwc_regs->dmacr)))); ++ } else { ++ /* ++ * This should not happen, it indicates the driver is out of ++ * sync. If it does happen, clear dmacr anyway. ++ */ ++ dev_err(host_pvt.dwc_dev, "%s DMA protocol RX and" ++ "TX DMA not pending tag=0x%02x pending=%d" ++ " dmacr: 0x%08x\n", __func__, tag, ++ hsdevp->dma_pending[tag], ++ in_le32(&(hsdev->sata_dwc_regs->dmacr))); ++ out_le32(&(hsdev->sata_dwc_regs->dmacr), ++ SATA_DWC_DMACR_TXRXCH_CLEAR); ++ } ++} ++ ++static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status) ++{ ++ struct ata_queued_cmd *qc; ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); ++ u8 tag = 0; ++ ++ tag = ap->link.active_tag; ++ qc = ata_qc_from_tag(ap, tag); ++ if (!qc) { ++ dev_err(ap->dev, "failed to get qc"); ++ return; ++ } ++ ++#ifdef DEBUG_NCQ ++ if (tag > 0) { ++ dev_info(ap->dev, "%s tag=%u cmd=0x%02x dma dir=%s proto=%s " ++ "dmacr=0x%08x\n", __func__, qc->tag, qc->tf.command, ++ get_dma_dir_descript(qc->dma_dir), ++ get_prot_descript(qc->tf.protocol), ++ in_le32(&(hsdev->sata_dwc_regs->dmacr))); ++ } ++#endif ++ ++ if (ata_is_dma(qc->tf.protocol)) { ++ if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_NONE) { ++ dev_err(ap->dev, "%s DMA protocol RX and TX DMA not " ++ "pending dmacr: 0x%08x\n", __func__, ++ in_le32(&(hsdev->sata_dwc_regs->dmacr))); ++ } ++ ++ hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_NONE; ++ sata_dwc_qc_complete(ap, qc, check_status); ++ ap->link.active_tag = ATA_TAG_POISON; ++ } else { ++ sata_dwc_qc_complete(ap, qc, check_status); ++ } ++} ++ ++static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc, ++ u32 check_status) ++{ ++ u8 status = 0; ++ u32 mask = 0x0; ++ u8 tag = qc->tag; ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ host_pvt.sata_dwc_sactive_queued = 0; ++ dev_dbg(ap->dev, "%s checkstatus? %x\n", __func__, check_status); ++ ++ if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) ++ dev_err(ap->dev, "TX DMA PENDING\n"); ++ else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_RX) ++ dev_err(ap->dev, "RX DMA PENDING\n"); ++ dev_dbg(ap->dev, "QC complete cmd=0x%02x status=0x%02x ata%u:" ++ " protocol=%d\n", qc->tf.command, status, ap->print_id, ++ qc->tf.protocol); ++ ++ /* clear active bit */ ++ mask = (~(qcmd_tag_to_mask(tag))); ++ host_pvt.sata_dwc_sactive_queued = (host_pvt.sata_dwc_sactive_queued) \ ++ & mask; ++ host_pvt.sata_dwc_sactive_issued = (host_pvt.sata_dwc_sactive_issued) \ ++ & mask; ++ ata_qc_complete(qc); ++ return 0; ++} ++ ++static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev) ++{ ++ /* Enable selective interrupts by setting the interrupt maskregister*/ ++ out_le32(&hsdev->sata_dwc_regs->intmr, ++ SATA_DWC_INTMR_ERRM | ++ SATA_DWC_INTMR_NEWFPM | ++ SATA_DWC_INTMR_PMABRTM | ++ SATA_DWC_INTMR_DMATM); ++ /* ++ * Unmask the error bits that should trigger an error interrupt by ++ * setting the error mask register. ++ */ ++ out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS); ++ ++ dev_dbg(host_pvt.dwc_dev, "%s: INTMR = 0x%08x, ERRMR = 0x%08x\n", ++ __func__, in_le32(&hsdev->sata_dwc_regs->intmr), ++ in_le32(&hsdev->sata_dwc_regs->errmr)); ++} ++ ++static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base) ++{ ++ port->cmd_addr = (void *)base + 0x00; ++ port->data_addr = (void *)base + 0x00; ++ ++ port->error_addr = (void *)base + 0x04; ++ port->feature_addr = (void *)base + 0x04; ++ ++ port->nsect_addr = (void *)base + 0x08; ++ ++ port->lbal_addr = (void *)base + 0x0c; ++ port->lbam_addr = (void *)base + 0x10; ++ port->lbah_addr = (void *)base + 0x14; ++ ++ port->device_addr = (void *)base + 0x18; ++ port->command_addr = (void *)base + 0x1c; ++ port->status_addr = (void *)base + 0x1c; ++ ++ port->altstatus_addr = (void *)base + 0x20; ++ port->ctl_addr = (void *)base + 0x20; ++} ++ ++/* ++ * Function : sata_dwc_port_start ++ * arguments : struct ata_ioports *port ++ * Return value : returns 0 if success, error code otherwise ++ * This function allocates the scatter gather LLI table for AHB DMA ++ */ ++static int sata_dwc_port_start(struct ata_port *ap) ++{ ++ int err = 0; ++ struct sata_dwc_device *hsdev; ++ struct sata_dwc_device_port *hsdevp = NULL; ++ struct device *pdev; ++ int i; ++ ++ hsdev = HSDEV_FROM_AP(ap); ++ ++ dev_dbg(ap->dev, "%s: port_no=%d\n", __func__, ap->port_no); ++ ++ hsdev->host = ap->host; ++ pdev = ap->host->dev; ++ if (!pdev) { ++ dev_err(ap->dev, "%s: no ap->host->dev\n", __func__); ++ err = -ENODEV; ++ goto CLEANUP; ++ } ++ ++ /* Allocate Port Struct */ ++ hsdevp = kzalloc(sizeof(*hsdevp), GFP_KERNEL); ++ if (!hsdevp) { ++ dev_err(ap->dev, "%s: kmalloc failed for hsdevp\n", __func__); ++ err = -ENOMEM; ++ goto CLEANUP; ++ } ++ hsdevp->hsdev = hsdev; ++ ++ for (i = 0; i < SATA_DWC_QCMD_MAX; i++) ++ hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT; ++ ++ ap->bmdma_prd = 0; /* set these so libata doesn't use them */ ++ ap->bmdma_prd_dma = 0; ++ ++ /* ++ * DMA - Assign scatter gather LLI table. We can't use the libata ++ * version since it's PRD is IDE PCI specific. ++ */ ++ for (i = 0; i < SATA_DWC_QCMD_MAX; i++) { ++ hsdevp->llit[i] = dma_alloc_coherent(pdev, ++ SATA_DWC_DMAC_LLI_TBL_SZ, ++ &(hsdevp->llit_dma[i]), ++ GFP_ATOMIC); ++ if (!hsdevp->llit[i]) { ++ dev_err(ap->dev, "%s: dma_alloc_coherent failed\n", ++ __func__); ++ err = -ENOMEM; ++ goto CLEANUP_ALLOC; ++ } ++ } ++ ++ if (ap->port_no == 0) { ++ dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n", ++ __func__); ++ out_le32(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_TXRXCH_CLEAR); ++ ++ dev_dbg(ap->dev, "%s: setting burst size in DBTSR\n", ++ __func__); ++ out_le32(&hsdev->sata_dwc_regs->dbtsr, ++ (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | ++ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT))); ++ } ++ ++ /* Clear any error bits before libata starts issuing commands */ ++ clear_serror(); ++ ap->private_data = hsdevp; ++ dev_dbg(ap->dev, "%s: done\n", __func__); ++ return 0; ++ ++CLEANUP_ALLOC: ++ kfree(hsdevp); ++CLEANUP: ++ dev_dbg(ap->dev, "%s: fail. ap->id = %d\n", __func__, ap->print_id); ++ return err; ++} ++ ++static void sata_dwc_port_stop(struct ata_port *ap) ++{ ++ int i; ++ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ ++ dev_dbg(ap->dev, "%s: ap->id = %d\n", __func__, ap->print_id); ++ ++ if (hsdevp && hsdev) { ++ /* deallocate LLI table */ ++ for (i = 0; i < SATA_DWC_QCMD_MAX; i++) { ++ dma_free_coherent(ap->host->dev, ++ SATA_DWC_DMAC_LLI_TBL_SZ, ++ hsdevp->llit[i], hsdevp->llit_dma[i]); ++ } ++ ++ kfree(hsdevp); ++ } ++ ap->private_data = NULL; ++} ++ ++/* ++ * Function : sata_dwc_exec_command_by_tag ++ * arguments : ata_port *ap, ata_taskfile *tf, u8 tag, u32 cmd_issued ++ * Return value : None ++ * This function keeps track of individual command tag ids and calls ++ * ata_exec_command in libata ++ */ ++static void sata_dwc_exec_command_by_tag(struct ata_port *ap, ++ struct ata_taskfile *tf, ++ u8 tag, u32 cmd_issued) ++{ ++ unsigned long flags; ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ ++ dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d\n", __func__, tf->command, ++ ata_get_cmd_descript(tf->command), tag); ++ ++ spin_lock_irqsave(&ap->host->lock, flags); ++ hsdevp->cmd_issued[tag] = cmd_issued; ++ spin_unlock_irqrestore(&ap->host->lock, flags); ++ /* ++ * Clear SError before executing a new command. ++ * sata_dwc_scr_write and read can not be used here. Clearing the PM ++ * managed SError register for the disk needs to be done before the ++ * task file is loaded. ++ */ ++ clear_serror(); ++ ata_sff_exec_command(ap, tf); ++} ++ ++static void sata_dwc_bmdma_setup_by_tag(struct ata_queued_cmd *qc, u8 tag) ++{ ++ sata_dwc_exec_command_by_tag(qc->ap, &qc->tf, tag, ++ SATA_DWC_CMD_ISSUED_PEND); ++} ++ ++static void sata_dwc_bmdma_setup(struct ata_queued_cmd *qc) ++{ ++ u8 tag = qc->tag; ++ ++ if (ata_is_ncq(qc->tf.protocol)) { ++ dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n", ++ __func__, qc->ap->link.sactive, tag); ++ } else { ++ tag = 0; ++ } ++ sata_dwc_bmdma_setup_by_tag(qc, tag); ++} ++ ++static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag) ++{ ++ int start_dma; ++ u32 reg, dma_chan; ++ struct sata_dwc_device *hsdev = HSDEV_FROM_QC(qc); ++ struct ata_port *ap = qc->ap; ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ int dir = qc->dma_dir; ++ dma_chan = hsdevp->dma_chan[tag]; ++ ++ if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_NOT) { ++ start_dma = 1; ++ if (dir == DMA_TO_DEVICE) ++ hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_TX; ++ else ++ hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_RX; ++ } else { ++ dev_err(ap->dev, "%s: Command not pending cmd_issued=%d " ++ "(tag=%d) DMA NOT started\n", __func__, ++ hsdevp->cmd_issued[tag], tag); ++ start_dma = 0; ++ } ++ ++ dev_dbg(ap->dev, "%s qc=%p tag: %x cmd: 0x%02x dma_dir: %s " ++ "start_dma? %x\n", __func__, qc, tag, qc->tf.command, ++ get_dma_dir_descript(qc->dma_dir), start_dma); ++ sata_dwc_tf_dump(&(qc->tf)); ++ ++ if (start_dma) { ++ reg = core_scr_read(SCR_ERROR); ++ if (reg & SATA_DWC_SERROR_ERR_BITS) { ++ dev_err(ap->dev, "%s: ****** SError=0x%08x ******\n", ++ __func__, reg); ++ } ++ ++ if (dir == DMA_TO_DEVICE) ++ out_le32(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_TXCHEN); ++ else ++ out_le32(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_RXCHEN); ++ ++ /* Enable AHB DMA transfer on the specified channel */ ++ dma_dwc_xfer_start(dma_chan); ++ } ++} ++ ++static void sata_dwc_bmdma_start(struct ata_queued_cmd *qc) ++{ ++ u8 tag = qc->tag; ++ ++ if (ata_is_ncq(qc->tf.protocol)) { ++ dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n", ++ __func__, qc->ap->link.sactive, tag); ++ } else { ++ tag = 0; ++ } ++ dev_dbg(qc->ap->dev, "%s\n", __func__); ++ sata_dwc_bmdma_start_by_tag(qc, tag); ++} ++ ++/* ++ * Function : sata_dwc_qc_prep_by_tag ++ * arguments : ata_queued_cmd *qc, u8 tag ++ * Return value : None ++ * qc_prep for a particular queued command based on tag ++ */ ++static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) ++{ ++ struct scatterlist *sg = qc->sg; ++ struct ata_port *ap = qc->ap; ++ int dma_chan; ++ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ ++ dev_dbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n", ++ __func__, ap->port_no, get_dma_dir_descript(qc->dma_dir), ++ qc->n_elem); ++ ++ dma_chan = dma_dwc_xfer_setup(sg, qc->n_elem, hsdevp->llit[tag], ++ hsdevp->llit_dma[tag], ++ (void *__iomem)(&hsdev->sata_dwc_regs->\ ++ dmadr), qc->dma_dir); ++ if (dma_chan < 0) { ++ dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns err %d\n", ++ __func__, dma_chan); ++ return; ++ } ++ hsdevp->dma_chan[tag] = dma_chan; ++} ++ ++static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc) ++{ ++ u32 sactive; ++ u8 tag = qc->tag; ++ struct ata_port *ap = qc->ap; ++ ++#ifdef DEBUG_NCQ ++ if (qc->tag > 0 || ap->link.sactive > 1) ++ dev_info(ap->dev, "%s ap id=%d cmd(0x%02x)=%s qc tag=%d " ++ "prot=%s ap active_tag=0x%08x ap sactive=0x%08x\n", ++ __func__, ap->print_id, qc->tf.command, ++ ata_get_cmd_descript(qc->tf.command), ++ qc->tag, get_prot_descript(qc->tf.protocol), ++ ap->link.active_tag, ap->link.sactive); ++#endif ++ ++ if (!ata_is_ncq(qc->tf.protocol)) ++ tag = 0; ++ sata_dwc_qc_prep_by_tag(qc, tag); ++ ++ if (ata_is_ncq(qc->tf.protocol)) { ++ sactive = core_scr_read(SCR_ACTIVE); ++ sactive |= (0x00000001 << tag); ++ core_scr_write(SCR_ACTIVE, sactive); ++ ++ dev_dbg(qc->ap->dev, "%s: tag=%d ap->link.sactive = 0x%08x " ++ "sactive=0x%08x\n", __func__, tag, qc->ap->link.sactive, ++ sactive); ++ ++ ap->ops->sff_tf_load(ap, &qc->tf); ++ sata_dwc_exec_command_by_tag(ap, &qc->tf, qc->tag, ++ SATA_DWC_CMD_ISSUED_PEND); ++ } else { ++ ata_sff_qc_issue(qc); ++ } ++ return 0; ++} ++ ++/* ++ * Function : sata_dwc_qc_prep ++ * arguments : ata_queued_cmd *qc ++ * Return value : None ++ * qc_prep for a particular queued command ++ */ ++ ++static void sata_dwc_qc_prep(struct ata_queued_cmd *qc) ++{ ++ if ((qc->dma_dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO)) ++ return; ++ ++#ifdef DEBUG_NCQ ++ if (qc->tag > 0) ++ dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n", ++ __func__, qc->tag, qc->ap->link.active_tag); ++ ++ return ; ++#endif ++} ++ ++static void sata_dwc_error_handler(struct ata_port *ap) ++{ ++ ata_sff_error_handler(ap); ++} ++ ++int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, ++ unsigned long deadline) ++{ ++ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(link->ap); ++ int ret; ++ ++ ret = sata_sff_hardreset(link, class, deadline); ++ ++ sata_dwc_enable_interrupts(hsdev); ++ ++ /* Reconfigure the DMA control register */ ++ out_le32(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_TXRXCH_CLEAR); ++ ++ /* Reconfigure the DMA Burst Transaction Size register */ ++ out_le32(&hsdev->sata_dwc_regs->dbtsr, ++ SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | ++ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)); ++ ++ return ret; ++} ++ ++/* ++ * scsi mid-layer and libata interface structures ++ */ ++static struct scsi_host_template sata_dwc_sht = { ++ ATA_NCQ_SHT(DRV_NAME), ++ /* ++ * test-only: Currently this driver doesn't handle NCQ ++ * correctly. We enable NCQ but set the queue depth to a ++ * max of 1. This will get fixed in in a future release. ++ */ ++ .sg_tablesize = LIBATA_MAX_PRD, ++ .can_queue = ATA_DEF_QUEUE, /* ATA_MAX_QUEUE */ ++ .dma_boundary = ATA_DMA_BOUNDARY, ++}; ++ ++static struct ata_port_operations sata_dwc_ops = { ++ .inherits = &ata_sff_port_ops, ++ ++ .error_handler = sata_dwc_error_handler, ++ .hardreset = sata_dwc_hardreset, ++ ++ .qc_prep = sata_dwc_qc_prep, ++ .qc_issue = sata_dwc_qc_issue, ++ ++ .scr_read = sata_dwc_scr_read, ++ .scr_write = sata_dwc_scr_write, ++ ++ .port_start = sata_dwc_port_start, ++ .port_stop = sata_dwc_port_stop, ++ ++ .bmdma_setup = sata_dwc_bmdma_setup, ++ .bmdma_start = sata_dwc_bmdma_start, ++}; ++ ++static const struct ata_port_info sata_dwc_port_info[] = { ++ { ++ .flags = ATA_FLAG_SATA | ATA_FLAG_NCQ, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &sata_dwc_ops, ++ }, ++}; ++ ++static int sata_dwc_probe(struct platform_device *ofdev) ++{ ++ struct sata_dwc_device *hsdev; ++ u32 idr, versionr; ++ char *ver = (char *)&versionr; ++ u8 *base = NULL; ++ int err = 0; ++ int irq, rc; ++ struct ata_host *host; ++ struct ata_port_info pi = sata_dwc_port_info[0]; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct device_node *np = ofdev->dev.of_node; ++ u32 dma_chan; ++ ++ /* Allocate DWC SATA device */ ++ hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); ++ if (hsdev == NULL) { ++ dev_err(&ofdev->dev, "kmalloc failed for hsdev\n"); ++ err = -ENOMEM; ++ goto error; ++ } ++ ++ if (of_property_read_u32(np, "dma-channel", &dma_chan)) { ++ dev_warn(&ofdev->dev, "no dma-channel property set." ++ " Use channel 0\n"); ++ dma_chan = 0; ++ } ++ host_pvt.dma_channel = dma_chan; ++ ++ /* Ioremap SATA registers */ ++ base = of_iomap(ofdev->dev.of_node, 0); ++ if (!base) { ++ dev_err(&ofdev->dev, "ioremap failed for SATA register" ++ " address\n"); ++ err = -ENODEV; ++ goto error_kmalloc; ++ } ++ hsdev->reg_base = base; ++ dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n"); ++ ++ /* Synopsys DWC SATA specific Registers */ ++ hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET); ++ ++ /* Allocate and fill host */ ++ host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); ++ if (!host) { ++ dev_err(&ofdev->dev, "ata_host_alloc_pinfo failed\n"); ++ err = -ENOMEM; ++ goto error_iomap; ++ } ++ ++ host->private_data = hsdev; ++ ++ /* Setup port */ ++ host->ports[0]->ioaddr.cmd_addr = base; ++ host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET; ++ host_pvt.scr_addr_sstatus = base + SATA_DWC_SCR_OFFSET; ++ sata_dwc_setup_port(&host->ports[0]->ioaddr, (unsigned long)base); ++ ++ /* Read the ID and Version Registers */ ++ idr = in_le32(&hsdev->sata_dwc_regs->idr); ++ versionr = in_le32(&hsdev->sata_dwc_regs->versionr); ++ dev_notice(&ofdev->dev, "id %d, controller version %c.%c%c\n", ++ idr, ver[0], ver[1], ver[2]); ++ ++ /* Get SATA DMA interrupt number */ ++ irq = irq_of_parse_and_map(ofdev->dev.of_node, 1); ++ if (irq == NO_IRQ) { ++ dev_err(&ofdev->dev, "no SATA DMA irq\n"); ++ err = -ENODEV; ++ goto error_out; ++ } ++ ++ /* Get physical SATA DMA register base address */ ++ host_pvt.sata_dma_regs = of_iomap(ofdev->dev.of_node, 1); ++ if (!(host_pvt.sata_dma_regs)) { ++ dev_err(&ofdev->dev, "ioremap failed for AHBDMA register" ++ " address\n"); ++ err = -ENODEV; ++ goto error_out; ++ } ++ ++ /* Save dev for later use in dev_xxx() routines */ ++ host_pvt.dwc_dev = &ofdev->dev; ++ ++ /* Initialize AHB DMAC */ ++ dma_dwc_init(hsdev, irq); ++ ++ /* Enable SATA Interrupts */ ++ sata_dwc_enable_interrupts(hsdev); ++ ++ /* Get SATA interrupt number */ ++ irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); ++ if (irq == NO_IRQ) { ++ dev_err(&ofdev->dev, "no SATA DMA irq\n"); ++ err = -ENODEV; ++ goto error_out; ++ } ++ ++ /* ++ * Now, register with libATA core, this will also initiate the ++ * device discovery process, invoking our port_start() handler & ++ * error_handler() to execute a dummy Softreset EH session ++ */ ++ rc = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); ++ ++ if (rc != 0) ++ dev_err(&ofdev->dev, "failed to activate host"); ++ ++ dev_set_drvdata(&ofdev->dev, host); ++ return 0; ++ ++error_out: ++ /* Free SATA DMA resources */ ++ dma_dwc_exit(hsdev); ++ ++error_iomap: ++ iounmap(base); ++error_kmalloc: ++ kfree(hsdev); ++error: ++ return err; ++} ++ ++static int sata_dwc_remove(struct platform_device *ofdev) ++{ ++ struct device *dev = &ofdev->dev; ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct sata_dwc_device *hsdev = host->private_data; ++ ++ ata_host_detach(host); ++ dev_set_drvdata(dev, NULL); ++ ++ /* Free SATA DMA resources */ ++ dma_dwc_exit(hsdev); ++ ++ iounmap(hsdev->reg_base); ++ kfree(hsdev); ++ kfree(host); ++ dev_dbg(&ofdev->dev, "done\n"); ++ return 0; ++} ++ ++static const struct of_device_id sata_dwc_match[] = { ++ { .compatible = "amcc,sata-460ex", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sata_dwc_match); ++ ++static struct platform_driver sata_dwc_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = sata_dwc_match, ++ }, ++ .probe = sata_dwc_probe, ++ .remove = sata_dwc_remove, ++}; ++ ++module_platform_driver(sata_dwc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Mark Miesfeld "); ++MODULE_DESCRIPTION("DesignWare Cores SATA controller low lever driver"); ++MODULE_VERSION(DRV_VERSION); +diff -Nur linux-3.14.36/drivers/ata/sata_highbank.c linux-openelec/drivers/ata/sata_highbank.c +--- linux-3.14.36/drivers/ata/sata_highbank.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_highbank.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -403,6 +402,7 @@ + static const unsigned long timing[] = { 5, 100, 500}; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -431,7 +431,7 @@ + break; + } while (!online && retry--); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +diff -Nur linux-3.14.36/drivers/ata/sata_nv.c linux-openelec/drivers/ata/sata_nv.c +--- linux-3.14.36/drivers/ata/sata_nv.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_nv.c 2015-05-06 12:05:42.000000000 -0500 +@@ -40,7 +40,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_promise.c linux-openelec/drivers/ata/sata_promise.c +--- linux-3.14.36/drivers/ata/sata_promise.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_promise.c 2015-05-06 12:05:42.000000000 -0500 +@@ -35,7 +35,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_qstor.c linux-openelec/drivers/ata/sata_qstor.c +--- linux-3.14.36/drivers/ata/sata_qstor.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_qstor.c 2015-05-06 12:05:42.000000000 -0500 +@@ -31,7 +31,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_sil.c linux-openelec/drivers/ata/sata_sil.c +--- linux-3.14.36/drivers/ata/sata_sil.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_sil.c 2015-05-06 12:05:42.000000000 -0500 +@@ -37,7 +37,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_sis.c linux-openelec/drivers/ata/sata_sis.c +--- linux-3.14.36/drivers/ata/sata_sis.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_sis.c 2015-05-06 12:05:42.000000000 -0500 +@@ -33,7 +33,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_svw.c linux-openelec/drivers/ata/sata_svw.c +--- linux-3.14.36/drivers/ata/sata_svw.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_svw.c 2015-05-06 12:05:42.000000000 -0500 +@@ -39,7 +39,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_sx4.c linux-openelec/drivers/ata/sata_sx4.c +--- linux-3.14.36/drivers/ata/sata_sx4.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_sx4.c 2015-05-06 12:05:42.000000000 -0500 +@@ -82,7 +82,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_uli.c linux-openelec/drivers/ata/sata_uli.c +--- linux-3.14.36/drivers/ata/sata_uli.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_uli.c 2015-05-06 12:05:42.000000000 -0500 +@@ -28,7 +28,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_via.c linux-openelec/drivers/ata/sata_via.c +--- linux-3.14.36/drivers/ata/sata_via.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_via.c 2015-05-06 12:05:42.000000000 -0500 +@@ -36,7 +36,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/ata/sata_vsc.c linux-openelec/drivers/ata/sata_vsc.c +--- linux-3.14.36/drivers/ata/sata_vsc.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/ata/sata_vsc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -37,7 +37,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -Nur linux-3.14.36/drivers/base/bus.c linux-openelec/drivers/base/bus.c +--- linux-3.14.36/drivers/base/bus.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/bus.c 2015-07-24 18:03:29.300842002 -0500 +@@ -1220,7 +1220,7 @@ + * with the name of the subsystem. The root device can carry subsystem- + * wide attributes. All registered devices are below this single root + * device and are named after the subsystem with a simple enumeration +- * number appended. The registered devices are not explicitely named; ++ * number appended. The registered devices are not explicitly named; + * only 'id' in the device needs to be set. + * + * Do not use this interface for anything new, it exists for compatibility +diff -Nur linux-3.14.36/drivers/base/cpu.c linux-openelec/drivers/base/cpu.c +--- linux-3.14.36/drivers/base/cpu.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/cpu.c 2015-05-06 12:05:42.000000000 -0500 +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include "base.h" + +@@ -286,6 +287,45 @@ + */ + } + ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE ++#ifdef CONFIG_GENERIC_CPU_AUTOPROBE ++static ssize_t print_cpu_modalias(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ ssize_t n; ++ u32 i; ++ ++ n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:", ++ CPU_FEATURE_TYPEVAL); ++ ++ for (i = 0; i < MAX_CPU_FEATURES; i++) ++ if (cpu_have_feature(i)) { ++ if (PAGE_SIZE < n + sizeof(",XXXX\n")) { ++ WARN(1, "CPU features overflow page\n"); ++ break; ++ } ++ n += sprintf(&buf[n], ",%04X", i); ++ } ++ buf[n++] = '\n'; ++ return n; ++} ++#else ++#define print_cpu_modalias arch_print_cpu_modalias ++#endif ++ ++static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); ++ if (buf) { ++ print_cpu_modalias(NULL, NULL, buf); ++ add_uevent_var(env, "MODALIAS=%s", buf); ++ kfree(buf); ++ } ++ return 0; ++} ++#endif ++ + /* + * register_cpu - Setup a sysfs device for a CPU. + * @cpu - cpu->hotpluggable field set to 1 will generate a control file in +@@ -306,8 +346,8 @@ + cpu->dev.offline_disabled = !cpu->hotpluggable; + cpu->dev.offline = !cpu_online(num); + cpu->dev.of_node = of_get_cpu_node(num, NULL); +-#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE +- cpu->dev.bus->uevent = arch_cpu_uevent; ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE ++ cpu->dev.bus->uevent = cpu_uevent; + #endif + cpu->dev.groups = common_cpu_attr_groups; + if (cpu->hotpluggable) +@@ -330,8 +370,8 @@ + } + EXPORT_SYMBOL_GPL(get_cpu_device); + +-#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE +-static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE ++static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); + #endif + + static struct attribute *cpu_root_attrs[] = { +@@ -344,7 +384,7 @@ + &cpu_attrs[2].attr.attr, + &dev_attr_kernel_max.attr, + &dev_attr_offline.attr, +-#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE + &dev_attr_modalias.attr, + #endif + NULL +diff -Nur linux-3.14.36/drivers/base/dma-buf.c linux-openelec/drivers/base/dma-buf.c +--- linux-3.14.36/drivers/base/dma-buf.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/dma-buf.c 2015-05-06 12:05:42.000000000 -0500 +@@ -251,9 +251,8 @@ + * @dmabuf: [in] buffer to attach device to. + * @dev: [in] device to be attached. + * +- * Returns struct dma_buf_attachment * for this attachment; may return negative +- * error codes. +- * ++ * Returns struct dma_buf_attachment * for this attachment; returns ERR_PTR on ++ * error. + */ + struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, + struct device *dev) +@@ -319,9 +318,8 @@ + * @attach: [in] attachment whose scatterlist is to be returned + * @direction: [in] direction of DMA transfer + * +- * Returns sg_table containing the scatterlist to be returned; may return NULL +- * or ERR_PTR. +- * ++ * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR ++ * on error. + */ + struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, + enum dma_data_direction direction) +@@ -334,6 +332,8 @@ + return ERR_PTR(-EINVAL); + + sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction); ++ if (!sg_table) ++ sg_table = ERR_PTR(-ENOMEM); + + return sg_table; + } +@@ -544,6 +544,8 @@ + * These calls are optional in drivers. The intended use for them + * is for mapping objects linear in kernel space for high use objects. + * Please attempt to use kmap/kunmap before thinking about these interfaces. ++ * ++ * Returns NULL on error. + */ + void *dma_buf_vmap(struct dma_buf *dmabuf) + { +@@ -566,7 +568,9 @@ + BUG_ON(dmabuf->vmap_ptr); + + ptr = dmabuf->ops->vmap(dmabuf); +- if (IS_ERR_OR_NULL(ptr)) ++ if (WARN_ON_ONCE(IS_ERR(ptr))) ++ ptr = NULL; ++ if (!ptr) + goto out_unlock; + + dmabuf->vmap_ptr = ptr; +diff -Nur linux-3.14.36/drivers/base/dma-contiguous.c linux-openelec/drivers/base/dma-contiguous.c +--- linux-3.14.36/drivers/base/dma-contiguous.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/dma-contiguous.c 2015-05-06 12:05:42.000000000 -0500 +@@ -24,22 +24,9 @@ + + #include + #include +-#include +-#include +-#include + #include +-#include +-#include +-#include + #include +- +-struct cma { +- unsigned long base_pfn; +- unsigned long count; +- unsigned long *bitmap; +-}; +- +-struct cma *dma_contiguous_default_area; ++#include + + #ifdef CONFIG_CMA_SIZE_MBYTES + #define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES +@@ -47,6 +34,8 @@ + #define CMA_SIZE_MBYTES 0 + #endif + ++struct cma *dma_contiguous_default_area; ++ + /* + * Default global CMA area size can be defined in kernel's .config. + * This is useful mainly for distro maintainers to create a kernel +@@ -59,11 +48,22 @@ + */ + static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M; + static phys_addr_t size_cmdline = -1; ++static phys_addr_t base_cmdline; ++static phys_addr_t limit_cmdline; + + static int __init early_cma(char *p) + { + pr_debug("%s(%s)\n", __func__, p); + size_cmdline = memparse(p, &p); ++ if (*p != '@') ++ return 0; ++ base_cmdline = memparse(p + 1, &p); ++ if (*p != '-') { ++ limit_cmdline = base_cmdline + size_cmdline; ++ return 0; ++ } ++ limit_cmdline = memparse(p + 1, &p); ++ + return 0; + } + early_param("cma", early_cma); +@@ -107,11 +107,18 @@ + void __init dma_contiguous_reserve(phys_addr_t limit) + { + phys_addr_t selected_size = 0; ++ phys_addr_t selected_base = 0; ++ phys_addr_t selected_limit = limit; ++ bool fixed = false; + + pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); + + if (size_cmdline != -1) { + selected_size = size_cmdline; ++ selected_base = base_cmdline; ++ selected_limit = min_not_zero(limit_cmdline, limit); ++ if (base_cmdline + size_cmdline == limit_cmdline) ++ fixed = true; + } else { + #ifdef CONFIG_CMA_SIZE_SEL_MBYTES + selected_size = size_bytes; +@@ -128,68 +135,12 @@ + pr_debug("%s: reserving %ld MiB for global area\n", __func__, + (unsigned long)selected_size / SZ_1M); + +- dma_contiguous_reserve_area(selected_size, 0, limit, +- &dma_contiguous_default_area); +- } +-}; +- +-static DEFINE_MUTEX(cma_mutex); +- +-static int __init cma_activate_area(struct cma *cma) +-{ +- int bitmap_size = BITS_TO_LONGS(cma->count) * sizeof(long); +- unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; +- unsigned i = cma->count >> pageblock_order; +- struct zone *zone; +- +- cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); +- +- if (!cma->bitmap) +- return -ENOMEM; +- +- WARN_ON_ONCE(!pfn_valid(pfn)); +- zone = page_zone(pfn_to_page(pfn)); +- +- do { +- unsigned j; +- base_pfn = pfn; +- for (j = pageblock_nr_pages; j; --j, pfn++) { +- WARN_ON_ONCE(!pfn_valid(pfn)); +- /* +- * alloc_contig_range requires the pfn range +- * specified to be in the same zone. Make this +- * simple by forcing the entire CMA resv range +- * to be in the same zone. +- */ +- if (page_zone(pfn_to_page(pfn)) != zone) +- goto err; +- } +- init_cma_reserved_pageblock(pfn_to_page(base_pfn)); +- } while (--i); +- +- return 0; +- +-err: +- kfree(cma->bitmap); +- return -EINVAL; +-} +- +-static struct cma cma_areas[MAX_CMA_AREAS]; +-static unsigned cma_area_count; +- +-static int __init cma_init_reserved_areas(void) +-{ +- int i; +- +- for (i = 0; i < cma_area_count; i++) { +- int ret = cma_activate_area(&cma_areas[i]); +- if (ret) +- return ret; ++ dma_contiguous_reserve_area(selected_size, selected_base, ++ selected_limit, ++ &dma_contiguous_default_area, ++ fixed); + } +- +- return 0; + } +-core_initcall(cma_init_reserved_areas); + + /** + * dma_contiguous_reserve_area() - reserve custom contiguous area +@@ -197,78 +148,32 @@ + * @base: Base address of the reserved area optional, use 0 for any + * @limit: End address of the reserved memory (optional, 0 for any). + * @res_cma: Pointer to store the created cma region. ++ * @fixed: hint about where to place the reserved area + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas for specific + * devices. ++ * ++ * If @fixed is true, reserve contiguous area at exactly @base. If false, ++ * reserve in range from @base to @limit. + */ + int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, +- phys_addr_t limit, struct cma **res_cma) ++ phys_addr_t limit, struct cma **res_cma, ++ bool fixed) + { +- struct cma *cma = &cma_areas[cma_area_count]; +- phys_addr_t alignment; +- int ret = 0; +- +- pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__, +- (unsigned long)size, (unsigned long)base, +- (unsigned long)limit); +- +- /* Sanity checks */ +- if (cma_area_count == ARRAY_SIZE(cma_areas)) { +- pr_err("Not enough slots for CMA reserved regions!\n"); +- return -ENOSPC; +- } +- +- if (!size) +- return -EINVAL; +- +- /* Sanitise input arguments */ +- alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order); +- base = ALIGN(base, alignment); +- size = ALIGN(size, alignment); +- limit &= ~(alignment - 1); +- +- /* Reserve memory */ +- if (base) { +- if (memblock_is_region_reserved(base, size) || +- memblock_reserve(base, size) < 0) { +- ret = -EBUSY; +- goto err; +- } +- } else { +- /* +- * Use __memblock_alloc_base() since +- * memblock_alloc_base() panic()s. +- */ +- phys_addr_t addr = __memblock_alloc_base(size, alignment, limit); +- if (!addr) { +- ret = -ENOMEM; +- goto err; +- } else { +- base = addr; +- } +- } +- +- /* +- * Each reserved area must be initialised later, when more kernel +- * subsystems (like slab allocator) are available. +- */ +- cma->base_pfn = PFN_DOWN(base); +- cma->count = size >> PAGE_SHIFT; +- *res_cma = cma; +- cma_area_count++; ++ int ret; + +- pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M, +- (unsigned long)base); ++ ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, res_cma); ++ if (ret) ++ return ret; + + /* Architecture specific contiguous memory fixup. */ +- dma_contiguous_early_fixup(base, size); ++ dma_contiguous_early_fixup(cma_get_base(*res_cma), ++ cma_get_size(*res_cma)); ++ + return 0; +-err: +- pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); +- return ret; + } + + /** +@@ -279,57 +184,16 @@ + * + * This function allocates memory buffer for specified device. It uses + * device specific contiguous memory area if available or the default +- * global one. Requires architecture specific get_dev_cma_area() helper ++ * global one. Requires architecture specific dev_get_cma_area() helper + * function. + */ + struct page *dma_alloc_from_contiguous(struct device *dev, int count, + unsigned int align) + { +- unsigned long mask, pfn, pageno, start = 0; +- struct cma *cma = dev_get_cma_area(dev); +- struct page *page = NULL; +- int ret; +- +- if (!cma || !cma->count) +- return NULL; +- + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + +- pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma, +- count, align); +- +- if (!count) +- return NULL; +- +- mask = (1 << align) - 1; +- +- mutex_lock(&cma_mutex); +- +- for (;;) { +- pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count, +- start, count, mask); +- if (pageno >= cma->count) +- break; +- +- pfn = cma->base_pfn + pageno; +- ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); +- if (ret == 0) { +- bitmap_set(cma->bitmap, pageno, count); +- page = pfn_to_page(pfn); +- break; +- } else if (ret != -EBUSY) { +- break; +- } +- pr_debug("%s(): memory range at %p is busy, retrying\n", +- __func__, pfn_to_page(pfn)); +- /* try again with a bit different memory target */ +- start = pageno + mask + 1; +- } +- +- mutex_unlock(&cma_mutex); +- pr_debug("%s(): returned %p\n", __func__, page); +- return page; ++ return cma_alloc(dev_get_cma_area(dev), count, align); + } + + /** +@@ -345,25 +209,5 @@ + bool dma_release_from_contiguous(struct device *dev, struct page *pages, + int count) + { +- struct cma *cma = dev_get_cma_area(dev); +- unsigned long pfn; +- +- if (!cma || !pages) +- return false; +- +- pr_debug("%s(page %p)\n", __func__, (void *)pages); +- +- pfn = page_to_pfn(pages); +- +- if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count) +- return false; +- +- VM_BUG_ON(pfn + count > cma->base_pfn + cma->count); +- +- mutex_lock(&cma_mutex); +- bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count); +- free_contig_range(pfn, count); +- mutex_unlock(&cma_mutex); +- +- return true; ++ return cma_release(dev_get_cma_area(dev), pages, count); + } +diff -Nur linux-3.14.36/drivers/base/Kconfig linux-openelec/drivers/base/Kconfig +--- linux-3.14.36/drivers/base/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -185,6 +185,14 @@ + bool + default n + ++config HAVE_CPU_AUTOPROBE ++ def_bool ARCH_HAS_CPU_AUTOPROBE ++ ++config GENERIC_CPU_AUTOPROBE ++ bool ++ depends on !ARCH_HAS_CPU_AUTOPROBE ++ select HAVE_CPU_AUTOPROBE ++ + config SOC_BUS + bool + +@@ -266,16 +274,6 @@ + + If unsure, leave the default value "8". + +-config CMA_AREAS +- int "Maximum count of the CMA device-private areas" +- default 7 +- help +- CMA allows to create CMA areas for particular devices. This parameter +- sets the maximum number of such device private CMA areas in the +- system. +- +- If unsure, leave the default value "7". +- + endif + + endmenu +diff -Nur linux-3.14.36/drivers/base/regmap/Kconfig linux-openelec/drivers/base/regmap/Kconfig +--- linux-3.14.36/drivers/base/regmap/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/regmap/Kconfig 2015-07-24 18:03:30.304842002 -0500 +@@ -3,11 +3,14 @@ + # subsystems should select the appropriate symbols. + + config REGMAP +- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ) ++ default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) + select LZO_COMPRESS + select LZO_DECOMPRESS + select IRQ_DOMAIN if REGMAP_IRQ + bool ++ ++config REGMAP_AC97 ++ tristate + + config REGMAP_I2C + tristate +diff -Nur linux-3.14.36/drivers/base/regmap/Makefile linux-openelec/drivers/base/regmap/Makefile +--- linux-3.14.36/drivers/base/regmap/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/regmap/Makefile 2015-07-24 18:03:30.304842002 -0500 +@@ -1,6 +1,7 @@ + obj-$(CONFIG_REGMAP) += regmap.o regcache.o + obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o + obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o ++obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o + obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o + obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o + obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o +diff -Nur linux-3.14.36/drivers/base/regmap/regmap-ac97.c linux-openelec/drivers/base/regmap/regmap-ac97.c +--- linux-3.14.36/drivers/base/regmap/regmap-ac97.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/base/regmap/regmap-ac97.c 2015-07-24 18:03:30.304842002 -0500 +@@ -0,0 +1,113 @@ ++/* ++ * Register map access API - AC'97 support ++ * ++ * Copyright 2013 Linaro Ltd. 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 . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case AC97_RESET: ++ case AC97_POWERDOWN: ++ case AC97_INT_PAGING: ++ case AC97_EXTENDED_ID: ++ case AC97_EXTENDED_STATUS: ++ case AC97_EXTENDED_MID: ++ case AC97_EXTENDED_MSTATUS: ++ case AC97_GPIO_STATUS: ++ case AC97_MISC_AFE: ++ case AC97_VENDOR_ID1: ++ case AC97_VENDOR_ID2: ++ case AC97_CODEC_CLASS_REV: ++ case AC97_PCI_SVID: ++ case AC97_PCI_SID: ++ case AC97_FUNC_SELECT: ++ case AC97_FUNC_INFO: ++ case AC97_SENSE_INFO: ++ return true; ++ default: ++ return false; ++ } ++} ++EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile); ++ ++static int regmap_ac97_reg_read(void *context, unsigned int reg, ++ unsigned int *val) ++{ ++ struct snd_ac97 *ac97 = context; ++ *val = ac97->bus->ops->read(ac97, reg); ++ ++ return 0; ++} ++ ++static int regmap_ac97_reg_write(void *context, unsigned int reg, ++ unsigned int val) ++{ ++ struct snd_ac97 *ac97 = context; ++ ++ ac97->bus->ops->write(ac97, reg, val); ++ ++ return 0; ++} ++ ++static const struct regmap_bus ac97_regmap_bus = { ++ .reg_write = regmap_ac97_reg_write, ++ .reg_read = regmap_ac97_reg_read, ++}; ++ ++/** ++ * regmap_init_ac97(): Initialise AC'97 register map ++ * ++ * @ac97: Device that will be interacted with ++ * @config: Configuration for register map ++ * ++ * The return value will be an ERR_PTR() on error or a valid pointer to ++ * a struct regmap. ++ */ ++struct regmap *regmap_init_ac97(struct snd_ac97 *ac97, ++ const struct regmap_config *config) ++{ ++ return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); ++} ++EXPORT_SYMBOL_GPL(regmap_init_ac97); ++ ++/** ++ * devm_regmap_init_ac97(): Initialise AC'97 register map ++ * ++ * @ac97: Device that will be interacted with ++ * @config: Configuration for register map ++ * ++ * The return value will be an ERR_PTR() on error or a valid pointer ++ * to a struct regmap. The regmap will be automatically freed by the ++ * device management code. ++ */ ++struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97, ++ const struct regmap_config *config) ++{ ++ return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); ++} ++EXPORT_SYMBOL_GPL(devm_regmap_init_ac97); ++ ++MODULE_LICENSE("GPL v2"); +diff -Nur linux-3.14.36/drivers/base/regmap/regmap.c linux-openelec/drivers/base/regmap/regmap.c +--- linux-3.14.36/drivers/base/regmap/regmap.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/base/regmap/regmap.c 2015-07-24 18:03:30.312842002 -0500 +@@ -35,10 +35,14 @@ + unsigned int mask, unsigned int val, + bool *change); + ++static int _regmap_bus_reg_read(void *context, unsigned int reg, ++ unsigned int *val); + static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val); + static int _regmap_bus_formatted_write(void *context, unsigned int reg, + unsigned int val); ++static int _regmap_bus_reg_write(void *context, unsigned int reg, ++ unsigned int val); + static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val); + +@@ -472,6 +476,12 @@ + + map->defer_caching = false; + goto skip_format_initialization; ++ } else if (!bus->read || !bus->write) { ++ map->reg_read = _regmap_bus_reg_read; ++ map->reg_write = _regmap_bus_reg_write; ++ ++ map->defer_caching = false; ++ goto skip_format_initialization; + } else { + map->reg_read = _regmap_bus_read; + } +@@ -1267,6 +1277,14 @@ + return ret; + } + ++static int _regmap_bus_reg_write(void *context, unsigned int reg, ++ unsigned int val) ++{ ++ struct regmap *map = context; ++ ++ return map->bus->reg_write(map->bus_context, reg, val); ++} ++ + static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val) + { +@@ -1708,6 +1726,14 @@ + return ret; + } + ++static int _regmap_bus_reg_read(void *context, unsigned int reg, ++ unsigned int *val) ++{ ++ struct regmap *map = context; ++ ++ return map->bus->reg_read(map->bus_context, reg, val); ++} ++ + static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val) + { +diff -Nur linux-3.14.36/drivers/bus/arm-cci.c linux-openelec/drivers/bus/arm-cci.c +--- linux-3.14.36/drivers/bus/arm-cci.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/bus/arm-cci.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,6 +26,7 @@ + + #include + #include ++#include + #include + #include + +@@ -544,6 +545,7 @@ + + cci_pmu->plat_device = pdev; + cci_pmu->num_events = pmu_get_max_counters(); ++ cpumask_setall(&cci_pmu->valid_cpus); + + return armpmu_register(cci_pmu, -1); + } +@@ -969,6 +971,11 @@ + const char *match_str; + bool is_ace; + ++ if (psci_probe() == 0) { ++ pr_debug("psci found. Aborting cci probe\n"); ++ return -ENODEV; ++ } ++ + np = of_find_matching_node(NULL, arm_cci_matches); + if (!np) + return -ENODEV; +diff -Nur linux-3.14.36/drivers/char/fsl_otp.c linux-openelec/drivers/char/fsl_otp.c +--- linux-3.14.36/drivers/char/fsl_otp.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/char/fsl_otp.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,316 @@ ++/* ++ * Freescale On-Chip OTP driver ++ * ++ * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HW_OCOTP_CTRL 0x00000000 ++#define HW_OCOTP_CTRL_SET 0x00000004 ++#define BP_OCOTP_CTRL_WR_UNLOCK 16 ++#define BM_OCOTP_CTRL_WR_UNLOCK 0xFFFF0000 ++#define BM_OCOTP_CTRL_RELOAD_SHADOWS 0x00000400 ++#define BM_OCOTP_CTRL_ERROR 0x00000200 ++#define BM_OCOTP_CTRL_BUSY 0x00000100 ++#define BP_OCOTP_CTRL_ADDR 0 ++#define BM_OCOTP_CTRL_ADDR 0x0000007F ++ ++#define HW_OCOTP_TIMING 0x00000010 ++#define BP_OCOTP_TIMING_STROBE_READ 16 ++#define BM_OCOTP_TIMING_STROBE_READ 0x003F0000 ++#define BP_OCOTP_TIMING_RELAX 12 ++#define BM_OCOTP_TIMING_RELAX 0x0000F000 ++#define BP_OCOTP_TIMING_STROBE_PROG 0 ++#define BM_OCOTP_TIMING_STROBE_PROG 0x00000FFF ++ ++#define HW_OCOTP_DATA 0x00000020 ++ ++#define HW_OCOTP_CUST_N(n) (0x00000400 + (n) * 0x10) ++#define BF(value, field) (((value) << BP_##field) & BM_##field) ++ ++#define DEF_RELAX 20 /* > 16.5ns */ ++ ++#define BANK(a, b, c, d, e, f, g, h) { \ ++ "HW_OCOTP_"#a, "HW_OCOTP_"#b, "HW_OCOTP_"#c, "HW_OCOTP_"#d, \ ++ "HW_OCOTP_"#e, "HW_OCOTP_"#f, "HW_OCOTP_"#g, "HW_OCOTP_"#h, \ ++} ++ ++static const char *imx6q_otp_desc[16][8] = { ++ BANK(LOCK, CFG0, CFG1, CFG2, CFG3, CFG4, CFG5, CFG6), ++ BANK(MEM0, MEM1, MEM2, MEM3, MEM4, ANA0, ANA1, ANA2), ++ BANK(OTPMK0, OTPMK1, OTPMK2, OTPMK3, OTPMK4, OTPMK5, OTPMK6, OTPMK7), ++ BANK(SRK0, SRK1, SRK2, SRK3, SRK4, SRK5, SRK6, SRK7), ++ BANK(RESP0, HSJC_RESP1, MAC0, MAC1, HDCP_KSV0, HDCP_KSV1, GP1, GP2), ++ BANK(DTCP_KEY0, DTCP_KEY1, DTCP_KEY2, DTCP_KEY3, DTCP_KEY4, MISC_CONF, FIELD_RETURN, SRK_REVOKE), ++ BANK(HDCP_KEY0, HDCP_KEY1, HDCP_KEY2, HDCP_KEY3, HDCP_KEY4, HDCP_KEY5, HDCP_KEY6, HDCP_KEY7), ++ BANK(HDCP_KEY8, HDCP_KEY9, HDCP_KEY10, HDCP_KEY11, HDCP_KEY12, HDCP_KEY13, HDCP_KEY14, HDCP_KEY15), ++ BANK(HDCP_KEY16, HDCP_KEY17, HDCP_KEY18, HDCP_KEY19, HDCP_KEY20, HDCP_KEY21, HDCP_KEY22, HDCP_KEY23), ++ BANK(HDCP_KEY24, HDCP_KEY25, HDCP_KEY26, HDCP_KEY27, HDCP_KEY28, HDCP_KEY29, HDCP_KEY30, HDCP_KEY31), ++ BANK(HDCP_KEY32, HDCP_KEY33, HDCP_KEY34, HDCP_KEY35, HDCP_KEY36, HDCP_KEY37, HDCP_KEY38, HDCP_KEY39), ++ BANK(HDCP_KEY40, HDCP_KEY41, HDCP_KEY42, HDCP_KEY43, HDCP_KEY44, HDCP_KEY45, HDCP_KEY46, HDCP_KEY47), ++ BANK(HDCP_KEY48, HDCP_KEY49, HDCP_KEY50, HDCP_KEY51, HDCP_KEY52, HDCP_KEY53, HDCP_KEY54, HDCP_KEY55), ++ BANK(HDCP_KEY56, HDCP_KEY57, HDCP_KEY58, HDCP_KEY59, HDCP_KEY60, HDCP_KEY61, HDCP_KEY62, HDCP_KEY63), ++ BANK(HDCP_KEY64, HDCP_KEY65, HDCP_KEY66, HDCP_KEY67, HDCP_KEY68, HDCP_KEY69, HDCP_KEY70, HDCP_KEY71), ++ BANK(CRC0, CRC1, CRC2, CRC3, CRC4, CRC5, CRC6, CRC7), ++}; ++ ++static DEFINE_MUTEX(otp_mutex); ++static void __iomem *otp_base; ++static struct clk *otp_clk; ++struct kobject *otp_kobj; ++struct kobj_attribute *otp_kattr; ++struct attribute_group *otp_attr_group; ++ ++static void set_otp_timing(void) ++{ ++ unsigned long clk_rate = 0; ++ unsigned long strobe_read, relex, strobe_prog; ++ u32 timing = 0; ++ ++ clk_rate = clk_get_rate(otp_clk); ++ ++ /* do optimization for too many zeros */ ++ relex = clk_rate / (1000000000 / DEF_RELAX) - 1; ++ strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; ++ strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; ++ ++ timing = BF(relex, OCOTP_TIMING_RELAX); ++ timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ); ++ timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG); ++ ++ __raw_writel(timing, otp_base + HW_OCOTP_TIMING); ++} ++ ++static int otp_wait_busy(u32 flags) ++{ ++ int count; ++ u32 c; ++ ++ for (count = 10000; count >= 0; count--) { ++ c = __raw_readl(otp_base + HW_OCOTP_CTRL); ++ if (!(c & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR | flags))) ++ break; ++ cpu_relax(); ++ } ++ ++ if (count < 0) ++ return -ETIMEDOUT; ++ ++ return 0; ++} ++ ++int fsl_otp_readl(unsigned long offset, u32 *value) ++{ ++ int ret = 0; ++ ++ ret = clk_prepare_enable(otp_clk); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&otp_mutex); ++ ++ set_otp_timing(); ++ ret = otp_wait_busy(0); ++ if (ret) ++ goto out; ++ ++ *value = __raw_readl(otp_base + offset); ++ ++out: ++ mutex_unlock(&otp_mutex); ++ clk_disable_unprepare(otp_clk); ++ return ret; ++} ++EXPORT_SYMBOL(fsl_otp_readl); ++ ++static ssize_t fsl_otp_show(struct kobject *kobj, struct kobj_attribute *attr, ++ char *buf) ++{ ++ unsigned int index = attr - otp_kattr; ++ u32 value = 0; ++ int ret; ++ ++ ret = fsl_otp_readl(HW_OCOTP_CUST_N(index), &value); ++ ++ return ret ? 0 : sprintf(buf, "0x%x\n", value); ++} ++ ++#ifdef CONFIG_FSL_OTP_WRITE_ENABLE ++static int otp_write_bits(int addr, u32 data, u32 magic) ++{ ++ u32 c; /* for control register */ ++ ++ /* init the control register */ ++ c = __raw_readl(otp_base + HW_OCOTP_CTRL); ++ c &= ~BM_OCOTP_CTRL_ADDR; ++ c |= BF(addr, OCOTP_CTRL_ADDR); ++ c |= BF(magic, OCOTP_CTRL_WR_UNLOCK); ++ __raw_writel(c, otp_base + HW_OCOTP_CTRL); ++ ++ /* init the data register */ ++ __raw_writel(data, otp_base + HW_OCOTP_DATA); ++ otp_wait_busy(0); ++ ++ mdelay(2); /* Write Postamble */ ++ ++ return 0; ++} ++ ++static ssize_t fsl_otp_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ unsigned int index = attr - otp_kattr; ++ u32 value; ++ int ret; ++ ++ sscanf(buf, "0x%x", &value); ++ ++ ret = clk_prepare_enable(otp_clk); ++ if (ret) ++ return 0; ++ ++ mutex_lock(&otp_mutex); ++ ++ set_otp_timing(); ++ ret = otp_wait_busy(0); ++ if (ret) ++ goto out; ++ ++ otp_write_bits(index, value, 0x3e77); ++ ++ /* Reload all the shadow registers */ ++ __raw_writel(BM_OCOTP_CTRL_RELOAD_SHADOWS, ++ otp_base + HW_OCOTP_CTRL_SET); ++ udelay(1); ++ otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS); ++ ++out: ++ mutex_unlock(&otp_mutex); ++ clk_disable_unprepare(otp_clk); ++ return ret ? 0 : count; ++} ++#endif ++ ++static int fsl_otp_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct attribute **attrs; ++ const char **desc; ++ int i, num; ++ int ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ otp_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(otp_base)) { ++ ret = PTR_ERR(otp_base); ++ dev_err(&pdev->dev, "failed to ioremap resource: %d\n", ret); ++ return ret; ++ } ++ ++ otp_clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(otp_clk)) { ++ ret = PTR_ERR(otp_clk); ++ dev_err(&pdev->dev, "failed to get clock: %d\n", ret); ++ return ret; ++ } ++ ++ desc = (const char **) imx6q_otp_desc; ++ num = sizeof(imx6q_otp_desc) / sizeof(void *); ++ ++ /* The last one is NULL, which is used to detect the end */ ++ attrs = devm_kzalloc(&pdev->dev, (num + 1) * sizeof(*attrs), ++ GFP_KERNEL); ++ otp_kattr = devm_kzalloc(&pdev->dev, num * sizeof(*otp_kattr), ++ GFP_KERNEL); ++ otp_attr_group = devm_kzalloc(&pdev->dev, sizeof(*otp_attr_group), ++ GFP_KERNEL); ++ if (!attrs || !otp_kattr || !otp_attr_group) ++ return -ENOMEM; ++ ++ for (i = 0; i < num; i++) { ++ sysfs_attr_init(&otp_kattr[i].attr); ++ otp_kattr[i].attr.name = desc[i]; ++#ifdef CONFIG_FSL_OTP_WRITE_ENABLE ++ otp_kattr[i].attr.mode = 0600; ++ otp_kattr[i].store = fsl_otp_store; ++#else ++ otp_kattr[i].attr.mode = 0400; ++#endif ++ otp_kattr[i].show = fsl_otp_show; ++ attrs[i] = &otp_kattr[i].attr; ++ } ++ otp_attr_group->attrs = attrs; ++ ++ otp_kobj = kobject_create_and_add("fsl_otp", NULL); ++ if (!otp_kobj) { ++ dev_err(&pdev->dev, "failed to add kobject\n"); ++ return -ENOMEM; ++ } ++ ++ ret = sysfs_create_group(otp_kobj, otp_attr_group); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to create sysfs group: %d\n", ret); ++ kobject_put(otp_kobj); ++ return ret; ++ } ++ ++ mutex_init(&otp_mutex); ++ ++ return 0; ++} ++ ++static int fsl_otp_remove(struct platform_device *pdev) ++{ ++ sysfs_remove_group(otp_kobj, otp_attr_group); ++ kobject_put(otp_kobj); ++ ++ return 0; ++} ++ ++static const struct of_device_id fsl_otp_dt_ids[] = { ++ { .compatible = "fsl,imx6q-ocotp", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, fsl_otp_dt_ids); ++ ++static struct platform_driver fsl_otp_driver = { ++ .driver = { ++ .name = "imx-ocotp", ++ .owner = THIS_MODULE, ++ .of_match_table = fsl_otp_dt_ids, ++ }, ++ .probe = fsl_otp_probe, ++ .remove = fsl_otp_remove, ++}; ++module_platform_driver(fsl_otp_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Huang Shijie "); ++MODULE_DESCRIPTION("Freescale i.MX OCOTP driver"); +diff -Nur linux-3.14.36/drivers/char/Kconfig linux-openelec/drivers/char/Kconfig +--- linux-3.14.36/drivers/char/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/char/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -82,6 +82,21 @@ + + If unsure, say N. + ++config FSL_OTP ++ tristate "Freescale On-Chip OTP Memory Support" ++ depends on HAS_IOMEM && OF ++ help ++ If you say Y here, you will get support for a character device ++ interface into the One Time Programmable memory pages that are ++ stored on the some Freescale i.MX processors. This will not get ++ you access to the secure memory pages however. You will need to ++ write your own secure code and reader for that. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called fsl_otp. ++ ++ If unsure, it is safe to say Y. ++ + config PRINTER + tristate "Parallel printer support" + depends on PARPORT +diff -Nur linux-3.14.36/drivers/char/Makefile linux-openelec/drivers/char/Makefile +--- linux-3.14.36/drivers/char/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/char/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -16,6 +16,7 @@ + obj-$(CONFIG_IBM_BSR) += bsr.o + obj-$(CONFIG_SGI_MBCS) += mbcs.o + obj-$(CONFIG_BFIN_OTP) += bfin-otp.o ++obj-$(CONFIG_FSL_OTP) += fsl_otp.o + + obj-$(CONFIG_PRINTER) += lp.o + +diff -Nur linux-3.14.36/drivers/clk/clk.c linux-openelec/drivers/clk/clk.c +--- linux-3.14.36/drivers/clk/clk.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/clk/clk.c 2015-07-24 18:03:29.384842002 -0500 +@@ -1707,6 +1707,7 @@ + */ + int clk_set_parent(struct clk *clk, struct clk *parent) + { ++ struct clk *child; + int ret = 0; + int p_index = 0; + unsigned long p_rate = 0; +@@ -1733,6 +1734,18 @@ + goto out; + } + ++ /* check two consecutive basic mux clocks */ ++ if (clk->flags & CLK_IS_BASIC_MUX) { ++ hlist_for_each_entry(child, &clk->children, child_node) { ++ if (child->flags & CLK_IS_BASIC_MUX) { ++ pr_err("%s: failed to switch parent of %s due to child mux %s\n", ++ __func__, clk->name, child->name); ++ ret = -EBUSY; ++ goto out; ++ } ++ } ++ } ++ + /* try finding the new parent index */ + if (parent) { + p_index = clk_fetch_parent_index(clk, parent); +diff -Nur linux-3.14.36/drivers/clk/clk.c.orig linux-openelec/drivers/clk/clk.c.orig +--- linux-3.14.36/drivers/clk/clk.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/clk/clk.c.orig 2015-07-24 18:03:28.460842002 -0500 +@@ -0,0 +1,2558 @@ ++/* ++ * Copyright (C) 2010-2011 Canonical Ltd ++ * Copyright (C) 2011-2012 Linaro Ltd ++ * ++ * 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. ++ * ++ * Standard functionality for the common clock API. See Documentation/clk.txt ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "clk.h" ++ ++static DEFINE_SPINLOCK(enable_lock); ++static DEFINE_MUTEX(prepare_lock); ++ ++static struct task_struct *prepare_owner; ++static struct task_struct *enable_owner; ++ ++static int prepare_refcnt; ++static int enable_refcnt; ++ ++static HLIST_HEAD(clk_root_list); ++static HLIST_HEAD(clk_orphan_list); ++static LIST_HEAD(clk_notifier_list); ++ ++/*** locking ***/ ++static void clk_prepare_lock(void) ++{ ++ if (!mutex_trylock(&prepare_lock)) { ++ if (prepare_owner == current) { ++ prepare_refcnt++; ++ return; ++ } ++ mutex_lock(&prepare_lock); ++ } ++ WARN_ON_ONCE(prepare_owner != NULL); ++ WARN_ON_ONCE(prepare_refcnt != 0); ++ prepare_owner = current; ++ prepare_refcnt = 1; ++} ++ ++static void clk_prepare_unlock(void) ++{ ++ WARN_ON_ONCE(prepare_owner != current); ++ WARN_ON_ONCE(prepare_refcnt == 0); ++ ++ if (--prepare_refcnt) ++ return; ++ prepare_owner = NULL; ++ mutex_unlock(&prepare_lock); ++} ++ ++static unsigned long clk_enable_lock(void) ++{ ++ unsigned long flags; ++ ++ if (!spin_trylock_irqsave(&enable_lock, flags)) { ++ if (enable_owner == current) { ++ enable_refcnt++; ++ return flags; ++ } ++ spin_lock_irqsave(&enable_lock, flags); ++ } ++ WARN_ON_ONCE(enable_owner != NULL); ++ WARN_ON_ONCE(enable_refcnt != 0); ++ enable_owner = current; ++ enable_refcnt = 1; ++ return flags; ++} ++ ++static void clk_enable_unlock(unsigned long flags) ++{ ++ WARN_ON_ONCE(enable_owner != current); ++ WARN_ON_ONCE(enable_refcnt == 0); ++ ++ if (--enable_refcnt) ++ return; ++ enable_owner = NULL; ++ spin_unlock_irqrestore(&enable_lock, flags); ++} ++ ++/*** debugfs support ***/ ++ ++#ifdef CONFIG_DEBUG_FS ++#include ++ ++static struct dentry *rootdir; ++static struct dentry *orphandir; ++static int inited = 0; ++ ++static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) ++{ ++ if (!c) ++ return; ++ ++ seq_printf(s, "%*s%-*s %-11d %-12d %-10lu %-11lu", ++ level * 3 + 1, "", ++ 30 - level * 3, c->name, ++ c->enable_count, c->prepare_count, clk_get_rate(c), ++ clk_get_accuracy(c)); ++ seq_printf(s, "\n"); ++} ++ ++static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, ++ int level) ++{ ++ struct clk *child; ++ ++ if (!c) ++ return; ++ ++ clk_summary_show_one(s, c, level); ++ ++ hlist_for_each_entry(child, &c->children, child_node) ++ clk_summary_show_subtree(s, child, level + 1); ++} ++ ++static int clk_summary_show(struct seq_file *s, void *data) ++{ ++ struct clk *c; ++ ++ seq_printf(s, " clock enable_cnt prepare_cnt rate accuracy\n"); ++ seq_printf(s, "---------------------------------------------------------------------------------\n"); ++ ++ clk_prepare_lock(); ++ ++ hlist_for_each_entry(c, &clk_root_list, child_node) ++ clk_summary_show_subtree(s, c, 0); ++ ++ hlist_for_each_entry(c, &clk_orphan_list, child_node) ++ clk_summary_show_subtree(s, c, 0); ++ ++ clk_prepare_unlock(); ++ ++ return 0; ++} ++ ++ ++static int clk_summary_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, clk_summary_show, inode->i_private); ++} ++ ++static const struct file_operations clk_summary_fops = { ++ .open = clk_summary_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void clk_dump_one(struct seq_file *s, struct clk *c, int level) ++{ ++ if (!c) ++ return; ++ ++ seq_printf(s, "\"%s\": { ", c->name); ++ seq_printf(s, "\"enable_count\": %d,", c->enable_count); ++ seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); ++ seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); ++ seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c)); ++} ++ ++static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) ++{ ++ struct clk *child; ++ ++ if (!c) ++ return; ++ ++ clk_dump_one(s, c, level); ++ ++ hlist_for_each_entry(child, &c->children, child_node) { ++ seq_printf(s, ","); ++ clk_dump_subtree(s, child, level + 1); ++ } ++ ++ seq_printf(s, "}"); ++} ++ ++static int clk_dump(struct seq_file *s, void *data) ++{ ++ struct clk *c; ++ bool first_node = true; ++ ++ seq_printf(s, "{"); ++ ++ clk_prepare_lock(); ++ ++ hlist_for_each_entry(c, &clk_root_list, child_node) { ++ if (!first_node) ++ seq_printf(s, ","); ++ first_node = false; ++ clk_dump_subtree(s, c, 0); ++ } ++ ++ hlist_for_each_entry(c, &clk_orphan_list, child_node) { ++ seq_printf(s, ","); ++ clk_dump_subtree(s, c, 0); ++ } ++ ++ clk_prepare_unlock(); ++ ++ seq_printf(s, "}"); ++ return 0; ++} ++ ++ ++static int clk_dump_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, clk_dump, inode->i_private); ++} ++ ++static const struct file_operations clk_dump_fops = { ++ .open = clk_dump_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++/* caller must hold prepare_lock */ ++static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) ++{ ++ struct dentry *d; ++ int ret = -ENOMEM; ++ ++ if (!clk || !pdentry) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ d = debugfs_create_dir(clk->name, pdentry); ++ if (!d) ++ goto out; ++ ++ clk->dentry = d; ++ ++ d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry, ++ (u32 *)&clk->rate); ++ if (!d) ++ goto err_out; ++ ++ d = debugfs_create_u32("clk_accuracy", S_IRUGO, clk->dentry, ++ (u32 *)&clk->accuracy); ++ if (!d) ++ goto err_out; ++ ++ d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry, ++ (u32 *)&clk->flags); ++ if (!d) ++ goto err_out; ++ ++ d = debugfs_create_u32("clk_prepare_count", S_IRUGO, clk->dentry, ++ (u32 *)&clk->prepare_count); ++ if (!d) ++ goto err_out; ++ ++ d = debugfs_create_u32("clk_enable_count", S_IRUGO, clk->dentry, ++ (u32 *)&clk->enable_count); ++ if (!d) ++ goto err_out; ++ ++ d = debugfs_create_u32("clk_notifier_count", S_IRUGO, clk->dentry, ++ (u32 *)&clk->notifier_count); ++ if (!d) ++ goto err_out; ++ ++ ret = 0; ++ goto out; ++ ++err_out: ++ debugfs_remove_recursive(clk->dentry); ++ clk->dentry = NULL; ++out: ++ return ret; ++} ++ ++/* caller must hold prepare_lock */ ++static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry) ++{ ++ struct clk *child; ++ int ret = -EINVAL;; ++ ++ if (!clk || !pdentry) ++ goto out; ++ ++ ret = clk_debug_create_one(clk, pdentry); ++ ++ if (ret) ++ goto out; ++ ++ hlist_for_each_entry(child, &clk->children, child_node) ++ clk_debug_create_subtree(child, clk->dentry); ++ ++ ret = 0; ++out: ++ return ret; ++} ++ ++/** ++ * clk_debug_register - add a clk node to the debugfs clk tree ++ * @clk: the clk being added to the debugfs clk tree ++ * ++ * Dynamically adds a clk to the debugfs clk tree if debugfs has been ++ * initialized. Otherwise it bails out early since the debugfs clk tree ++ * will be created lazily by clk_debug_init as part of a late_initcall. ++ * ++ * Caller must hold prepare_lock. Only clk_init calls this function (so ++ * far) so this is taken care. ++ */ ++static int clk_debug_register(struct clk *clk) ++{ ++ struct clk *parent; ++ struct dentry *pdentry; ++ int ret = 0; ++ ++ if (!inited) ++ goto out; ++ ++ parent = clk->parent; ++ ++ /* ++ * Check to see if a clk is a root clk. Also check that it is ++ * safe to add this clk to debugfs ++ */ ++ if (!parent) ++ if (clk->flags & CLK_IS_ROOT) ++ pdentry = rootdir; ++ else ++ pdentry = orphandir; ++ else ++ if (parent->dentry) ++ pdentry = parent->dentry; ++ else ++ goto out; ++ ++ ret = clk_debug_create_subtree(clk, pdentry); ++ ++out: ++ return ret; ++} ++ ++ /** ++ * clk_debug_unregister - remove a clk node from the debugfs clk tree ++ * @clk: the clk being removed from the debugfs clk tree ++ * ++ * Dynamically removes a clk and all it's children clk nodes from the ++ * debugfs clk tree if clk->dentry points to debugfs created by ++ * clk_debug_register in __clk_init. ++ * ++ * Caller must hold prepare_lock. ++ */ ++static void clk_debug_unregister(struct clk *clk) ++{ ++ debugfs_remove_recursive(clk->dentry); ++} ++ ++/** ++ * clk_debug_reparent - reparent clk node in the debugfs clk tree ++ * @clk: the clk being reparented ++ * @new_parent: the new clk parent, may be NULL ++ * ++ * Rename clk entry in the debugfs clk tree if debugfs has been ++ * initialized. Otherwise it bails out early since the debugfs clk tree ++ * will be created lazily by clk_debug_init as part of a late_initcall. ++ * ++ * Caller must hold prepare_lock. ++ */ ++static void clk_debug_reparent(struct clk *clk, struct clk *new_parent) ++{ ++ struct dentry *d; ++ struct dentry *new_parent_d; ++ ++ if (!inited) ++ return; ++ ++ if (new_parent) ++ new_parent_d = new_parent->dentry; ++ else ++ new_parent_d = orphandir; ++ ++ d = debugfs_rename(clk->dentry->d_parent, clk->dentry, ++ new_parent_d, clk->name); ++ if (d) ++ clk->dentry = d; ++ else ++ pr_debug("%s: failed to rename debugfs entry for %s\n", ++ __func__, clk->name); ++} ++ ++/** ++ * clk_debug_init - lazily create the debugfs clk tree visualization ++ * ++ * clks are often initialized very early during boot before memory can ++ * be dynamically allocated and well before debugfs is setup. ++ * clk_debug_init walks the clk tree hierarchy while holding ++ * prepare_lock and creates the topology as part of a late_initcall, ++ * thus insuring that clks initialized very early will still be ++ * represented in the debugfs clk tree. This function should only be ++ * called once at boot-time, and all other clks added dynamically will ++ * be done so with clk_debug_register. ++ */ ++static int __init clk_debug_init(void) ++{ ++ struct clk *clk; ++ struct dentry *d; ++ ++ rootdir = debugfs_create_dir("clk", NULL); ++ ++ if (!rootdir) ++ return -ENOMEM; ++ ++ d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, NULL, ++ &clk_summary_fops); ++ if (!d) ++ return -ENOMEM; ++ ++ d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, NULL, ++ &clk_dump_fops); ++ if (!d) ++ return -ENOMEM; ++ ++ orphandir = debugfs_create_dir("orphans", rootdir); ++ ++ if (!orphandir) ++ return -ENOMEM; ++ ++ clk_prepare_lock(); ++ ++ hlist_for_each_entry(clk, &clk_root_list, child_node) ++ clk_debug_create_subtree(clk, rootdir); ++ ++ hlist_for_each_entry(clk, &clk_orphan_list, child_node) ++ clk_debug_create_subtree(clk, orphandir); ++ ++ inited = 1; ++ ++ clk_prepare_unlock(); ++ ++ return 0; ++} ++late_initcall(clk_debug_init); ++#else ++static inline int clk_debug_register(struct clk *clk) { return 0; } ++static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent) ++{ ++} ++static inline void clk_debug_unregister(struct clk *clk) ++{ ++} ++#endif ++ ++/* caller must hold prepare_lock */ ++static void clk_unprepare_unused_subtree(struct clk *clk) ++{ ++ struct clk *child; ++ ++ if (!clk) ++ return; ++ ++ hlist_for_each_entry(child, &clk->children, child_node) ++ clk_unprepare_unused_subtree(child); ++ ++ if (clk->prepare_count) ++ return; ++ ++ if (clk->flags & CLK_IGNORE_UNUSED) ++ return; ++ ++ if (__clk_is_prepared(clk)) { ++ if (clk->ops->unprepare_unused) ++ clk->ops->unprepare_unused(clk->hw); ++ else if (clk->ops->unprepare) ++ clk->ops->unprepare(clk->hw); ++ } ++} ++ ++/* caller must hold prepare_lock */ ++static void clk_disable_unused_subtree(struct clk *clk) ++{ ++ struct clk *child; ++ unsigned long flags; ++ ++ if (!clk) ++ goto out; ++ ++ hlist_for_each_entry(child, &clk->children, child_node) ++ clk_disable_unused_subtree(child); ++ ++ flags = clk_enable_lock(); ++ ++ if (clk->enable_count) ++ goto unlock_out; ++ ++ if (clk->flags & CLK_IGNORE_UNUSED) ++ goto unlock_out; ++ ++ /* ++ * some gate clocks have special needs during the disable-unused ++ * sequence. call .disable_unused if available, otherwise fall ++ * back to .disable ++ */ ++ if (__clk_is_enabled(clk)) { ++ if (clk->ops->disable_unused) ++ clk->ops->disable_unused(clk->hw); ++ else if (clk->ops->disable) ++ clk->ops->disable(clk->hw); ++ } ++ ++unlock_out: ++ clk_enable_unlock(flags); ++ ++out: ++ return; ++} ++ ++static bool clk_ignore_unused; ++static int __init clk_ignore_unused_setup(char *__unused) ++{ ++ clk_ignore_unused = true; ++ return 1; ++} ++__setup("clk_ignore_unused", clk_ignore_unused_setup); ++ ++static int clk_disable_unused(void) ++{ ++ struct clk *clk; ++ ++ if (clk_ignore_unused) { ++ pr_warn("clk: Not disabling unused clocks\n"); ++ return 0; ++ } ++ ++ clk_prepare_lock(); ++ ++ hlist_for_each_entry(clk, &clk_root_list, child_node) ++ clk_disable_unused_subtree(clk); ++ ++ hlist_for_each_entry(clk, &clk_orphan_list, child_node) ++ clk_disable_unused_subtree(clk); ++ ++ hlist_for_each_entry(clk, &clk_root_list, child_node) ++ clk_unprepare_unused_subtree(clk); ++ ++ hlist_for_each_entry(clk, &clk_orphan_list, child_node) ++ clk_unprepare_unused_subtree(clk); ++ ++ clk_prepare_unlock(); ++ ++ return 0; ++} ++late_initcall_sync(clk_disable_unused); ++ ++/*** helper functions ***/ ++ ++const char *__clk_get_name(struct clk *clk) ++{ ++ return !clk ? NULL : clk->name; ++} ++EXPORT_SYMBOL_GPL(__clk_get_name); ++ ++struct clk_hw *__clk_get_hw(struct clk *clk) ++{ ++ return !clk ? NULL : clk->hw; ++} ++EXPORT_SYMBOL_GPL(__clk_get_hw); ++ ++u8 __clk_get_num_parents(struct clk *clk) ++{ ++ return !clk ? 0 : clk->num_parents; ++} ++EXPORT_SYMBOL_GPL(__clk_get_num_parents); ++ ++struct clk *__clk_get_parent(struct clk *clk) ++{ ++ return !clk ? NULL : clk->parent; ++} ++EXPORT_SYMBOL_GPL(__clk_get_parent); ++ ++struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) ++{ ++ if (!clk || index >= clk->num_parents) ++ return NULL; ++ else if (!clk->parents) ++ return __clk_lookup(clk->parent_names[index]); ++ else if (!clk->parents[index]) ++ return clk->parents[index] = ++ __clk_lookup(clk->parent_names[index]); ++ else ++ return clk->parents[index]; ++} ++EXPORT_SYMBOL_GPL(clk_get_parent_by_index); ++ ++unsigned int __clk_get_enable_count(struct clk *clk) ++{ ++ return !clk ? 0 : clk->enable_count; ++} ++ ++unsigned int __clk_get_prepare_count(struct clk *clk) ++{ ++ return !clk ? 0 : clk->prepare_count; ++} ++ ++unsigned long __clk_get_rate(struct clk *clk) ++{ ++ unsigned long ret; ++ ++ if (!clk) { ++ ret = 0; ++ goto out; ++ } ++ ++ ret = clk->rate; ++ ++ if (clk->flags & CLK_IS_ROOT) ++ goto out; ++ ++ if (!clk->parent) ++ ret = 0; ++ ++out: ++ return ret; ++} ++EXPORT_SYMBOL_GPL(__clk_get_rate); ++ ++unsigned long __clk_get_accuracy(struct clk *clk) ++{ ++ if (!clk) ++ return 0; ++ ++ return clk->accuracy; ++} ++ ++unsigned long __clk_get_flags(struct clk *clk) ++{ ++ return !clk ? 0 : clk->flags; ++} ++EXPORT_SYMBOL_GPL(__clk_get_flags); ++ ++bool __clk_is_prepared(struct clk *clk) ++{ ++ int ret; ++ ++ if (!clk) ++ return false; ++ ++ /* ++ * .is_prepared is optional for clocks that can prepare ++ * fall back to software usage counter if it is missing ++ */ ++ if (!clk->ops->is_prepared) { ++ ret = clk->prepare_count ? 1 : 0; ++ goto out; ++ } ++ ++ ret = clk->ops->is_prepared(clk->hw); ++out: ++ return !!ret; ++} ++ ++bool __clk_is_enabled(struct clk *clk) ++{ ++ int ret; ++ ++ if (!clk) ++ return false; ++ ++ /* ++ * .is_enabled is only mandatory for clocks that gate ++ * fall back to software usage counter if .is_enabled is missing ++ */ ++ if (!clk->ops->is_enabled) { ++ ret = clk->enable_count ? 1 : 0; ++ goto out; ++ } ++ ++ ret = clk->ops->is_enabled(clk->hw); ++out: ++ return !!ret; ++} ++EXPORT_SYMBOL_GPL(__clk_is_enabled); ++ ++static struct clk *__clk_lookup_subtree(const char *name, struct clk *clk) ++{ ++ struct clk *child; ++ struct clk *ret; ++ ++ if (!strcmp(clk->name, name)) ++ return clk; ++ ++ hlist_for_each_entry(child, &clk->children, child_node) { ++ ret = __clk_lookup_subtree(name, child); ++ if (ret) ++ return ret; ++ } ++ ++ return NULL; ++} ++ ++struct clk *__clk_lookup(const char *name) ++{ ++ struct clk *root_clk; ++ struct clk *ret; ++ ++ if (!name) ++ return NULL; ++ ++ /* search the 'proper' clk tree first */ ++ hlist_for_each_entry(root_clk, &clk_root_list, child_node) { ++ ret = __clk_lookup_subtree(name, root_clk); ++ if (ret) ++ return ret; ++ } ++ ++ /* if not found, then search the orphan tree */ ++ hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) { ++ ret = __clk_lookup_subtree(name, root_clk); ++ if (ret) ++ return ret; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Helper for finding best parent to provide a given frequency. This can be used ++ * directly as a determine_rate callback (e.g. for a mux), or from a more ++ * complex clock that may combine a mux with other operations. ++ */ ++long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *best_parent_rate, ++ struct clk **best_parent_p) ++{ ++ struct clk *clk = hw->clk, *parent, *best_parent = NULL; ++ int i, num_parents; ++ unsigned long parent_rate, best = 0; ++ ++ /* if NO_REPARENT flag set, pass through to current parent */ ++ if (clk->flags & CLK_SET_RATE_NO_REPARENT) { ++ parent = clk->parent; ++ if (clk->flags & CLK_SET_RATE_PARENT) ++ best = __clk_round_rate(parent, rate); ++ else if (parent) ++ best = __clk_get_rate(parent); ++ else ++ best = __clk_get_rate(clk); ++ goto out; ++ } ++ ++ /* find the parent that can provide the fastest rate <= rate */ ++ num_parents = clk->num_parents; ++ for (i = 0; i < num_parents; i++) { ++ parent = clk_get_parent_by_index(clk, i); ++ if (!parent) ++ continue; ++ if (clk->flags & CLK_SET_RATE_PARENT) ++ parent_rate = __clk_round_rate(parent, rate); ++ else ++ parent_rate = __clk_get_rate(parent); ++ if (parent_rate <= rate && parent_rate > best) { ++ best_parent = parent; ++ best = parent_rate; ++ } ++ } ++ ++out: ++ if (best_parent) ++ *best_parent_p = best_parent; ++ *best_parent_rate = best; ++ ++ return best; ++} ++EXPORT_SYMBOL_GPL(__clk_mux_determine_rate); ++ ++/*** clk api ***/ ++ ++void __clk_unprepare(struct clk *clk) ++{ ++ if (!clk) ++ return; ++ ++ if (WARN_ON(clk->prepare_count == 0)) ++ return; ++ ++ if (--clk->prepare_count > 0) ++ return; ++ ++ WARN_ON(clk->enable_count > 0); ++ ++ if (clk->ops->unprepare) ++ clk->ops->unprepare(clk->hw); ++ ++ __clk_unprepare(clk->parent); ++} ++ ++/** ++ * clk_unprepare - undo preparation of a clock source ++ * @clk: the clk being unprepared ++ * ++ * clk_unprepare may sleep, which differentiates it from clk_disable. In a ++ * simple case, clk_unprepare can be used instead of clk_disable to gate a clk ++ * if the operation may sleep. One example is a clk which is accessed over ++ * I2c. In the complex case a clk gate operation may require a fast and a slow ++ * part. It is this reason that clk_unprepare and clk_disable are not mutually ++ * exclusive. In fact clk_disable must be called before clk_unprepare. ++ */ ++void clk_unprepare(struct clk *clk) ++{ ++ clk_prepare_lock(); ++ __clk_unprepare(clk); ++ clk_prepare_unlock(); ++} ++EXPORT_SYMBOL_GPL(clk_unprepare); ++ ++int __clk_prepare(struct clk *clk) ++{ ++ int ret = 0; ++ ++ if (!clk) ++ return 0; ++ ++ if (clk->prepare_count == 0) { ++ ret = __clk_prepare(clk->parent); ++ if (ret) ++ return ret; ++ ++ if (clk->ops->prepare) { ++ ret = clk->ops->prepare(clk->hw); ++ if (ret) { ++ __clk_unprepare(clk->parent); ++ return ret; ++ } ++ } ++ } ++ ++ clk->prepare_count++; ++ ++ return 0; ++} ++ ++/** ++ * clk_prepare - prepare a clock source ++ * @clk: the clk being prepared ++ * ++ * clk_prepare may sleep, which differentiates it from clk_enable. In a simple ++ * case, clk_prepare can be used instead of clk_enable to ungate a clk if the ++ * operation may sleep. One example is a clk which is accessed over I2c. In ++ * the complex case a clk ungate operation may require a fast and a slow part. ++ * It is this reason that clk_prepare and clk_enable are not mutually ++ * exclusive. In fact clk_prepare must be called before clk_enable. ++ * Returns 0 on success, -EERROR otherwise. ++ */ ++int clk_prepare(struct clk *clk) ++{ ++ int ret; ++ ++ clk_prepare_lock(); ++ ret = __clk_prepare(clk); ++ clk_prepare_unlock(); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(clk_prepare); ++ ++static void __clk_disable(struct clk *clk) ++{ ++ if (!clk) ++ return; ++ ++ if (WARN_ON(IS_ERR(clk))) ++ return; ++ ++ if (WARN_ON(clk->enable_count == 0)) ++ return; ++ ++ if (--clk->enable_count > 0) ++ return; ++ ++ if (clk->ops->disable) ++ clk->ops->disable(clk->hw); ++ ++ __clk_disable(clk->parent); ++} ++ ++/** ++ * clk_disable - gate a clock ++ * @clk: the clk being gated ++ * ++ * clk_disable must not sleep, which differentiates it from clk_unprepare. In ++ * a simple case, clk_disable can be used instead of clk_unprepare to gate a ++ * clk if the operation is fast and will never sleep. One example is a ++ * SoC-internal clk which is controlled via simple register writes. In the ++ * complex case a clk gate operation may require a fast and a slow part. It is ++ * this reason that clk_unprepare and clk_disable are not mutually exclusive. ++ * In fact clk_disable must be called before clk_unprepare. ++ */ ++void clk_disable(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ flags = clk_enable_lock(); ++ __clk_disable(clk); ++ clk_enable_unlock(flags); ++} ++EXPORT_SYMBOL_GPL(clk_disable); ++ ++static int __clk_enable(struct clk *clk) ++{ ++ int ret = 0; ++ ++ if (!clk) ++ return 0; ++ ++ if (WARN_ON(clk->prepare_count == 0)) ++ return -ESHUTDOWN; ++ ++ if (clk->enable_count == 0) { ++ ret = __clk_enable(clk->parent); ++ ++ if (ret) ++ return ret; ++ ++ if (clk->ops->enable) { ++ ret = clk->ops->enable(clk->hw); ++ if (ret) { ++ __clk_disable(clk->parent); ++ return ret; ++ } ++ } ++ } ++ ++ clk->enable_count++; ++ return 0; ++} ++ ++/** ++ * clk_enable - ungate a clock ++ * @clk: the clk being ungated ++ * ++ * clk_enable must not sleep, which differentiates it from clk_prepare. In a ++ * simple case, clk_enable can be used instead of clk_prepare to ungate a clk ++ * if the operation will never sleep. One example is a SoC-internal clk which ++ * is controlled via simple register writes. In the complex case a clk ungate ++ * operation may require a fast and a slow part. It is this reason that ++ * clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare ++ * must be called before clk_enable. Returns 0 on success, -EERROR ++ * otherwise. ++ */ ++int clk_enable(struct clk *clk) ++{ ++ unsigned long flags; ++ int ret; ++ ++ flags = clk_enable_lock(); ++ ret = __clk_enable(clk); ++ clk_enable_unlock(flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(clk_enable); ++ ++/** ++ * __clk_round_rate - round the given rate for a clk ++ * @clk: round the rate of this clock ++ * @rate: the rate which is to be rounded ++ * ++ * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate ++ */ ++unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ unsigned long parent_rate = 0; ++ struct clk *parent; ++ ++ if (!clk) ++ return 0; ++ ++ parent = clk->parent; ++ if (parent) ++ parent_rate = parent->rate; ++ ++ if (clk->ops->determine_rate) ++ return clk->ops->determine_rate(clk->hw, rate, &parent_rate, ++ &parent); ++ else if (clk->ops->round_rate) ++ return clk->ops->round_rate(clk->hw, rate, &parent_rate); ++ else if (clk->flags & CLK_SET_RATE_PARENT) ++ return __clk_round_rate(clk->parent, rate); ++ else ++ return clk->rate; ++} ++ ++/** ++ * clk_round_rate - round the given rate for a clk ++ * @clk: the clk for which we are rounding a rate ++ * @rate: the rate which is to be rounded ++ * ++ * Takes in a rate as input and rounds it to a rate that the clk can actually ++ * use which is then returned. If clk doesn't support round_rate operation ++ * then the parent rate is returned. ++ */ ++long clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ unsigned long ret; ++ ++ clk_prepare_lock(); ++ ret = __clk_round_rate(clk, rate); ++ clk_prepare_unlock(); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(clk_round_rate); ++ ++/** ++ * __clk_notify - call clk notifier chain ++ * @clk: struct clk * that is changing rate ++ * @msg: clk notifier type (see include/linux/clk.h) ++ * @old_rate: old clk rate ++ * @new_rate: new clk rate ++ * ++ * Triggers a notifier call chain on the clk rate-change notification ++ * for 'clk'. Passes a pointer to the struct clk and the previous ++ * and current rates to the notifier callback. Intended to be called by ++ * internal clock code only. Returns NOTIFY_DONE from the last driver ++ * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if ++ * a driver returns that. ++ */ ++static int __clk_notify(struct clk *clk, unsigned long msg, ++ unsigned long old_rate, unsigned long new_rate) ++{ ++ struct clk_notifier *cn; ++ struct clk_notifier_data cnd; ++ int ret = NOTIFY_DONE; ++ ++ cnd.clk = clk; ++ cnd.old_rate = old_rate; ++ cnd.new_rate = new_rate; ++ ++ list_for_each_entry(cn, &clk_notifier_list, node) { ++ if (cn->clk == clk) { ++ ret = srcu_notifier_call_chain(&cn->notifier_head, msg, ++ &cnd); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++/** ++ * __clk_recalc_accuracies ++ * @clk: first clk in the subtree ++ * ++ * Walks the subtree of clks starting with clk and recalculates accuracies as ++ * it goes. Note that if a clk does not implement the .recalc_accuracy ++ * callback then it is assumed that the clock will take on the accuracy of it's ++ * parent. ++ * ++ * Caller must hold prepare_lock. ++ */ ++static void __clk_recalc_accuracies(struct clk *clk) ++{ ++ unsigned long parent_accuracy = 0; ++ struct clk *child; ++ ++ if (clk->parent) ++ parent_accuracy = clk->parent->accuracy; ++ ++ if (clk->ops->recalc_accuracy) ++ clk->accuracy = clk->ops->recalc_accuracy(clk->hw, ++ parent_accuracy); ++ else ++ clk->accuracy = parent_accuracy; ++ ++ hlist_for_each_entry(child, &clk->children, child_node) ++ __clk_recalc_accuracies(child); ++} ++ ++/** ++ * clk_get_accuracy - return the accuracy of clk ++ * @clk: the clk whose accuracy is being returned ++ * ++ * Simply returns the cached accuracy of the clk, unless ++ * CLK_GET_ACCURACY_NOCACHE flag is set, which means a recalc_rate will be ++ * issued. ++ * If clk is NULL then returns 0. ++ */ ++long clk_get_accuracy(struct clk *clk) ++{ ++ unsigned long accuracy; ++ ++ clk_prepare_lock(); ++ if (clk && (clk->flags & CLK_GET_ACCURACY_NOCACHE)) ++ __clk_recalc_accuracies(clk); ++ ++ accuracy = __clk_get_accuracy(clk); ++ clk_prepare_unlock(); ++ ++ return accuracy; ++} ++EXPORT_SYMBOL_GPL(clk_get_accuracy); ++ ++/** ++ * __clk_recalc_rates ++ * @clk: first clk in the subtree ++ * @msg: notification type (see include/linux/clk.h) ++ * ++ * Walks the subtree of clks starting with clk and recalculates rates as it ++ * goes. Note that if a clk does not implement the .recalc_rate callback then ++ * it is assumed that the clock will take on the rate of its parent. ++ * ++ * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, ++ * if necessary. ++ * ++ * Caller must hold prepare_lock. ++ */ ++static void __clk_recalc_rates(struct clk *clk, unsigned long msg) ++{ ++ unsigned long old_rate; ++ unsigned long parent_rate = 0; ++ struct clk *child; ++ ++ old_rate = clk->rate; ++ ++ if (clk->parent) ++ parent_rate = clk->parent->rate; ++ ++ if (clk->ops->recalc_rate) ++ clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate); ++ else ++ clk->rate = parent_rate; ++ ++ /* ++ * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE ++ * & ABORT_RATE_CHANGE notifiers ++ */ ++ if (clk->notifier_count && msg) ++ __clk_notify(clk, msg, old_rate, clk->rate); ++ ++ hlist_for_each_entry(child, &clk->children, child_node) ++ __clk_recalc_rates(child, msg); ++} ++ ++/** ++ * clk_get_rate - return the rate of clk ++ * @clk: the clk whose rate is being returned ++ * ++ * Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag ++ * is set, which means a recalc_rate will be issued. ++ * If clk is NULL then returns 0. ++ */ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ unsigned long rate; ++ ++ clk_prepare_lock(); ++ ++ if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) ++ __clk_recalc_rates(clk, 0); ++ ++ rate = __clk_get_rate(clk); ++ clk_prepare_unlock(); ++ ++ return rate; ++} ++EXPORT_SYMBOL_GPL(clk_get_rate); ++ ++static int clk_fetch_parent_index(struct clk *clk, struct clk *parent) ++{ ++ int i; ++ ++ if (!clk->parents) { ++ clk->parents = kcalloc(clk->num_parents, ++ sizeof(struct clk *), GFP_KERNEL); ++ if (!clk->parents) ++ return -ENOMEM; ++ } ++ ++ /* ++ * find index of new parent clock using cached parent ptrs, ++ * or if not yet cached, use string name comparison and cache ++ * them now to avoid future calls to __clk_lookup. ++ */ ++ for (i = 0; i < clk->num_parents; i++) { ++ if (clk->parents[i] == parent) ++ return i; ++ ++ if (clk->parents[i]) ++ continue; ++ ++ if (!strcmp(clk->parent_names[i], parent->name)) { ++ clk->parents[i] = __clk_lookup(parent->name); ++ return i; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static void clk_reparent(struct clk *clk, struct clk *new_parent) ++{ ++ hlist_del(&clk->child_node); ++ ++ if (new_parent) { ++ /* avoid duplicate POST_RATE_CHANGE notifications */ ++ if (new_parent->new_child == clk) ++ new_parent->new_child = NULL; ++ ++ hlist_add_head(&clk->child_node, &new_parent->children); ++ } else { ++ hlist_add_head(&clk->child_node, &clk_orphan_list); ++ } ++ ++ clk->parent = new_parent; ++} ++ ++static struct clk *__clk_set_parent_before(struct clk *clk, struct clk *parent) ++{ ++ unsigned long flags; ++ struct clk *old_parent = clk->parent; ++ ++ /* ++ * Migrate prepare state between parents and prevent race with ++ * clk_enable(). ++ * ++ * If the clock is not prepared, then a race with ++ * clk_enable/disable() is impossible since we already have the ++ * prepare lock (future calls to clk_enable() need to be preceded by ++ * a clk_prepare()). ++ * ++ * If the clock is prepared, migrate the prepared state to the new ++ * parent and also protect against a race with clk_enable() by ++ * forcing the clock and the new parent on. This ensures that all ++ * future calls to clk_enable() are practically NOPs with respect to ++ * hardware and software states. ++ * ++ * See also: Comment for clk_set_parent() below. ++ */ ++ if (clk->prepare_count) { ++ __clk_prepare(parent); ++ clk_enable(parent); ++ clk_enable(clk); ++ } ++ ++ /* update the clk tree topology */ ++ flags = clk_enable_lock(); ++ clk_reparent(clk, parent); ++ clk_enable_unlock(flags); ++ ++ return old_parent; ++} ++ ++static void __clk_set_parent_after(struct clk *clk, struct clk *parent, ++ struct clk *old_parent) ++{ ++ /* ++ * Finish the migration of prepare state and undo the changes done ++ * for preventing a race with clk_enable(). ++ */ ++ if (clk->prepare_count) { ++ clk_disable(clk); ++ clk_disable(old_parent); ++ __clk_unprepare(old_parent); ++ } ++ ++ /* update debugfs with new clk tree topology */ ++ clk_debug_reparent(clk, parent); ++} ++ ++static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) ++{ ++ unsigned long flags; ++ int ret = 0; ++ struct clk *old_parent; ++ ++ old_parent = __clk_set_parent_before(clk, parent); ++ ++ /* change clock input source */ ++ if (parent && clk->ops->set_parent) ++ ret = clk->ops->set_parent(clk->hw, p_index); ++ ++ if (ret) { ++ flags = clk_enable_lock(); ++ clk_reparent(clk, old_parent); ++ clk_enable_unlock(flags); ++ ++ if (clk->prepare_count) { ++ clk_disable(clk); ++ clk_disable(parent); ++ __clk_unprepare(parent); ++ } ++ return ret; ++ } ++ ++ __clk_set_parent_after(clk, parent, old_parent); ++ ++ return 0; ++} ++ ++/** ++ * __clk_speculate_rates ++ * @clk: first clk in the subtree ++ * @parent_rate: the "future" rate of clk's parent ++ * ++ * Walks the subtree of clks starting with clk, speculating rates as it ++ * goes and firing off PRE_RATE_CHANGE notifications as necessary. ++ * ++ * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending ++ * pre-rate change notifications and returns early if no clks in the ++ * subtree have subscribed to the notifications. Note that if a clk does not ++ * implement the .recalc_rate callback then it is assumed that the clock will ++ * take on the rate of its parent. ++ * ++ * Caller must hold prepare_lock. ++ */ ++static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) ++{ ++ struct clk *child; ++ unsigned long new_rate; ++ int ret = NOTIFY_DONE; ++ ++ if (clk->ops->recalc_rate) ++ new_rate = clk->ops->recalc_rate(clk->hw, parent_rate); ++ else ++ new_rate = parent_rate; ++ ++ /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ ++ if (clk->notifier_count) ++ ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); ++ ++ if (ret & NOTIFY_STOP_MASK) ++ goto out; ++ ++ hlist_for_each_entry(child, &clk->children, child_node) { ++ ret = __clk_speculate_rates(child, new_rate); ++ if (ret & NOTIFY_STOP_MASK) ++ break; ++ } ++ ++out: ++ return ret; ++} ++ ++static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, ++ struct clk *new_parent, u8 p_index) ++{ ++ struct clk *child; ++ ++ clk->new_rate = new_rate; ++ clk->new_parent = new_parent; ++ clk->new_parent_index = p_index; ++ /* include clk in new parent's PRE_RATE_CHANGE notifications */ ++ clk->new_child = NULL; ++ if (new_parent && new_parent != clk->parent) ++ new_parent->new_child = clk; ++ ++ hlist_for_each_entry(child, &clk->children, child_node) { ++ if (child->ops->recalc_rate) ++ child->new_rate = child->ops->recalc_rate(child->hw, new_rate); ++ else ++ child->new_rate = new_rate; ++ clk_calc_subtree(child, child->new_rate, NULL, 0); ++ } ++} ++ ++/* ++ * calculate the new rates returning the topmost clock that has to be ++ * changed. ++ */ ++static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) ++{ ++ struct clk *top = clk; ++ struct clk *old_parent, *parent; ++ unsigned long best_parent_rate = 0; ++ unsigned long new_rate; ++ int p_index = 0; ++ ++ /* sanity */ ++ if (IS_ERR_OR_NULL(clk)) ++ return NULL; ++ ++ /* save parent rate, if it exists */ ++ parent = old_parent = clk->parent; ++ if (parent) ++ best_parent_rate = parent->rate; ++ ++ /* find the closest rate and parent clk/rate */ ++ if (clk->ops->determine_rate) { ++ new_rate = clk->ops->determine_rate(clk->hw, rate, ++ &best_parent_rate, ++ &parent); ++ } else if (clk->ops->round_rate) { ++ new_rate = clk->ops->round_rate(clk->hw, rate, ++ &best_parent_rate); ++ } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) { ++ /* pass-through clock without adjustable parent */ ++ clk->new_rate = clk->rate; ++ return NULL; ++ } else { ++ /* pass-through clock with adjustable parent */ ++ top = clk_calc_new_rates(parent, rate); ++ new_rate = parent->new_rate; ++ goto out; ++ } ++ ++ /* some clocks must be gated to change parent */ ++ if (parent != old_parent && ++ (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { ++ pr_debug("%s: %s not gated but wants to reparent\n", ++ __func__, clk->name); ++ return NULL; ++ } ++ ++ /* try finding the new parent index */ ++ if (parent) { ++ p_index = clk_fetch_parent_index(clk, parent); ++ if (p_index < 0) { ++ pr_debug("%s: clk %s can not be parent of clk %s\n", ++ __func__, parent->name, clk->name); ++ return NULL; ++ } ++ } ++ ++ if ((clk->flags & CLK_SET_RATE_PARENT) && parent && ++ best_parent_rate != parent->rate) ++ top = clk_calc_new_rates(parent, best_parent_rate); ++ ++out: ++ clk_calc_subtree(clk, new_rate, parent, p_index); ++ ++ return top; ++} ++ ++/* ++ * Notify about rate changes in a subtree. Always walk down the whole tree ++ * so that in case of an error we can walk down the whole tree again and ++ * abort the change. ++ */ ++static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) ++{ ++ struct clk *child, *tmp_clk, *fail_clk = NULL; ++ int ret = NOTIFY_DONE; ++ ++ if (clk->rate == clk->new_rate) ++ return NULL; ++ ++ if (clk->notifier_count) { ++ ret = __clk_notify(clk, event, clk->rate, clk->new_rate); ++ if (ret & NOTIFY_STOP_MASK) ++ fail_clk = clk; ++ } ++ ++ hlist_for_each_entry(child, &clk->children, child_node) { ++ /* Skip children who will be reparented to another clock */ ++ if (child->new_parent && child->new_parent != clk) ++ continue; ++ tmp_clk = clk_propagate_rate_change(child, event); ++ if (tmp_clk) ++ fail_clk = tmp_clk; ++ } ++ ++ /* handle the new child who might not be in clk->children yet */ ++ if (clk->new_child) { ++ tmp_clk = clk_propagate_rate_change(clk->new_child, event); ++ if (tmp_clk) ++ fail_clk = tmp_clk; ++ } ++ ++ return fail_clk; ++} ++ ++/* ++ * walk down a subtree and set the new rates notifying the rate ++ * change on the way ++ */ ++static void clk_change_rate(struct clk *clk) ++{ ++ struct clk *child; ++ struct hlist_node *tmp; ++ unsigned long old_rate; ++ unsigned long best_parent_rate = 0; ++ bool skip_set_rate = false; ++ struct clk *old_parent; ++ ++ old_rate = clk->rate; ++ ++ if (clk->new_parent) ++ best_parent_rate = clk->new_parent->rate; ++ else if (clk->parent) ++ best_parent_rate = clk->parent->rate; ++ ++ if (clk->new_parent && clk->new_parent != clk->parent) { ++ old_parent = __clk_set_parent_before(clk, clk->new_parent); ++ ++ if (clk->ops->set_rate_and_parent) { ++ skip_set_rate = true; ++ clk->ops->set_rate_and_parent(clk->hw, clk->new_rate, ++ best_parent_rate, ++ clk->new_parent_index); ++ } else if (clk->ops->set_parent) { ++ clk->ops->set_parent(clk->hw, clk->new_parent_index); ++ } ++ ++ __clk_set_parent_after(clk, clk->new_parent, old_parent); ++ } ++ ++ if (!skip_set_rate && clk->ops->set_rate) ++ clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); ++ ++ if (clk->ops->recalc_rate) ++ clk->rate = clk->ops->recalc_rate(clk->hw, best_parent_rate); ++ else ++ clk->rate = best_parent_rate; ++ ++ if (clk->notifier_count && old_rate != clk->rate) ++ __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); ++ ++ /* ++ * Use safe iteration, as change_rate can actually swap parents ++ * for certain clock types. ++ */ ++ hlist_for_each_entry_safe(child, tmp, &clk->children, child_node) { ++ /* Skip children who will be reparented to another clock */ ++ if (child->new_parent && child->new_parent != clk) ++ continue; ++ clk_change_rate(child); ++ } ++ ++ /* handle the new child who might not be in clk->children yet */ ++ if (clk->new_child) ++ clk_change_rate(clk->new_child); ++} ++ ++/** ++ * clk_set_rate - specify a new rate for clk ++ * @clk: the clk whose rate is being changed ++ * @rate: the new rate for clk ++ * ++ * In the simplest case clk_set_rate will only adjust the rate of clk. ++ * ++ * Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to ++ * propagate up to clk's parent; whether or not this happens depends on the ++ * outcome of clk's .round_rate implementation. If *parent_rate is unchanged ++ * after calling .round_rate then upstream parent propagation is ignored. If ++ * *parent_rate comes back with a new rate for clk's parent then we propagate ++ * up to clk's parent and set its rate. Upward propagation will continue ++ * until either a clk does not support the CLK_SET_RATE_PARENT flag or ++ * .round_rate stops requesting changes to clk's parent_rate. ++ * ++ * Rate changes are accomplished via tree traversal that also recalculates the ++ * rates for the clocks and fires off POST_RATE_CHANGE notifiers. ++ * ++ * Returns 0 on success, -EERROR otherwise. ++ */ ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ struct clk *top, *fail_clk; ++ int ret = 0; ++ ++ if (!clk) ++ return 0; ++ ++ /* prevent racing with updates to the clock topology */ ++ clk_prepare_lock(); ++ ++ /* bail early if nothing to do */ ++ if (rate == clk_get_rate(clk)) ++ goto out; ++ ++ if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) { ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ /* calculate new rates and get the topmost changed clock */ ++ top = clk_calc_new_rates(clk, rate); ++ if (!top) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* notify that we are about to change rates */ ++ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); ++ if (fail_clk) { ++ pr_warn("%s: failed to set %s rate\n", __func__, ++ fail_clk->name); ++ clk_propagate_rate_change(top, ABORT_RATE_CHANGE); ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ /* change the rates */ ++ clk_change_rate(top); ++ ++out: ++ clk_prepare_unlock(); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(clk_set_rate); ++ ++/** ++ * clk_get_parent - return the parent of a clk ++ * @clk: the clk whose parent gets returned ++ * ++ * Simply returns clk->parent. Returns NULL if clk is NULL. ++ */ ++struct clk *clk_get_parent(struct clk *clk) ++{ ++ struct clk *parent; ++ ++ clk_prepare_lock(); ++ parent = __clk_get_parent(clk); ++ clk_prepare_unlock(); ++ ++ return parent; ++} ++EXPORT_SYMBOL_GPL(clk_get_parent); ++ ++/* ++ * .get_parent is mandatory for clocks with multiple possible parents. It is ++ * optional for single-parent clocks. Always call .get_parent if it is ++ * available and WARN if it is missing for multi-parent clocks. ++ * ++ * For single-parent clocks without .get_parent, first check to see if the ++ * .parents array exists, and if so use it to avoid an expensive tree ++ * traversal. If .parents does not exist then walk the tree with __clk_lookup. ++ */ ++static struct clk *__clk_init_parent(struct clk *clk) ++{ ++ struct clk *ret = NULL; ++ u8 index; ++ ++ /* handle the trivial cases */ ++ ++ if (!clk->num_parents) ++ goto out; ++ ++ if (clk->num_parents == 1) { ++ if (IS_ERR_OR_NULL(clk->parent)) ++ ret = clk->parent = __clk_lookup(clk->parent_names[0]); ++ ret = clk->parent; ++ goto out; ++ } ++ ++ if (!clk->ops->get_parent) { ++ WARN(!clk->ops->get_parent, ++ "%s: multi-parent clocks must implement .get_parent\n", ++ __func__); ++ goto out; ++ }; ++ ++ /* ++ * Do our best to cache parent clocks in clk->parents. This prevents ++ * unnecessary and expensive calls to __clk_lookup. We don't set ++ * clk->parent here; that is done by the calling function ++ */ ++ ++ index = clk->ops->get_parent(clk->hw); ++ ++ if (!clk->parents) ++ clk->parents = ++ kcalloc(clk->num_parents, sizeof(struct clk *), ++ GFP_KERNEL); ++ ++ ret = clk_get_parent_by_index(clk, index); ++ ++out: ++ return ret; ++} ++ ++void __clk_reparent(struct clk *clk, struct clk *new_parent) ++{ ++ clk_reparent(clk, new_parent); ++ clk_debug_reparent(clk, new_parent); ++ __clk_recalc_accuracies(clk); ++ __clk_recalc_rates(clk, POST_RATE_CHANGE); ++} ++ ++/** ++ * clk_set_parent - switch the parent of a mux clk ++ * @clk: the mux clk whose input we are switching ++ * @parent: the new input to clk ++ * ++ * Re-parent clk to use parent as its new input source. If clk is in ++ * prepared state, the clk will get enabled for the duration of this call. If ++ * that's not acceptable for a specific clk (Eg: the consumer can't handle ++ * that, the reparenting is glitchy in hardware, etc), use the ++ * CLK_SET_PARENT_GATE flag to allow reparenting only when clk is unprepared. ++ * ++ * After successfully changing clk's parent clk_set_parent will update the ++ * clk topology, sysfs topology and propagate rate recalculation via ++ * __clk_recalc_rates. ++ * ++ * Returns 0 on success, -EERROR otherwise. ++ */ ++int clk_set_parent(struct clk *clk, struct clk *parent) ++{ ++ struct clk *child; ++ int ret = 0; ++ int p_index = 0; ++ unsigned long p_rate = 0; ++ ++ if (!clk) ++ return 0; ++ ++ if (!clk->ops) ++ return -EINVAL; ++ ++ /* verify ops for for multi-parent clks */ ++ if ((clk->num_parents > 1) && (!clk->ops->set_parent)) ++ return -ENOSYS; ++ ++ /* prevent racing with updates to the clock topology */ ++ clk_prepare_lock(); ++ ++ if (clk->parent == parent) ++ goto out; ++ ++ /* check that we are allowed to re-parent if the clock is in use */ ++ if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ /* check two consecutive basic mux clocks */ ++ if (clk->flags & CLK_IS_BASIC_MUX) { ++ hlist_for_each_entry(child, &clk->children, child_node) { ++ if (child->flags & CLK_IS_BASIC_MUX) { ++ pr_err("%s: failed to switch parent of %s due to child mux %s\n", ++ __func__, clk->name, child->name); ++ ret = -EBUSY; ++ goto out; ++ } ++ } ++ } ++ ++ /* try finding the new parent index */ ++ if (parent) { ++ p_index = clk_fetch_parent_index(clk, parent); ++ p_rate = parent->rate; ++ if (p_index < 0) { ++ pr_debug("%s: clk %s can not be parent of clk %s\n", ++ __func__, parent->name, clk->name); ++ ret = p_index; ++ goto out; ++ } ++ } ++ ++ /* propagate PRE_RATE_CHANGE notifications */ ++ ret = __clk_speculate_rates(clk, p_rate); ++ ++ /* abort if a driver objects */ ++ if (ret & NOTIFY_STOP_MASK) ++ goto out; ++ ++ /* do the re-parent */ ++ ret = __clk_set_parent(clk, parent, p_index); ++ ++ /* propagate rate an accuracy recalculation accordingly */ ++ if (ret) { ++ __clk_recalc_rates(clk, ABORT_RATE_CHANGE); ++ } else { ++ __clk_recalc_rates(clk, POST_RATE_CHANGE); ++ __clk_recalc_accuracies(clk); ++ } ++ ++out: ++ clk_prepare_unlock(); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(clk_set_parent); ++ ++/** ++ * __clk_init - initialize the data structures in a struct clk ++ * @dev: device initializing this clk, placeholder for now ++ * @clk: clk being initialized ++ * ++ * Initializes the lists in struct clk, queries the hardware for the ++ * parent and rate and sets them both. ++ */ ++int __clk_init(struct device *dev, struct clk *clk) ++{ ++ int i, ret = 0; ++ struct clk *orphan; ++ struct hlist_node *tmp2; ++ ++ if (!clk) ++ return -EINVAL; ++ ++ clk_prepare_lock(); ++ ++ /* check to see if a clock with this name is already registered */ ++ if (__clk_lookup(clk->name)) { ++ pr_debug("%s: clk %s already initialized\n", ++ __func__, clk->name); ++ ret = -EEXIST; ++ goto out; ++ } ++ ++ /* check that clk_ops are sane. See Documentation/clk.txt */ ++ if (clk->ops->set_rate && ++ !((clk->ops->round_rate || clk->ops->determine_rate) && ++ clk->ops->recalc_rate)) { ++ pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", ++ __func__, clk->name); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (clk->ops->set_parent && !clk->ops->get_parent) { ++ pr_warning("%s: %s must implement .get_parent & .set_parent\n", ++ __func__, clk->name); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (clk->ops->set_rate_and_parent && ++ !(clk->ops->set_parent && clk->ops->set_rate)) { ++ pr_warn("%s: %s must implement .set_parent & .set_rate\n", ++ __func__, clk->name); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* throw a WARN if any entries in parent_names are NULL */ ++ for (i = 0; i < clk->num_parents; i++) ++ WARN(!clk->parent_names[i], ++ "%s: invalid NULL in %s's .parent_names\n", ++ __func__, clk->name); ++ ++ /* ++ * Allocate an array of struct clk *'s to avoid unnecessary string ++ * look-ups of clk's possible parents. This can fail for clocks passed ++ * in to clk_init during early boot; thus any access to clk->parents[] ++ * must always check for a NULL pointer and try to populate it if ++ * necessary. ++ * ++ * If clk->parents is not NULL we skip this entire block. This allows ++ * for clock drivers to statically initialize clk->parents. ++ */ ++ if (clk->num_parents > 1 && !clk->parents) { ++ clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *), ++ GFP_KERNEL); ++ /* ++ * __clk_lookup returns NULL for parents that have not been ++ * clk_init'd; thus any access to clk->parents[] must check ++ * for a NULL pointer. We can always perform lazy lookups for ++ * missing parents later on. ++ */ ++ if (clk->parents) ++ for (i = 0; i < clk->num_parents; i++) ++ clk->parents[i] = ++ __clk_lookup(clk->parent_names[i]); ++ } ++ ++ clk->parent = __clk_init_parent(clk); ++ ++ /* ++ * Populate clk->parent if parent has already been __clk_init'd. If ++ * parent has not yet been __clk_init'd then place clk in the orphan ++ * list. If clk has set the CLK_IS_ROOT flag then place it in the root ++ * clk list. ++ * ++ * Every time a new clk is clk_init'd then we walk the list of orphan ++ * clocks and re-parent any that are children of the clock currently ++ * being clk_init'd. ++ */ ++ if (clk->parent) ++ hlist_add_head(&clk->child_node, ++ &clk->parent->children); ++ else if (clk->flags & CLK_IS_ROOT) ++ hlist_add_head(&clk->child_node, &clk_root_list); ++ else ++ hlist_add_head(&clk->child_node, &clk_orphan_list); ++ ++ /* ++ * Set clk's accuracy. The preferred method is to use ++ * .recalc_accuracy. For simple clocks and lazy developers the default ++ * fallback is to use the parent's accuracy. If a clock doesn't have a ++ * parent (or is orphaned) then accuracy is set to zero (perfect ++ * clock). ++ */ ++ if (clk->ops->recalc_accuracy) ++ clk->accuracy = clk->ops->recalc_accuracy(clk->hw, ++ __clk_get_accuracy(clk->parent)); ++ else if (clk->parent) ++ clk->accuracy = clk->parent->accuracy; ++ else ++ clk->accuracy = 0; ++ ++ /* ++ * Set clk's rate. The preferred method is to use .recalc_rate. For ++ * simple clocks and lazy developers the default fallback is to use the ++ * parent's rate. If a clock doesn't have a parent (or is orphaned) ++ * then rate is set to zero. ++ */ ++ if (clk->ops->recalc_rate) ++ clk->rate = clk->ops->recalc_rate(clk->hw, ++ __clk_get_rate(clk->parent)); ++ else if (clk->parent) ++ clk->rate = clk->parent->rate; ++ else ++ clk->rate = 0; ++ ++ clk_debug_register(clk); ++ /* ++ * walk the list of orphan clocks and reparent any that are children of ++ * this clock ++ */ ++ hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { ++ if (orphan->num_parents && orphan->ops->get_parent) { ++ i = orphan->ops->get_parent(orphan->hw); ++ if (!strcmp(clk->name, orphan->parent_names[i])) ++ __clk_reparent(orphan, clk); ++ continue; ++ } ++ ++ for (i = 0; i < orphan->num_parents; i++) ++ if (!strcmp(clk->name, orphan->parent_names[i])) { ++ __clk_reparent(orphan, clk); ++ break; ++ } ++ } ++ ++ /* ++ * optional platform-specific magic ++ * ++ * The .init callback is not used by any of the basic clock types, but ++ * exists for weird hardware that must perform initialization magic. ++ * Please consider other ways of solving initialization problems before ++ * using this callback, as its use is discouraged. ++ */ ++ if (clk->ops->init) ++ clk->ops->init(clk->hw); ++ ++ kref_init(&clk->ref); ++out: ++ clk_prepare_unlock(); ++ ++ return ret; ++} ++ ++/** ++ * __clk_register - register a clock and return a cookie. ++ * ++ * Same as clk_register, except that the .clk field inside hw shall point to a ++ * preallocated (generally statically allocated) struct clk. None of the fields ++ * of the struct clk need to be initialized. ++ * ++ * The data pointed to by .init and .clk field shall NOT be marked as init ++ * data. ++ * ++ * __clk_register is only exposed via clk-private.h and is intended for use with ++ * very large numbers of clocks that need to be statically initialized. It is ++ * a layering violation to include clk-private.h from any code which implements ++ * a clock's .ops; as such any statically initialized clock data MUST be in a ++ * separate C file from the logic that implements its operations. Returns 0 ++ * on success, otherwise an error code. ++ */ ++struct clk *__clk_register(struct device *dev, struct clk_hw *hw) ++{ ++ int ret; ++ struct clk *clk; ++ ++ clk = hw->clk; ++ clk->name = hw->init->name; ++ clk->ops = hw->init->ops; ++ clk->hw = hw; ++ clk->flags = hw->init->flags; ++ clk->parent_names = hw->init->parent_names; ++ clk->num_parents = hw->init->num_parents; ++ if (dev && dev->driver) ++ clk->owner = dev->driver->owner; ++ else ++ clk->owner = NULL; ++ ++ ret = __clk_init(dev, clk); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ return clk; ++} ++EXPORT_SYMBOL_GPL(__clk_register); ++ ++/** ++ * clk_register - allocate a new clock, register it and return an opaque cookie ++ * @dev: device that is registering this clock ++ * @hw: link to hardware-specific clock data ++ * ++ * clk_register is the primary interface for populating the clock tree with new ++ * clock nodes. It returns a pointer to the newly allocated struct clk which ++ * cannot be dereferenced by driver code but may be used in conjuction with the ++ * rest of the clock API. In the event of an error clk_register will return an ++ * error code; drivers must test for an error code after calling clk_register. ++ */ ++struct clk *clk_register(struct device *dev, struct clk_hw *hw) ++{ ++ int i, ret; ++ struct clk *clk; ++ ++ clk = kzalloc(sizeof(*clk), GFP_KERNEL); ++ if (!clk) { ++ pr_err("%s: could not allocate clk\n", __func__); ++ ret = -ENOMEM; ++ goto fail_out; ++ } ++ ++ clk->name = kstrdup(hw->init->name, GFP_KERNEL); ++ if (!clk->name) { ++ pr_err("%s: could not allocate clk->name\n", __func__); ++ ret = -ENOMEM; ++ goto fail_name; ++ } ++ clk->ops = hw->init->ops; ++ if (dev && dev->driver) ++ clk->owner = dev->driver->owner; ++ clk->hw = hw; ++ clk->flags = hw->init->flags; ++ clk->num_parents = hw->init->num_parents; ++ hw->clk = clk; ++ ++ /* allocate local copy in case parent_names is __initdata */ ++ clk->parent_names = kcalloc(clk->num_parents, sizeof(char *), ++ GFP_KERNEL); ++ ++ if (!clk->parent_names) { ++ pr_err("%s: could not allocate clk->parent_names\n", __func__); ++ ret = -ENOMEM; ++ goto fail_parent_names; ++ } ++ ++ ++ /* copy each string name in case parent_names is __initdata */ ++ for (i = 0; i < clk->num_parents; i++) { ++ clk->parent_names[i] = kstrdup(hw->init->parent_names[i], ++ GFP_KERNEL); ++ if (!clk->parent_names[i]) { ++ pr_err("%s: could not copy parent_names\n", __func__); ++ ret = -ENOMEM; ++ goto fail_parent_names_copy; ++ } ++ } ++ ++ ret = __clk_init(dev, clk); ++ if (!ret) ++ return clk; ++ ++fail_parent_names_copy: ++ while (--i >= 0) ++ kfree(clk->parent_names[i]); ++ kfree(clk->parent_names); ++fail_parent_names: ++ kfree(clk->name); ++fail_name: ++ kfree(clk); ++fail_out: ++ return ERR_PTR(ret); ++} ++EXPORT_SYMBOL_GPL(clk_register); ++ ++/* ++ * Free memory allocated for a clock. ++ * Caller must hold prepare_lock. ++ */ ++static void __clk_release(struct kref *ref) ++{ ++ struct clk *clk = container_of(ref, struct clk, ref); ++ int i = clk->num_parents; ++ ++ kfree(clk->parents); ++ while (--i >= 0) ++ kfree(clk->parent_names[i]); ++ ++ kfree(clk->parent_names); ++ kfree(clk->name); ++ kfree(clk); ++} ++ ++/* ++ * Empty clk_ops for unregistered clocks. These are used temporarily ++ * after clk_unregister() was called on a clock and until last clock ++ * consumer calls clk_put() and the struct clk object is freed. ++ */ ++static int clk_nodrv_prepare_enable(struct clk_hw *hw) ++{ ++ return -ENXIO; ++} ++ ++static void clk_nodrv_disable_unprepare(struct clk_hw *hw) ++{ ++ WARN_ON_ONCE(1); ++} ++ ++static int clk_nodrv_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ return -ENXIO; ++} ++ ++static int clk_nodrv_set_parent(struct clk_hw *hw, u8 index) ++{ ++ return -ENXIO; ++} ++ ++static const struct clk_ops clk_nodrv_ops = { ++ .enable = clk_nodrv_prepare_enable, ++ .disable = clk_nodrv_disable_unprepare, ++ .prepare = clk_nodrv_prepare_enable, ++ .unprepare = clk_nodrv_disable_unprepare, ++ .set_rate = clk_nodrv_set_rate, ++ .set_parent = clk_nodrv_set_parent, ++}; ++ ++/** ++ * clk_unregister - unregister a currently registered clock ++ * @clk: clock to unregister ++ */ ++void clk_unregister(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ if (!clk || WARN_ON_ONCE(IS_ERR(clk))) ++ return; ++ ++ clk_prepare_lock(); ++ ++ if (clk->ops == &clk_nodrv_ops) { ++ pr_err("%s: unregistered clock: %s\n", __func__, clk->name); ++ goto out; ++ } ++ /* ++ * Assign empty clock ops for consumers that might still hold ++ * a reference to this clock. ++ */ ++ flags = clk_enable_lock(); ++ clk->ops = &clk_nodrv_ops; ++ clk_enable_unlock(flags); ++ ++ if (!hlist_empty(&clk->children)) { ++ struct clk *child; ++ struct hlist_node *t; ++ ++ /* Reparent all children to the orphan list. */ ++ hlist_for_each_entry_safe(child, t, &clk->children, child_node) ++ clk_set_parent(child, NULL); ++ } ++ ++ clk_debug_unregister(clk); ++ ++ hlist_del_init(&clk->child_node); ++ ++ if (clk->prepare_count) ++ pr_warn("%s: unregistering prepared clock: %s\n", ++ __func__, clk->name); ++ ++ kref_put(&clk->ref, __clk_release); ++out: ++ clk_prepare_unlock(); ++} ++EXPORT_SYMBOL_GPL(clk_unregister); ++ ++static void devm_clk_release(struct device *dev, void *res) ++{ ++ clk_unregister(*(struct clk **)res); ++} ++ ++/** ++ * devm_clk_register - resource managed clk_register() ++ * @dev: device that is registering this clock ++ * @hw: link to hardware-specific clock data ++ * ++ * Managed clk_register(). Clocks returned from this function are ++ * automatically clk_unregister()ed on driver detach. See clk_register() for ++ * more information. ++ */ ++struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw) ++{ ++ struct clk *clk; ++ struct clk **clkp; ++ ++ clkp = devres_alloc(devm_clk_release, sizeof(*clkp), GFP_KERNEL); ++ if (!clkp) ++ return ERR_PTR(-ENOMEM); ++ ++ clk = clk_register(dev, hw); ++ if (!IS_ERR(clk)) { ++ *clkp = clk; ++ devres_add(dev, clkp); ++ } else { ++ devres_free(clkp); ++ } ++ ++ return clk; ++} ++EXPORT_SYMBOL_GPL(devm_clk_register); ++ ++static int devm_clk_match(struct device *dev, void *res, void *data) ++{ ++ struct clk *c = res; ++ if (WARN_ON(!c)) ++ return 0; ++ return c == data; ++} ++ ++/** ++ * devm_clk_unregister - resource managed clk_unregister() ++ * @clk: clock to unregister ++ * ++ * Deallocate a clock allocated with devm_clk_register(). Normally ++ * this function will not need to be called and the resource management ++ * code will ensure that the resource is freed. ++ */ ++void devm_clk_unregister(struct device *dev, struct clk *clk) ++{ ++ WARN_ON(devres_release(dev, devm_clk_release, devm_clk_match, clk)); ++} ++EXPORT_SYMBOL_GPL(devm_clk_unregister); ++ ++/* ++ * clkdev helpers ++ */ ++int __clk_get(struct clk *clk) ++{ ++ if (clk) { ++ if (!try_module_get(clk->owner)) ++ return 0; ++ ++ kref_get(&clk->ref); ++ } ++ return 1; ++} ++ ++void __clk_put(struct clk *clk) ++{ ++ if (!clk || WARN_ON_ONCE(IS_ERR(clk))) ++ return; ++ ++ clk_prepare_lock(); ++ kref_put(&clk->ref, __clk_release); ++ clk_prepare_unlock(); ++ ++ module_put(clk->owner); ++} ++ ++/*** clk rate change notifiers ***/ ++ ++/** ++ * clk_notifier_register - add a clk rate change notifier ++ * @clk: struct clk * to watch ++ * @nb: struct notifier_block * with callback info ++ * ++ * Request notification when clk's rate changes. This uses an SRCU ++ * notifier because we want it to block and notifier unregistrations are ++ * uncommon. The callbacks associated with the notifier must not ++ * re-enter into the clk framework by calling any top-level clk APIs; ++ * this will cause a nested prepare_lock mutex. ++ * ++ * Pre-change notifier callbacks will be passed the current, pre-change ++ * rate of the clk via struct clk_notifier_data.old_rate. The new, ++ * post-change rate of the clk is passed via struct ++ * clk_notifier_data.new_rate. ++ * ++ * Post-change notifiers will pass the now-current, post-change rate of ++ * the clk in both struct clk_notifier_data.old_rate and struct ++ * clk_notifier_data.new_rate. ++ * ++ * Abort-change notifiers are effectively the opposite of pre-change ++ * notifiers: the original pre-change clk rate is passed in via struct ++ * clk_notifier_data.new_rate and the failed post-change rate is passed ++ * in via struct clk_notifier_data.old_rate. ++ * ++ * clk_notifier_register() must be called from non-atomic context. ++ * Returns -EINVAL if called with null arguments, -ENOMEM upon ++ * allocation failure; otherwise, passes along the return value of ++ * srcu_notifier_chain_register(). ++ */ ++int clk_notifier_register(struct clk *clk, struct notifier_block *nb) ++{ ++ struct clk_notifier *cn; ++ int ret = -ENOMEM; ++ ++ if (!clk || !nb) ++ return -EINVAL; ++ ++ clk_prepare_lock(); ++ ++ /* search the list of notifiers for this clk */ ++ list_for_each_entry(cn, &clk_notifier_list, node) ++ if (cn->clk == clk) ++ break; ++ ++ /* if clk wasn't in the notifier list, allocate new clk_notifier */ ++ if (cn->clk != clk) { ++ cn = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL); ++ if (!cn) ++ goto out; ++ ++ cn->clk = clk; ++ srcu_init_notifier_head(&cn->notifier_head); ++ ++ list_add(&cn->node, &clk_notifier_list); ++ } ++ ++ ret = srcu_notifier_chain_register(&cn->notifier_head, nb); ++ ++ clk->notifier_count++; ++ ++out: ++ clk_prepare_unlock(); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(clk_notifier_register); ++ ++/** ++ * clk_notifier_unregister - remove a clk rate change notifier ++ * @clk: struct clk * ++ * @nb: struct notifier_block * with callback info ++ * ++ * Request no further notification for changes to 'clk' and frees memory ++ * allocated in clk_notifier_register. ++ * ++ * Returns -EINVAL if called with null arguments; otherwise, passes ++ * along the return value of srcu_notifier_chain_unregister(). ++ */ ++int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) ++{ ++ struct clk_notifier *cn = NULL; ++ int ret = -EINVAL; ++ ++ if (!clk || !nb) ++ return -EINVAL; ++ ++ clk_prepare_lock(); ++ ++ list_for_each_entry(cn, &clk_notifier_list, node) ++ if (cn->clk == clk) ++ break; ++ ++ if (cn->clk == clk) { ++ ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb); ++ ++ clk->notifier_count--; ++ ++ /* XXX the notifier code should handle this better */ ++ if (!cn->notifier_head.head) { ++ srcu_cleanup_notifier_head(&cn->notifier_head); ++ list_del(&cn->node); ++ kfree(cn); ++ } ++ ++ } else { ++ ret = -ENOENT; ++ } ++ ++ clk_prepare_unlock(); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(clk_notifier_unregister); ++ ++#ifdef CONFIG_OF ++/** ++ * struct of_clk_provider - Clock provider registration structure ++ * @link: Entry in global list of clock providers ++ * @node: Pointer to device tree node of clock provider ++ * @get: Get clock callback. Returns NULL or a struct clk for the ++ * given clock specifier ++ * @data: context pointer to be passed into @get callback ++ */ ++struct of_clk_provider { ++ struct list_head link; ++ ++ struct device_node *node; ++ struct clk *(*get)(struct of_phandle_args *clkspec, void *data); ++ void *data; ++}; ++ ++static const struct of_device_id __clk_of_table_sentinel ++ __used __section(__clk_of_table_end); ++ ++static LIST_HEAD(of_clk_providers); ++static DEFINE_MUTEX(of_clk_mutex); ++ ++/* of_clk_provider list locking helpers */ ++void of_clk_lock(void) ++{ ++ mutex_lock(&of_clk_mutex); ++} ++ ++void of_clk_unlock(void) ++{ ++ mutex_unlock(&of_clk_mutex); ++} ++ ++struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, ++ void *data) ++{ ++ return data; ++} ++EXPORT_SYMBOL_GPL(of_clk_src_simple_get); ++ ++struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) ++{ ++ struct clk_onecell_data *clk_data = data; ++ unsigned int idx = clkspec->args[0]; ++ ++ if (idx >= clk_data->clk_num) { ++ pr_err("%s: invalid clock index %d\n", __func__, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return clk_data->clks[idx]; ++} ++EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); ++ ++/** ++ * of_clk_add_provider() - Register a clock provider for a node ++ * @np: Device node pointer associated with clock provider ++ * @clk_src_get: callback for decoding clock ++ * @data: context pointer for @clk_src_get callback. ++ */ ++int of_clk_add_provider(struct device_node *np, ++ struct clk *(*clk_src_get)(struct of_phandle_args *clkspec, ++ void *data), ++ void *data) ++{ ++ struct of_clk_provider *cp; ++ ++ cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL); ++ if (!cp) ++ return -ENOMEM; ++ ++ cp->node = of_node_get(np); ++ cp->data = data; ++ cp->get = clk_src_get; ++ ++ mutex_lock(&of_clk_mutex); ++ list_add(&cp->link, &of_clk_providers); ++ mutex_unlock(&of_clk_mutex); ++ pr_debug("Added clock from %s\n", np->full_name); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(of_clk_add_provider); ++ ++/** ++ * of_clk_del_provider() - Remove a previously registered clock provider ++ * @np: Device node pointer associated with clock provider ++ */ ++void of_clk_del_provider(struct device_node *np) ++{ ++ struct of_clk_provider *cp; ++ ++ mutex_lock(&of_clk_mutex); ++ list_for_each_entry(cp, &of_clk_providers, link) { ++ if (cp->node == np) { ++ list_del(&cp->link); ++ of_node_put(cp->node); ++ kfree(cp); ++ break; ++ } ++ } ++ mutex_unlock(&of_clk_mutex); ++} ++EXPORT_SYMBOL_GPL(of_clk_del_provider); ++ ++struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec) ++{ ++ struct of_clk_provider *provider; ++ struct clk *clk = ERR_PTR(-ENOENT); ++ ++ /* Check if we have such a provider in our array */ ++ list_for_each_entry(provider, &of_clk_providers, link) { ++ if (provider->node == clkspec->np) ++ clk = provider->get(clkspec, provider->data); ++ if (!IS_ERR(clk)) ++ break; ++ } ++ ++ return clk; ++} ++ ++struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) ++{ ++ struct clk *clk; ++ ++ mutex_lock(&of_clk_mutex); ++ clk = __of_clk_get_from_provider(clkspec); ++ mutex_unlock(&of_clk_mutex); ++ ++ return clk; ++} ++ ++int of_clk_get_parent_count(struct device_node *np) ++{ ++ return of_count_phandle_with_args(np, "clocks", "#clock-cells"); ++} ++EXPORT_SYMBOL_GPL(of_clk_get_parent_count); ++ ++const char *of_clk_get_parent_name(struct device_node *np, int index) ++{ ++ struct of_phandle_args clkspec; ++ const char *clk_name; ++ int rc; ++ ++ if (index < 0) ++ return NULL; ++ ++ rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, ++ &clkspec); ++ if (rc) ++ return NULL; ++ ++ if (of_property_read_string_index(clkspec.np, "clock-output-names", ++ clkspec.args_count ? clkspec.args[0] : 0, ++ &clk_name) < 0) ++ clk_name = clkspec.np->name; ++ ++ of_node_put(clkspec.np); ++ return clk_name; ++} ++EXPORT_SYMBOL_GPL(of_clk_get_parent_name); ++ ++/** ++ * of_clk_init() - Scan and init clock providers from the DT ++ * @matches: array of compatible values and init functions for providers. ++ * ++ * This function scans the device tree for matching clock providers and ++ * calls their initialization functions ++ */ ++void __init of_clk_init(const struct of_device_id *matches) ++{ ++ const struct of_device_id *match; ++ struct device_node *np; ++ ++ if (!matches) ++ matches = &__clk_of_table; ++ ++ for_each_matching_node_and_match(np, matches, &match) { ++ of_clk_init_cb_t clk_init_cb = match->data; ++ clk_init_cb(np); ++ } ++} ++#endif +diff -Nur linux-3.14.36/drivers/clk/clk-mux.c linux-openelec/drivers/clk/clk-mux.c +--- linux-3.14.36/drivers/clk/clk-mux.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/clk/clk-mux.c 2015-05-06 12:05:42.000000000 -0500 +@@ -143,7 +143,7 @@ + init.ops = &clk_mux_ro_ops; + else + init.ops = &clk_mux_ops; +- init.flags = flags | CLK_IS_BASIC; ++ init.flags = flags | CLK_IS_BASIC | CLK_IS_BASIC_MUX; + init.parent_names = parent_names; + init.num_parents = num_parents; + +diff -Nur linux-3.14.36/drivers/cpufreq/cpufreq_interactive.c linux-openelec/drivers/cpufreq/cpufreq_interactive.c +--- linux-3.14.36/drivers/cpufreq/cpufreq_interactive.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/cpufreq/cpufreq_interactive.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1349 @@ ++/* ++ * drivers/cpufreq/cpufreq_interactive.c ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Author: Mike Chan (mike@android.com) ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++struct cpufreq_interactive_cpuinfo { ++ struct timer_list cpu_timer; ++ struct timer_list cpu_slack_timer; ++ spinlock_t load_lock; /* protects the next 4 fields */ ++ u64 time_in_idle; ++ u64 time_in_idle_timestamp; ++ u64 cputime_speedadj; ++ u64 cputime_speedadj_timestamp; ++ struct cpufreq_policy *policy; ++ struct cpufreq_frequency_table *freq_table; ++ unsigned int target_freq; ++ unsigned int floor_freq; ++ u64 floor_validate_time; ++ u64 hispeed_validate_time; ++ struct rw_semaphore enable_sem; ++ int governor_enabled; ++}; ++ ++static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); ++ ++/* realtime thread handles frequency scaling */ ++static struct task_struct *speedchange_task; ++static cpumask_t speedchange_cpumask; ++static spinlock_t speedchange_cpumask_lock; ++static struct mutex gov_lock; ++ ++/* Target load. Lower values result in higher CPU speeds. */ ++#define DEFAULT_TARGET_LOAD 90 ++static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; ++ ++#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) ++#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE ++static unsigned int default_above_hispeed_delay[] = { ++ DEFAULT_ABOVE_HISPEED_DELAY }; ++ ++struct cpufreq_interactive_tunables { ++ int usage_count; ++ /* Hi speed to bump to from lo speed when load burst (default max) */ ++ unsigned int hispeed_freq; ++ /* Go to hi speed when CPU load at or above this value. */ ++#define DEFAULT_GO_HISPEED_LOAD 99 ++ unsigned long go_hispeed_load; ++ /* Target load. Lower values result in higher CPU speeds. */ ++ spinlock_t target_loads_lock; ++ unsigned int *target_loads; ++ int ntarget_loads; ++ /* ++ * The minimum amount of time to spend at a frequency before we can ramp ++ * down. ++ */ ++#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) ++ unsigned long min_sample_time; ++ /* ++ * The sample rate of the timer used to increase frequency ++ */ ++ unsigned long timer_rate; ++ /* ++ * Wait this long before raising speed above hispeed, by default a ++ * single timer interval. ++ */ ++ spinlock_t above_hispeed_delay_lock; ++ unsigned int *above_hispeed_delay; ++ int nabove_hispeed_delay; ++ /* Non-zero means indefinite speed boost active */ ++ int boost_val; ++ /* Duration of a boot pulse in usecs */ ++ int boostpulse_duration_val; ++ /* End time of boost pulse in ktime converted to usecs */ ++ u64 boostpulse_endtime; ++ /* ++ * Max additional time to wait in idle, beyond timer_rate, at speeds ++ * above minimum before wakeup to reduce speed, or -1 if unnecessary. ++ */ ++#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) ++ int timer_slack_val; ++ bool io_is_busy; ++}; ++ ++/* For cases where we have single governor instance for system */ ++struct cpufreq_interactive_tunables *common_tunables; ++ ++static struct attribute_group *get_sysfs_attr(void); ++ ++static void cpufreq_interactive_timer_resched( ++ struct cpufreq_interactive_cpuinfo *pcpu) ++{ ++ struct cpufreq_interactive_tunables *tunables = ++ pcpu->policy->governor_data; ++ unsigned long expires; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ pcpu->time_in_idle = ++ get_cpu_idle_time(smp_processor_id(), ++ &pcpu->time_in_idle_timestamp, ++ tunables->io_is_busy); ++ pcpu->cputime_speedadj = 0; ++ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; ++ expires = jiffies + usecs_to_jiffies(tunables->timer_rate); ++ mod_timer_pinned(&pcpu->cpu_timer, expires); ++ ++ if (tunables->timer_slack_val >= 0 && ++ pcpu->target_freq > pcpu->policy->min) { ++ expires += usecs_to_jiffies(tunables->timer_slack_val); ++ mod_timer_pinned(&pcpu->cpu_slack_timer, expires); ++ } ++ ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++} ++ ++/* The caller shall take enable_sem write semaphore to avoid any timer race. ++ * The cpu_timer and cpu_slack_timer must be deactivated when calling this ++ * function. ++ */ ++static void cpufreq_interactive_timer_start( ++ struct cpufreq_interactive_tunables *tunables, int cpu) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); ++ unsigned long expires = jiffies + ++ usecs_to_jiffies(tunables->timer_rate); ++ unsigned long flags; ++ ++ pcpu->cpu_timer.expires = expires; ++ add_timer_on(&pcpu->cpu_timer, cpu); ++ if (tunables->timer_slack_val >= 0 && ++ pcpu->target_freq > pcpu->policy->min) { ++ expires += usecs_to_jiffies(tunables->timer_slack_val); ++ pcpu->cpu_slack_timer.expires = expires; ++ add_timer_on(&pcpu->cpu_slack_timer, cpu); ++ } ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ pcpu->time_in_idle = ++ get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp, ++ tunables->io_is_busy); ++ pcpu->cputime_speedadj = 0; ++ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++} ++ ++static unsigned int freq_to_above_hispeed_delay( ++ struct cpufreq_interactive_tunables *tunables, ++ unsigned int freq) ++{ ++ int i; ++ unsigned int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); ++ ++ for (i = 0; i < tunables->nabove_hispeed_delay - 1 && ++ freq >= tunables->above_hispeed_delay[i+1]; i += 2) ++ ; ++ ++ ret = tunables->above_hispeed_delay[i]; ++ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); ++ return ret; ++} ++ ++static unsigned int freq_to_targetload( ++ struct cpufreq_interactive_tunables *tunables, unsigned int freq) ++{ ++ int i; ++ unsigned int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->target_loads_lock, flags); ++ ++ for (i = 0; i < tunables->ntarget_loads - 1 && ++ freq >= tunables->target_loads[i+1]; i += 2) ++ ; ++ ++ ret = tunables->target_loads[i]; ++ spin_unlock_irqrestore(&tunables->target_loads_lock, flags); ++ return ret; ++} ++ ++/* ++ * If increasing frequencies never map to a lower target load then ++ * choose_freq() will find the minimum frequency that does not exceed its ++ * target load given the current load. ++ */ ++static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu, ++ unsigned int loadadjfreq) ++{ ++ unsigned int freq = pcpu->policy->cur; ++ unsigned int prevfreq, freqmin, freqmax; ++ unsigned int tl; ++ int index; ++ ++ freqmin = 0; ++ freqmax = UINT_MAX; ++ ++ do { ++ prevfreq = freq; ++ tl = freq_to_targetload(pcpu->policy->governor_data, freq); ++ ++ /* ++ * Find the lowest frequency where the computed load is less ++ * than or equal to the target load. ++ */ ++ ++ if (cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, loadadjfreq / tl, ++ CPUFREQ_RELATION_L, &index)) ++ break; ++ freq = pcpu->freq_table[index].frequency; ++ ++ if (freq > prevfreq) { ++ /* The previous frequency is too low. */ ++ freqmin = prevfreq; ++ ++ if (freq >= freqmax) { ++ /* ++ * Find the highest frequency that is less ++ * than freqmax. ++ */ ++ if (cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, ++ freqmax - 1, CPUFREQ_RELATION_H, ++ &index)) ++ break; ++ freq = pcpu->freq_table[index].frequency; ++ ++ if (freq == freqmin) { ++ /* ++ * The first frequency below freqmax ++ * has already been found to be too ++ * low. freqmax is the lowest speed ++ * we found that is fast enough. ++ */ ++ freq = freqmax; ++ break; ++ } ++ } ++ } else if (freq < prevfreq) { ++ /* The previous frequency is high enough. */ ++ freqmax = prevfreq; ++ ++ if (freq <= freqmin) { ++ /* ++ * Find the lowest frequency that is higher ++ * than freqmin. ++ */ ++ if (cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, ++ freqmin + 1, CPUFREQ_RELATION_L, ++ &index)) ++ break; ++ freq = pcpu->freq_table[index].frequency; ++ ++ /* ++ * If freqmax is the first frequency above ++ * freqmin then we have already found that ++ * this speed is fast enough. ++ */ ++ if (freq == freqmax) ++ break; ++ } ++ } ++ ++ /* If same frequency chosen as previous then done. */ ++ } while (freq != prevfreq); ++ ++ return freq; ++} ++ ++static u64 update_load(int cpu) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); ++ struct cpufreq_interactive_tunables *tunables = ++ pcpu->policy->governor_data; ++ u64 now; ++ u64 now_idle; ++ unsigned int delta_idle; ++ unsigned int delta_time; ++ u64 active_time; ++ ++ now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); ++ delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); ++ delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); ++ ++ if (delta_time <= delta_idle) ++ active_time = 0; ++ else ++ active_time = delta_time - delta_idle; ++ ++ pcpu->cputime_speedadj += active_time * pcpu->policy->cur; ++ ++ pcpu->time_in_idle = now_idle; ++ pcpu->time_in_idle_timestamp = now; ++ return now; ++} ++ ++static void cpufreq_interactive_timer(unsigned long data) ++{ ++ u64 now; ++ unsigned int delta_time; ++ u64 cputime_speedadj; ++ int cpu_load; ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, data); ++ struct cpufreq_interactive_tunables *tunables = ++ pcpu->policy->governor_data; ++ unsigned int new_freq; ++ unsigned int loadadjfreq; ++ unsigned int index; ++ unsigned long flags; ++ bool boosted; ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) ++ goto exit; ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ now = update_load(data); ++ delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); ++ cputime_speedadj = pcpu->cputime_speedadj; ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++ ++ if (WARN_ON_ONCE(!delta_time)) ++ goto rearm; ++ ++ do_div(cputime_speedadj, delta_time); ++ loadadjfreq = (unsigned int)cputime_speedadj * 100; ++ cpu_load = loadadjfreq / pcpu->target_freq; ++ boosted = tunables->boost_val || now < tunables->boostpulse_endtime; ++ ++ if (cpu_load >= tunables->go_hispeed_load || boosted) { ++ if (pcpu->target_freq < tunables->hispeed_freq) { ++ new_freq = tunables->hispeed_freq; ++ } else { ++ new_freq = choose_freq(pcpu, loadadjfreq); ++ ++ if (new_freq < tunables->hispeed_freq) ++ new_freq = tunables->hispeed_freq; ++ } ++ } else { ++ new_freq = choose_freq(pcpu, loadadjfreq); ++ } ++ ++ if (pcpu->target_freq >= tunables->hispeed_freq && ++ new_freq > pcpu->target_freq && ++ now - pcpu->hispeed_validate_time < ++ freq_to_above_hispeed_delay(tunables, pcpu->target_freq)) { ++ trace_cpufreq_interactive_notyet( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ goto rearm; ++ } ++ ++ pcpu->hispeed_validate_time = now; ++ ++ if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, ++ new_freq, CPUFREQ_RELATION_L, ++ &index)) ++ goto rearm; ++ ++ new_freq = pcpu->freq_table[index].frequency; ++ ++ /* ++ * Do not scale below floor_freq unless we have been at or above the ++ * floor frequency for the minimum sample time since last validated. ++ */ ++ if (new_freq < pcpu->floor_freq) { ++ if (now - pcpu->floor_validate_time < ++ tunables->min_sample_time) { ++ trace_cpufreq_interactive_notyet( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ goto rearm; ++ } ++ } ++ ++ /* ++ * Update the timestamp for checking whether speed has been held at ++ * or above the selected frequency for a minimum of min_sample_time, ++ * if not boosted to hispeed_freq. If boosted to hispeed_freq then we ++ * allow the speed to drop as soon as the boostpulse duration expires ++ * (or the indefinite boost is turned off). ++ */ ++ ++ if (!boosted || new_freq > tunables->hispeed_freq) { ++ pcpu->floor_freq = new_freq; ++ pcpu->floor_validate_time = now; ++ } ++ ++ if (pcpu->target_freq == new_freq) { ++ trace_cpufreq_interactive_already( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ goto rearm_if_notmax; ++ } ++ ++ trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ ++ pcpu->target_freq = new_freq; ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ cpumask_set_cpu(data, &speedchange_cpumask); ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ wake_up_process(speedchange_task); ++ ++rearm_if_notmax: ++ /* ++ * Already set max speed and don't see a need to change that, ++ * wait until next idle to re-evaluate, don't need timer. ++ */ ++ if (pcpu->target_freq == pcpu->policy->max) ++ goto exit; ++ ++rearm: ++ if (!timer_pending(&pcpu->cpu_timer)) ++ cpufreq_interactive_timer_resched(pcpu); ++ ++exit: ++ up_read(&pcpu->enable_sem); ++ return; ++} ++ ++static void cpufreq_interactive_idle_start(void) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, smp_processor_id()); ++ int pending; ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return; ++ } ++ ++ pending = timer_pending(&pcpu->cpu_timer); ++ ++ if (pcpu->target_freq != pcpu->policy->min) { ++ /* ++ * Entering idle while not at lowest speed. On some ++ * platforms this can hold the other CPU(s) at that speed ++ * even though the CPU is idle. Set a timer to re-evaluate ++ * speed so this idle CPU doesn't hold the other CPUs above ++ * min indefinitely. This should probably be a quirk of ++ * the CPUFreq driver. ++ */ ++ if (!pending) ++ cpufreq_interactive_timer_resched(pcpu); ++ } ++ ++ up_read(&pcpu->enable_sem); ++} ++ ++static void cpufreq_interactive_idle_end(void) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, smp_processor_id()); ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return; ++ } ++ ++ /* Arm the timer for 1-2 ticks later if not already. */ ++ if (!timer_pending(&pcpu->cpu_timer)) { ++ cpufreq_interactive_timer_resched(pcpu); ++ } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { ++ del_timer(&pcpu->cpu_timer); ++ del_timer(&pcpu->cpu_slack_timer); ++ cpufreq_interactive_timer(smp_processor_id()); ++ } ++ ++ up_read(&pcpu->enable_sem); ++} ++ ++static int cpufreq_interactive_speedchange_task(void *data) ++{ ++ unsigned int cpu; ++ cpumask_t tmp_mask; ++ unsigned long flags; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ ++ if (cpumask_empty(&speedchange_cpumask)) { ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, ++ flags); ++ schedule(); ++ ++ if (kthread_should_stop()) ++ break; ++ ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ } ++ ++ set_current_state(TASK_RUNNING); ++ tmp_mask = speedchange_cpumask; ++ cpumask_clear(&speedchange_cpumask); ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ ++ for_each_cpu(cpu, &tmp_mask) { ++ unsigned int j; ++ unsigned int max_freq = 0; ++ ++ pcpu = &per_cpu(cpuinfo, cpu); ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ continue; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ continue; ++ } ++ ++ for_each_cpu(j, pcpu->policy->cpus) { ++ struct cpufreq_interactive_cpuinfo *pjcpu = ++ &per_cpu(cpuinfo, j); ++ ++ if (pjcpu->target_freq > max_freq) ++ max_freq = pjcpu->target_freq; ++ } ++ ++ if (max_freq != pcpu->policy->cur) ++ __cpufreq_driver_target(pcpu->policy, ++ max_freq, ++ CPUFREQ_RELATION_H); ++ trace_cpufreq_interactive_setspeed(cpu, ++ pcpu->target_freq, ++ pcpu->policy->cur); ++ ++ up_read(&pcpu->enable_sem); ++ } ++ } ++ ++ return 0; ++} ++ ++static void cpufreq_interactive_boost(void) ++{ ++ int i; ++ int anyboost = 0; ++ unsigned long flags; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ struct cpufreq_interactive_tunables *tunables; ++ ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ ++ for_each_online_cpu(i) { ++ pcpu = &per_cpu(cpuinfo, i); ++ tunables = pcpu->policy->governor_data; ++ ++ if (pcpu->target_freq < tunables->hispeed_freq) { ++ pcpu->target_freq = tunables->hispeed_freq; ++ cpumask_set_cpu(i, &speedchange_cpumask); ++ pcpu->hispeed_validate_time = ++ ktime_to_us(ktime_get()); ++ anyboost = 1; ++ } ++ ++ /* ++ * Set floor freq and (re)start timer for when last ++ * validated. ++ */ ++ ++ pcpu->floor_freq = tunables->hispeed_freq; ++ pcpu->floor_validate_time = ktime_to_us(ktime_get()); ++ } ++ ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ ++ if (anyboost) ++ wake_up_process(speedchange_task); ++} ++ ++static int cpufreq_interactive_notifier( ++ struct notifier_block *nb, unsigned long val, void *data) ++{ ++ struct cpufreq_freqs *freq = data; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ int cpu; ++ unsigned long flags; ++ ++ if (val == CPUFREQ_POSTCHANGE) { ++ pcpu = &per_cpu(cpuinfo, freq->cpu); ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return 0; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return 0; ++ } ++ ++ for_each_cpu(cpu, pcpu->policy->cpus) { ++ struct cpufreq_interactive_cpuinfo *pjcpu = ++ &per_cpu(cpuinfo, cpu); ++ if (cpu != freq->cpu) { ++ if (!down_read_trylock(&pjcpu->enable_sem)) ++ continue; ++ if (!pjcpu->governor_enabled) { ++ up_read(&pjcpu->enable_sem); ++ continue; ++ } ++ } ++ spin_lock_irqsave(&pjcpu->load_lock, flags); ++ update_load(cpu); ++ spin_unlock_irqrestore(&pjcpu->load_lock, flags); ++ if (cpu != freq->cpu) ++ up_read(&pjcpu->enable_sem); ++ } ++ ++ up_read(&pcpu->enable_sem); ++ } ++ return 0; ++} ++ ++static struct notifier_block cpufreq_notifier_block = { ++ .notifier_call = cpufreq_interactive_notifier, ++}; ++ ++static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) ++{ ++ const char *cp; ++ int i; ++ int ntokens = 1; ++ unsigned int *tokenized_data; ++ int err = -EINVAL; ++ ++ cp = buf; ++ while ((cp = strpbrk(cp + 1, " :"))) ++ ntokens++; ++ ++ if (!(ntokens & 0x1)) ++ goto err; ++ ++ tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); ++ if (!tokenized_data) { ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ cp = buf; ++ i = 0; ++ while (i < ntokens) { ++ if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) ++ goto err_kfree; ++ ++ cp = strpbrk(cp, " :"); ++ if (!cp) ++ break; ++ cp++; ++ } ++ ++ if (i != ntokens) ++ goto err_kfree; ++ ++ *num_tokens = ntokens; ++ return tokenized_data; ++ ++err_kfree: ++ kfree(tokenized_data); ++err: ++ return ERR_PTR(err); ++} ++ ++static ssize_t show_target_loads( ++ struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ int i; ++ ssize_t ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->target_loads_lock, flags); ++ ++ for (i = 0; i < tunables->ntarget_loads; i++) ++ ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i], ++ i & 0x1 ? ":" : " "); ++ ++ sprintf(buf + ret - 1, "\n"); ++ spin_unlock_irqrestore(&tunables->target_loads_lock, flags); ++ return ret; ++} ++ ++static ssize_t store_target_loads( ++ struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ntokens; ++ unsigned int *new_target_loads = NULL; ++ unsigned long flags; ++ ++ new_target_loads = get_tokenized_data(buf, &ntokens); ++ if (IS_ERR(new_target_loads)) ++ return PTR_RET(new_target_loads); ++ ++ spin_lock_irqsave(&tunables->target_loads_lock, flags); ++ if (tunables->target_loads != default_target_loads) ++ kfree(tunables->target_loads); ++ tunables->target_loads = new_target_loads; ++ tunables->ntarget_loads = ntokens; ++ spin_unlock_irqrestore(&tunables->target_loads_lock, flags); ++ return count; ++} ++ ++static ssize_t show_above_hispeed_delay( ++ struct cpufreq_interactive_tunables *tunables, char *buf) ++{ ++ int i; ++ ssize_t ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); ++ ++ for (i = 0; i < tunables->nabove_hispeed_delay; i++) ++ ret += sprintf(buf + ret, "%u%s", ++ tunables->above_hispeed_delay[i], ++ i & 0x1 ? ":" : " "); ++ ++ sprintf(buf + ret - 1, "\n"); ++ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); ++ return ret; ++} ++ ++static ssize_t store_above_hispeed_delay( ++ struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ntokens; ++ unsigned int *new_above_hispeed_delay = NULL; ++ unsigned long flags; ++ ++ new_above_hispeed_delay = get_tokenized_data(buf, &ntokens); ++ if (IS_ERR(new_above_hispeed_delay)) ++ return PTR_RET(new_above_hispeed_delay); ++ ++ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); ++ if (tunables->above_hispeed_delay != default_above_hispeed_delay) ++ kfree(tunables->above_hispeed_delay); ++ tunables->above_hispeed_delay = new_above_hispeed_delay; ++ tunables->nabove_hispeed_delay = ntokens; ++ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); ++ return count; ++ ++} ++ ++static ssize_t show_hispeed_freq(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%u\n", tunables->hispeed_freq); ++} ++ ++static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ long unsigned int val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->hispeed_freq = val; ++ return count; ++} ++ ++static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables ++ *tunables, char *buf) ++{ ++ return sprintf(buf, "%lu\n", tunables->go_hispeed_load); ++} ++ ++static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables ++ *tunables, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->go_hispeed_load = val; ++ return count; ++} ++ ++static ssize_t show_min_sample_time(struct cpufreq_interactive_tunables ++ *tunables, char *buf) ++{ ++ return sprintf(buf, "%lu\n", tunables->min_sample_time); ++} ++ ++static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables ++ *tunables, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->min_sample_time = val; ++ return count; ++} ++ ++static ssize_t show_timer_rate(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%lu\n", tunables->timer_rate); ++} ++ ++static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = strict_strtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->timer_rate = val; ++ return count; ++} ++ ++static ssize_t show_timer_slack(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", tunables->timer_slack_val); ++} ++ ++static ssize_t store_timer_slack(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtol(buf, 10, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->timer_slack_val = val; ++ return count; ++} ++ ++static ssize_t show_boost(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", tunables->boost_val); ++} ++ ++static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->boost_val = val; ++ ++ if (tunables->boost_val) { ++ trace_cpufreq_interactive_boost("on"); ++ cpufreq_interactive_boost(); ++ } else { ++ trace_cpufreq_interactive_unboost("off"); ++ } ++ ++ return count; ++} ++ ++static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->boostpulse_endtime = ktime_to_us(ktime_get()) + ++ tunables->boostpulse_duration_val; ++ trace_cpufreq_interactive_boost("pulse"); ++ cpufreq_interactive_boost(); ++ return count; ++} ++ ++static ssize_t show_boostpulse_duration(struct cpufreq_interactive_tunables ++ *tunables, char *buf) ++{ ++ return sprintf(buf, "%d\n", tunables->boostpulse_duration_val); ++} ++ ++static ssize_t store_boostpulse_duration(struct cpufreq_interactive_tunables ++ *tunables, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->boostpulse_duration_val = val; ++ return count; ++} ++ ++static ssize_t show_io_is_busy(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%u\n", tunables->io_is_busy); ++} ++ ++static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->io_is_busy = val; ++ return count; ++} ++ ++/* ++ * Create show/store routines ++ * - sys: One governor instance for complete SYSTEM ++ * - pol: One governor instance per struct cpufreq_policy ++ */ ++#define show_gov_pol_sys(file_name) \ ++static ssize_t show_##file_name##_gov_sys \ ++(struct kobject *kobj, struct attribute *attr, char *buf) \ ++{ \ ++ return show_##file_name(common_tunables, buf); \ ++} \ ++ \ ++static ssize_t show_##file_name##_gov_pol \ ++(struct cpufreq_policy *policy, char *buf) \ ++{ \ ++ return show_##file_name(policy->governor_data, buf); \ ++} ++ ++#define store_gov_pol_sys(file_name) \ ++static ssize_t store_##file_name##_gov_sys \ ++(struct kobject *kobj, struct attribute *attr, const char *buf, \ ++ size_t count) \ ++{ \ ++ return store_##file_name(common_tunables, buf, count); \ ++} \ ++ \ ++static ssize_t store_##file_name##_gov_pol \ ++(struct cpufreq_policy *policy, const char *buf, size_t count) \ ++{ \ ++ return store_##file_name(policy->governor_data, buf, count); \ ++} ++ ++#define show_store_gov_pol_sys(file_name) \ ++show_gov_pol_sys(file_name); \ ++store_gov_pol_sys(file_name) ++ ++show_store_gov_pol_sys(target_loads); ++show_store_gov_pol_sys(above_hispeed_delay); ++show_store_gov_pol_sys(hispeed_freq); ++show_store_gov_pol_sys(go_hispeed_load); ++show_store_gov_pol_sys(min_sample_time); ++show_store_gov_pol_sys(timer_rate); ++show_store_gov_pol_sys(timer_slack); ++show_store_gov_pol_sys(boost); ++store_gov_pol_sys(boostpulse); ++show_store_gov_pol_sys(boostpulse_duration); ++show_store_gov_pol_sys(io_is_busy); ++ ++#define gov_sys_attr_rw(_name) \ ++static struct global_attr _name##_gov_sys = \ ++__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys) ++ ++#define gov_pol_attr_rw(_name) \ ++static struct freq_attr _name##_gov_pol = \ ++__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol) ++ ++#define gov_sys_pol_attr_rw(_name) \ ++ gov_sys_attr_rw(_name); \ ++ gov_pol_attr_rw(_name) ++ ++gov_sys_pol_attr_rw(target_loads); ++gov_sys_pol_attr_rw(above_hispeed_delay); ++gov_sys_pol_attr_rw(hispeed_freq); ++gov_sys_pol_attr_rw(go_hispeed_load); ++gov_sys_pol_attr_rw(min_sample_time); ++gov_sys_pol_attr_rw(timer_rate); ++gov_sys_pol_attr_rw(timer_slack); ++gov_sys_pol_attr_rw(boost); ++gov_sys_pol_attr_rw(boostpulse_duration); ++gov_sys_pol_attr_rw(io_is_busy); ++ ++static struct global_attr boostpulse_gov_sys = ++ __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys); ++ ++static struct freq_attr boostpulse_gov_pol = ++ __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_pol); ++ ++/* One Governor instance for entire system */ ++static struct attribute *interactive_attributes_gov_sys[] = { ++ &target_loads_gov_sys.attr, ++ &above_hispeed_delay_gov_sys.attr, ++ &hispeed_freq_gov_sys.attr, ++ &go_hispeed_load_gov_sys.attr, ++ &min_sample_time_gov_sys.attr, ++ &timer_rate_gov_sys.attr, ++ &timer_slack_gov_sys.attr, ++ &boost_gov_sys.attr, ++ &boostpulse_gov_sys.attr, ++ &boostpulse_duration_gov_sys.attr, ++ &io_is_busy_gov_sys.attr, ++ NULL, ++}; ++ ++static struct attribute_group interactive_attr_group_gov_sys = { ++ .attrs = interactive_attributes_gov_sys, ++ .name = "interactive", ++}; ++ ++/* Per policy governor instance */ ++static struct attribute *interactive_attributes_gov_pol[] = { ++ &target_loads_gov_pol.attr, ++ &above_hispeed_delay_gov_pol.attr, ++ &hispeed_freq_gov_pol.attr, ++ &go_hispeed_load_gov_pol.attr, ++ &min_sample_time_gov_pol.attr, ++ &timer_rate_gov_pol.attr, ++ &timer_slack_gov_pol.attr, ++ &boost_gov_pol.attr, ++ &boostpulse_gov_pol.attr, ++ &boostpulse_duration_gov_pol.attr, ++ &io_is_busy_gov_pol.attr, ++ NULL, ++}; ++ ++static struct attribute_group interactive_attr_group_gov_pol = { ++ .attrs = interactive_attributes_gov_pol, ++ .name = "interactive", ++}; ++ ++static struct attribute_group *get_sysfs_attr(void) ++{ ++ if (have_governor_per_policy()) ++ return &interactive_attr_group_gov_pol; ++ else ++ return &interactive_attr_group_gov_sys; ++} ++ ++static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, ++ unsigned long val, ++ void *data) ++{ ++ switch (val) { ++ case IDLE_START: ++ cpufreq_interactive_idle_start(); ++ break; ++ case IDLE_END: ++ cpufreq_interactive_idle_end(); ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block cpufreq_interactive_idle_nb = { ++ .notifier_call = cpufreq_interactive_idle_notifier, ++}; ++ ++static int cpufreq_governor_interactive(struct cpufreq_policy *policy, ++ unsigned int event) ++{ ++ int rc; ++ unsigned int j; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ struct cpufreq_frequency_table *freq_table; ++ struct cpufreq_interactive_tunables *tunables; ++ ++ if (have_governor_per_policy()) ++ tunables = policy->governor_data; ++ else ++ tunables = common_tunables; ++ ++ WARN_ON(!tunables && (event != CPUFREQ_GOV_POLICY_INIT)); ++ ++ switch (event) { ++ case CPUFREQ_GOV_POLICY_INIT: ++ if (have_governor_per_policy()) { ++ WARN_ON(tunables); ++ } else if (tunables) { ++ tunables->usage_count++; ++ policy->governor_data = tunables; ++ return 0; ++ } ++ ++ tunables = kzalloc(sizeof(*tunables), GFP_KERNEL); ++ if (!tunables) { ++ pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__); ++ return -ENOMEM; ++ } ++ ++ tunables->usage_count = 1; ++ tunables->above_hispeed_delay = default_above_hispeed_delay; ++ tunables->nabove_hispeed_delay = ++ ARRAY_SIZE(default_above_hispeed_delay); ++ tunables->go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; ++ tunables->target_loads = default_target_loads; ++ tunables->ntarget_loads = ARRAY_SIZE(default_target_loads); ++ tunables->min_sample_time = DEFAULT_MIN_SAMPLE_TIME; ++ tunables->timer_rate = DEFAULT_TIMER_RATE; ++ tunables->boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; ++ tunables->timer_slack_val = DEFAULT_TIMER_SLACK; ++ ++ spin_lock_init(&tunables->target_loads_lock); ++ spin_lock_init(&tunables->above_hispeed_delay_lock); ++ ++ policy->governor_data = tunables; ++ if (!have_governor_per_policy()) { ++ common_tunables = tunables; ++ WARN_ON(cpufreq_get_global_kobject()); ++ } ++ ++ rc = sysfs_create_group(get_governor_parent_kobj(policy), ++ get_sysfs_attr()); ++ if (rc) { ++ kfree(tunables); ++ policy->governor_data = NULL; ++ if (!have_governor_per_policy()) ++ common_tunables = NULL; ++ return rc; ++ } ++ ++ if (!policy->governor->initialized) { ++ idle_notifier_register(&cpufreq_interactive_idle_nb); ++ cpufreq_register_notifier(&cpufreq_notifier_block, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ } ++ ++ break; ++ ++ case CPUFREQ_GOV_POLICY_EXIT: ++ if (!--tunables->usage_count) { ++ sysfs_remove_group(get_governor_parent_kobj(policy), ++ get_sysfs_attr()); ++ ++ if (!have_governor_per_policy()) ++ cpufreq_put_global_kobject(); ++ ++ if (policy->governor->initialized == 1) { ++ cpufreq_unregister_notifier(&cpufreq_notifier_block, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ idle_notifier_unregister(&cpufreq_interactive_idle_nb); ++ } ++ ++ kfree(tunables); ++ common_tunables = NULL; ++ } ++ ++ policy->governor_data = NULL; ++ break; ++ ++ case CPUFREQ_GOV_START: ++ mutex_lock(&gov_lock); ++ ++ freq_table = cpufreq_frequency_get_table(policy->cpu); ++ if (!tunables->hispeed_freq) ++ tunables->hispeed_freq = policy->max; ++ ++ for_each_cpu(j, policy->cpus) { ++ pcpu = &per_cpu(cpuinfo, j); ++ pcpu->policy = policy; ++ pcpu->target_freq = policy->cur; ++ pcpu->freq_table = freq_table; ++ pcpu->floor_freq = pcpu->target_freq; ++ pcpu->floor_validate_time = ++ ktime_to_us(ktime_get()); ++ pcpu->hispeed_validate_time = ++ pcpu->floor_validate_time; ++ down_write(&pcpu->enable_sem); ++ del_timer_sync(&pcpu->cpu_timer); ++ del_timer_sync(&pcpu->cpu_slack_timer); ++ cpufreq_interactive_timer_start(tunables, j); ++ pcpu->governor_enabled = 1; ++ up_write(&pcpu->enable_sem); ++ } ++ ++ mutex_unlock(&gov_lock); ++ break; ++ ++ case CPUFREQ_GOV_STOP: ++ mutex_lock(&gov_lock); ++ for_each_cpu(j, policy->cpus) { ++ pcpu = &per_cpu(cpuinfo, j); ++ down_write(&pcpu->enable_sem); ++ pcpu->governor_enabled = 0; ++ del_timer_sync(&pcpu->cpu_timer); ++ del_timer_sync(&pcpu->cpu_slack_timer); ++ up_write(&pcpu->enable_sem); ++ } ++ ++ mutex_unlock(&gov_lock); ++ break; ++ ++ case CPUFREQ_GOV_LIMITS: ++ if (policy->max < policy->cur) ++ __cpufreq_driver_target(policy, ++ policy->max, CPUFREQ_RELATION_H); ++ else if (policy->min > policy->cur) ++ __cpufreq_driver_target(policy, ++ policy->min, CPUFREQ_RELATION_L); ++ for_each_cpu(j, policy->cpus) { ++ pcpu = &per_cpu(cpuinfo, j); ++ ++ /* hold write semaphore to avoid race */ ++ down_write(&pcpu->enable_sem); ++ if (pcpu->governor_enabled == 0) { ++ up_write(&pcpu->enable_sem); ++ continue; ++ } ++ ++ /* update target_freq firstly */ ++ if (policy->max < pcpu->target_freq) ++ pcpu->target_freq = policy->max; ++ else if (policy->min > pcpu->target_freq) ++ pcpu->target_freq = policy->min; ++ ++ /* Reschedule timer. ++ * Delete the timers, else the timer callback may ++ * return without re-arm the timer when failed ++ * acquire the semaphore. This race may cause timer ++ * stopped unexpectedly. ++ */ ++ del_timer_sync(&pcpu->cpu_timer); ++ del_timer_sync(&pcpu->cpu_slack_timer); ++ cpufreq_interactive_timer_start(tunables, j); ++ up_write(&pcpu->enable_sem); ++ } ++ break; ++ } ++ return 0; ++} ++ ++#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++static ++#endif ++struct cpufreq_governor cpufreq_gov_interactive = { ++ .name = "interactive", ++ .governor = cpufreq_governor_interactive, ++ .max_transition_latency = 10000000, ++ .owner = THIS_MODULE, ++}; ++ ++static void cpufreq_interactive_nop_timer(unsigned long data) ++{ ++} ++ ++static int __init cpufreq_interactive_init(void) ++{ ++ unsigned int i; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; ++ ++ /* Initalize per-cpu timers */ ++ for_each_possible_cpu(i) { ++ pcpu = &per_cpu(cpuinfo, i); ++ init_timer_deferrable(&pcpu->cpu_timer); ++ pcpu->cpu_timer.function = cpufreq_interactive_timer; ++ pcpu->cpu_timer.data = i; ++ init_timer(&pcpu->cpu_slack_timer); ++ pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; ++ spin_lock_init(&pcpu->load_lock); ++ init_rwsem(&pcpu->enable_sem); ++ } ++ ++ spin_lock_init(&speedchange_cpumask_lock); ++ mutex_init(&gov_lock); ++ speedchange_task = ++ kthread_create(cpufreq_interactive_speedchange_task, NULL, ++ "cfinteractive"); ++ if (IS_ERR(speedchange_task)) ++ return PTR_ERR(speedchange_task); ++ ++ sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); ++ get_task_struct(speedchange_task); ++ ++ /* NB: wake up so the thread does not look hung to the freezer */ ++ wake_up_process(speedchange_task); ++ ++ return cpufreq_register_governor(&cpufreq_gov_interactive); ++} ++ ++#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++fs_initcall(cpufreq_interactive_init); ++#else ++module_init(cpufreq_interactive_init); ++#endif ++ ++static void __exit cpufreq_interactive_exit(void) ++{ ++ cpufreq_unregister_governor(&cpufreq_gov_interactive); ++ kthread_stop(speedchange_task); ++ put_task_struct(speedchange_task); ++} ++ ++module_exit(cpufreq_interactive_exit); ++ ++MODULE_AUTHOR("Mike Chan "); ++MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " ++ "Latency sensitive workloads"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/cpufreq/highbank-cpufreq.c linux-openelec/drivers/cpufreq/highbank-cpufreq.c +--- linux-3.14.36/drivers/cpufreq/highbank-cpufreq.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/cpufreq/highbank-cpufreq.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,7 +19,7 @@ + #include + #include + #include +-#include ++#include + #include + + #define HB_CPUFREQ_CHANGE_NOTE 0x80000001 +diff -Nur linux-3.14.36/drivers/cpufreq/imx6-cpufreq.c linux-openelec/drivers/cpufreq/imx6-cpufreq.c +--- linux-3.14.36/drivers/cpufreq/imx6-cpufreq.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/cpufreq/imx6-cpufreq.c 2015-07-24 18:03:30.408842002 -0500 +@@ -0,0 +1,400 @@ ++/* ++ * Copyright (C) 2013 Freescale Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PU_SOC_VOLTAGE_NORMAL 1250000 ++#define PU_SOC_VOLTAGE_HIGH 1275000 ++#define FREQ_1P2_GHZ 1200000000 ++ ++static struct regulator *arm_reg; ++static struct regulator *pu_reg; ++static struct regulator *soc_reg; ++ ++static struct clk *arm_clk; ++static struct clk *pll1_sys_clk; ++static struct clk *pll1_sw_clk; ++static struct clk *step_clk; ++static struct clk *pll2_pfd2_396m_clk; ++ ++static struct device *cpu_dev; ++static struct cpufreq_frequency_table *freq_table; ++static unsigned int transition_latency; ++static struct mutex set_cpufreq_lock; ++ ++static u32 *imx6_soc_volt; ++static u32 soc_opp_count; ++ ++static int imx6_set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ struct dev_pm_opp *opp; ++ unsigned long freq_hz, volt, volt_old; ++ unsigned int old_freq, new_freq; ++ int ret; ++ ++ mutex_lock(&set_cpufreq_lock); ++ ++ new_freq = freq_table[index].frequency; ++ freq_hz = new_freq * 1000; ++ old_freq = clk_get_rate(arm_clk) / 1000; ++ ++ rcu_read_lock(); ++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); ++ if (IS_ERR(opp)) { ++ rcu_read_unlock(); ++ dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz); ++ ret = PTR_ERR(opp); ++ goto unlock; ++ } ++ ++ volt = dev_pm_opp_get_voltage(opp); ++ rcu_read_unlock(); ++ volt_old = regulator_get_voltage(arm_reg); ++ ++ dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", ++ old_freq / 1000, volt_old / 1000, ++ new_freq / 1000, volt / 1000); ++ ++ /* ++ * CPU freq is increasing, so need to ensure ++ * that bus frequency is increased too. ++ */ ++ if (old_freq == freq_table[0].frequency) ++ request_bus_freq(BUS_FREQ_HIGH); ++ ++ /* scaling up? scale voltage before frequency */ ++ if (new_freq > old_freq) { ++ if (regulator_is_enabled(pu_reg)) { ++ ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); ++ if (ret) { ++ dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret); ++ goto unlock; ++ } ++ } ++ ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); ++ if (ret) { ++ dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret); ++ goto unlock; ++ } ++ ret = regulator_set_voltage_tol(arm_reg, volt, 0); ++ if (ret) { ++ dev_err(cpu_dev, ++ "failed to scale vddarm up: %d\n", ret); ++ goto unlock; ++ } ++ } ++ ++ /* ++ * The setpoints are selected per PLL/PDF frequencies, so we need to ++ * reprogram PLL for frequency scaling. The procedure of reprogramming ++ * PLL1 is as below. ++ * ++ * - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it ++ * - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it ++ * - Disable pll2_pfd2_396m_clk ++ */ ++ clk_set_parent(step_clk, pll2_pfd2_396m_clk); ++ clk_set_parent(pll1_sw_clk, step_clk); ++ if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { ++ clk_set_rate(pll1_sys_clk, new_freq * 1000); ++ clk_set_parent(pll1_sw_clk, pll1_sys_clk); ++ } ++ ++ /* Ensure the arm clock divider is what we expect */ ++ ret = clk_set_rate(arm_clk, new_freq * 1000); ++ if (ret) { ++ dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); ++ regulator_set_voltage_tol(arm_reg, volt_old, 0); ++ goto unlock; ++ } ++ ++ /* scaling down? scale voltage after frequency */ ++ if (new_freq < old_freq) { ++ ret = regulator_set_voltage_tol(arm_reg, volt, 0); ++ if (ret) { ++ dev_warn(cpu_dev, ++ "failed to scale vddarm down: %d\n", ret); ++ ret = 0; ++ } ++ ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); ++ if (ret) { ++ dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret); ++ ret = 0; ++ } ++ if (regulator_is_enabled(pu_reg)) { ++ ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); ++ if (ret) { ++ dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret); ++ ret = 0; ++ } ++ } ++ } ++ ++ if (policy->cur == freq_table[0].frequency) ++ release_bus_freq(BUS_FREQ_HIGH); ++ ++unlock: ++ mutex_unlock(&set_cpufreq_lock); ++ return ret; ++} ++ ++static int imx6_cpufreq_init(struct cpufreq_policy *policy) ++{ ++ policy->clk = arm_clk; ++ ++ if (policy->cur > freq_table[0].frequency) ++ request_bus_freq(BUS_FREQ_HIGH); ++ ++ return cpufreq_generic_init(policy, freq_table, transition_latency); ++} ++ ++static struct cpufreq_driver imx6_cpufreq_driver = { ++ .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = imx6_set_target, ++ .get = cpufreq_generic_get, ++ .init = imx6_cpufreq_init, ++ .exit = cpufreq_generic_exit, ++ .name = "imx6-cpufreq", ++ .attr = cpufreq_generic_attr, ++}; ++ ++static int imx6_cpufreq_pm_notify(struct notifier_block *nb, ++ unsigned long event, void *dummy) ++{ ++ struct cpufreq_policy *data = cpufreq_cpu_get(0); ++ static u32 cpufreq_policy_min_pre_suspend; ++ ++ /* ++ * During suspend/resume, When cpufreq driver try to increase ++ * voltage/freq, it needs to control I2C/SPI to communicate ++ * with external PMIC to adjust voltage, but these I2C/SPI ++ * devices may be already suspended, to avoid such scenario, ++ * we just increase cpufreq to highest setpoint before suspend. ++ */ ++ switch (event) { ++ case PM_SUSPEND_PREPARE: ++ cpufreq_policy_min_pre_suspend = data->user_policy.min; ++ data->user_policy.min = data->user_policy.max; ++ break; ++ case PM_POST_SUSPEND: ++ data->user_policy.min = cpufreq_policy_min_pre_suspend; ++ break; ++ default: ++ break; ++ } ++ ++ cpufreq_update_policy(0); ++ ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block imx6_cpufreq_pm_notifier = { ++ .notifier_call = imx6_cpufreq_pm_notify, ++}; ++ ++static int imx6_cpufreq_probe(struct platform_device *pdev) ++{ ++ struct device_node *np; ++ struct dev_pm_opp *opp; ++ unsigned long min_volt, max_volt; ++ int num, ret; ++ const struct property *prop; ++ const __be32 *val; ++ u32 nr, i, j; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get cpu0 device\n"); ++ return -ENODEV; ++ } ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) { ++ dev_err(cpu_dev, "failed to find cpu0 node\n"); ++ return -ENOENT; ++ } ++ ++ arm_clk = devm_clk_get(cpu_dev, "arm"); ++ pll1_sys_clk = devm_clk_get(cpu_dev, "pll1_sys"); ++ pll1_sw_clk = devm_clk_get(cpu_dev, "pll1_sw"); ++ step_clk = devm_clk_get(cpu_dev, "step"); ++ pll2_pfd2_396m_clk = devm_clk_get(cpu_dev, "pll2_pfd2_396m"); ++ if (IS_ERR(arm_clk) || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk) || ++ IS_ERR(step_clk) || IS_ERR(pll2_pfd2_396m_clk)) { ++ dev_err(cpu_dev, "failed to get clocks\n"); ++ ret = -ENOENT; ++ goto put_node; ++ } ++ ++ arm_reg = devm_regulator_get(cpu_dev, "arm"); ++ pu_reg = devm_regulator_get(cpu_dev, "pu"); ++ soc_reg = devm_regulator_get(cpu_dev, "soc"); ++ if (IS_ERR(arm_reg) || IS_ERR(pu_reg) || IS_ERR(soc_reg)) { ++ dev_err(cpu_dev, "failed to get regulators\n"); ++ ret = -ENOENT; ++ goto put_node; ++ } ++ ++ /* ++ * We expect an OPP table supplied by platform. ++ * Just, incase the platform did not supply the OPP ++ * table, it will try to get it. ++ */ ++ num = dev_pm_opp_get_opp_count(cpu_dev); ++ if (num < 0) { ++ ret = of_init_opp_table(cpu_dev); ++ if (ret < 0) { ++ dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); ++ goto put_node; ++ } ++ ++ num = dev_pm_opp_get_opp_count(cpu_dev); ++ if (num < 0) { ++ ret = num; ++ dev_err(cpu_dev, "no OPP table is found: %d\n", ret); ++ goto put_node; ++ } ++ } ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ++ if (ret) { ++ dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); ++ goto put_node; ++ } ++ ++ /* Make imx6_soc_volt array's size same as arm opp number */ ++ imx6_soc_volt = devm_kzalloc(cpu_dev, sizeof(*imx6_soc_volt) * num, GFP_KERNEL); ++ if (imx6_soc_volt == NULL) { ++ ret = -ENOMEM; ++ goto free_freq_table; ++ } ++ ++ prop = of_find_property(np, "fsl,soc-operating-points", NULL); ++ if (!prop || !prop->value) ++ goto soc_opp_out; ++ ++ /* ++ * Each OPP is a set of tuples consisting of frequency and ++ * voltage like . ++ */ ++ nr = prop->length / sizeof(u32); ++ if (nr % 2 || (nr / 2) < num) ++ goto soc_opp_out; ++ ++ for (j = 0; j < num; j++) { ++ val = prop->value; ++ for (i = 0; i < nr / 2; i++) { ++ unsigned long freq = be32_to_cpup(val++); ++ unsigned long volt = be32_to_cpup(val++); ++ if (freq_table[j].frequency == freq) { ++ imx6_soc_volt[soc_opp_count++] = volt; ++#ifdef CONFIG_MX6_VPU_352M ++ if (freq == 792000) { ++ pr_info("increase SOC/PU voltage for VPU352MHz\n"); ++ imx6_soc_volt[soc_opp_count-1] = 1250000; ++ } ++#endif ++ ++ break; ++ } ++ } ++ } ++ ++soc_opp_out: ++ /* use fixed soc opp volt if no valid soc opp info found in dtb */ ++ if (soc_opp_count != num) { ++ dev_warn(cpu_dev, "can NOT find valid fsl,soc-operating-points property in dtb, use default value!\n"); ++ for (j = 0; j < num; j++) ++ imx6_soc_volt[j] = PU_SOC_VOLTAGE_NORMAL; ++ if (freq_table[num - 1].frequency * 1000 == FREQ_1P2_GHZ) ++ imx6_soc_volt[num - 1] = PU_SOC_VOLTAGE_HIGH; ++ } ++ ++ if (of_property_read_u32(np, "clock-latency", &transition_latency)) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ /* ++ * Calculate the ramp time for max voltage change in the ++ * VDDSOC and VDDPU regulators. ++ */ ++ ret = regulator_set_voltage_time(soc_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); ++ if (ret > 0) ++ transition_latency += ret * 1000; ++ ret = regulator_set_voltage_time(pu_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); ++ if (ret > 0) ++ transition_latency += ret * 1000; ++ ++ /* ++ * OPP is maintained in order of increasing frequency, and ++ * freq_table initialised from OPP is therefore sorted in the ++ * same order. ++ */ ++ rcu_read_lock(); ++ opp = dev_pm_opp_find_freq_exact(cpu_dev, ++ freq_table[0].frequency * 1000, true); ++ min_volt = dev_pm_opp_get_voltage(opp); ++ opp = dev_pm_opp_find_freq_exact(cpu_dev, ++ freq_table[--num].frequency * 1000, true); ++ max_volt = dev_pm_opp_get_voltage(opp); ++ rcu_read_unlock(); ++ ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); ++ if (ret > 0) ++ transition_latency += ret * 1000; ++ ++ mutex_init(&set_cpufreq_lock); ++ register_pm_notifier(&imx6_cpufreq_pm_notifier); ++ ++ ret = cpufreq_register_driver(&imx6_cpufreq_driver); ++ if (ret) { ++ dev_err(cpu_dev, "failed register driver: %d\n", ret); ++ goto free_freq_table; ++ } ++ ++ of_node_put(np); ++ return 0; ++ ++free_freq_table: ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++put_node: ++ of_node_put(np); ++ return ret; ++} ++ ++static int imx6_cpufreq_remove(struct platform_device *pdev) ++{ ++ cpufreq_unregister_driver(&imx6_cpufreq_driver); ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++ ++ return 0; ++} ++ ++static struct platform_driver imx6_cpufreq_platdrv = { ++ .driver = { ++ .name = "imx6-cpufreq", ++ .owner = THIS_MODULE, ++ }, ++ .probe = imx6_cpufreq_probe, ++ .remove = imx6_cpufreq_remove, ++}; ++module_platform_driver(imx6_cpufreq_platdrv); ++ ++MODULE_AUTHOR("Shawn Guo "); ++MODULE_DESCRIPTION("Freescale i.MX6Q cpufreq driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/cpufreq/imx6q-cpufreq.c linux-openelec/drivers/cpufreq/imx6q-cpufreq.c +--- linux-3.14.36/drivers/cpufreq/imx6q-cpufreq.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/cpufreq/imx6q-cpufreq.c 1969-12-31 18:00:00.000000000 -0600 +@@ -1,330 +0,0 @@ +-/* +- * Copyright (C) 2013 Freescale Semiconductor, Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define PU_SOC_VOLTAGE_NORMAL 1250000 +-#define PU_SOC_VOLTAGE_HIGH 1275000 +-#define FREQ_1P2_GHZ 1200000000 +- +-static struct regulator *arm_reg; +-static struct regulator *pu_reg; +-static struct regulator *soc_reg; +- +-static struct clk *arm_clk; +-static struct clk *pll1_sys_clk; +-static struct clk *pll1_sw_clk; +-static struct clk *step_clk; +-static struct clk *pll2_pfd2_396m_clk; +- +-static struct device *cpu_dev; +-static struct cpufreq_frequency_table *freq_table; +-static unsigned int transition_latency; +- +-static u32 *imx6_soc_volt; +-static u32 soc_opp_count; +- +-static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) +-{ +- struct dev_pm_opp *opp; +- unsigned long freq_hz, volt, volt_old; +- unsigned int old_freq, new_freq; +- int ret; +- +- new_freq = freq_table[index].frequency; +- freq_hz = new_freq * 1000; +- old_freq = clk_get_rate(arm_clk) / 1000; +- +- rcu_read_lock(); +- opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); +- if (IS_ERR(opp)) { +- rcu_read_unlock(); +- dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz); +- return PTR_ERR(opp); +- } +- +- volt = dev_pm_opp_get_voltage(opp); +- rcu_read_unlock(); +- volt_old = regulator_get_voltage(arm_reg); +- +- dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", +- old_freq / 1000, volt_old / 1000, +- new_freq / 1000, volt / 1000); +- +- /* scaling up? scale voltage before frequency */ +- if (new_freq > old_freq) { +- ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); +- if (ret) { +- dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret); +- return ret; +- } +- ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); +- if (ret) { +- dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret); +- return ret; +- } +- ret = regulator_set_voltage_tol(arm_reg, volt, 0); +- if (ret) { +- dev_err(cpu_dev, +- "failed to scale vddarm up: %d\n", ret); +- return ret; +- } +- } +- +- /* +- * The setpoints are selected per PLL/PDF frequencies, so we need to +- * reprogram PLL for frequency scaling. The procedure of reprogramming +- * PLL1 is as below. +- * +- * - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it +- * - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it +- * - Disable pll2_pfd2_396m_clk +- */ +- clk_set_parent(step_clk, pll2_pfd2_396m_clk); +- clk_set_parent(pll1_sw_clk, step_clk); +- if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { +- clk_set_rate(pll1_sys_clk, new_freq * 1000); +- clk_set_parent(pll1_sw_clk, pll1_sys_clk); +- } +- +- /* Ensure the arm clock divider is what we expect */ +- ret = clk_set_rate(arm_clk, new_freq * 1000); +- if (ret) { +- dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); +- regulator_set_voltage_tol(arm_reg, volt_old, 0); +- return ret; +- } +- +- /* scaling down? scale voltage after frequency */ +- if (new_freq < old_freq) { +- ret = regulator_set_voltage_tol(arm_reg, volt, 0); +- if (ret) { +- dev_warn(cpu_dev, +- "failed to scale vddarm down: %d\n", ret); +- ret = 0; +- } +- ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); +- if (ret) { +- dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret); +- ret = 0; +- } +- ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); +- if (ret) { +- dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret); +- ret = 0; +- } +- } +- +- return 0; +-} +- +-static int imx6q_cpufreq_init(struct cpufreq_policy *policy) +-{ +- policy->clk = arm_clk; +- return cpufreq_generic_init(policy, freq_table, transition_latency); +-} +- +-static struct cpufreq_driver imx6q_cpufreq_driver = { +- .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, +- .verify = cpufreq_generic_frequency_table_verify, +- .target_index = imx6q_set_target, +- .get = cpufreq_generic_get, +- .init = imx6q_cpufreq_init, +- .exit = cpufreq_generic_exit, +- .name = "imx6q-cpufreq", +- .attr = cpufreq_generic_attr, +-}; +- +-static int imx6q_cpufreq_probe(struct platform_device *pdev) +-{ +- struct device_node *np; +- struct dev_pm_opp *opp; +- unsigned long min_volt, max_volt; +- int num, ret; +- const struct property *prop; +- const __be32 *val; +- u32 nr, i, j; +- +- cpu_dev = get_cpu_device(0); +- if (!cpu_dev) { +- pr_err("failed to get cpu0 device\n"); +- return -ENODEV; +- } +- +- np = of_node_get(cpu_dev->of_node); +- if (!np) { +- dev_err(cpu_dev, "failed to find cpu0 node\n"); +- return -ENOENT; +- } +- +- arm_clk = devm_clk_get(cpu_dev, "arm"); +- pll1_sys_clk = devm_clk_get(cpu_dev, "pll1_sys"); +- pll1_sw_clk = devm_clk_get(cpu_dev, "pll1_sw"); +- step_clk = devm_clk_get(cpu_dev, "step"); +- pll2_pfd2_396m_clk = devm_clk_get(cpu_dev, "pll2_pfd2_396m"); +- if (IS_ERR(arm_clk) || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk) || +- IS_ERR(step_clk) || IS_ERR(pll2_pfd2_396m_clk)) { +- dev_err(cpu_dev, "failed to get clocks\n"); +- ret = -ENOENT; +- goto put_node; +- } +- +- arm_reg = devm_regulator_get(cpu_dev, "arm"); +- pu_reg = devm_regulator_get(cpu_dev, "pu"); +- soc_reg = devm_regulator_get(cpu_dev, "soc"); +- if (IS_ERR(arm_reg) || IS_ERR(pu_reg) || IS_ERR(soc_reg)) { +- dev_err(cpu_dev, "failed to get regulators\n"); +- ret = -ENOENT; +- goto put_node; +- } +- +- /* +- * We expect an OPP table supplied by platform. +- * Just, incase the platform did not supply the OPP +- * table, it will try to get it. +- */ +- num = dev_pm_opp_get_opp_count(cpu_dev); +- if (num < 0) { +- ret = of_init_opp_table(cpu_dev); +- if (ret < 0) { +- dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); +- goto put_node; +- } +- +- num = dev_pm_opp_get_opp_count(cpu_dev); +- if (num < 0) { +- ret = num; +- dev_err(cpu_dev, "no OPP table is found: %d\n", ret); +- goto put_node; +- } +- } +- +- ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); +- if (ret) { +- dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); +- goto put_node; +- } +- +- /* Make imx6_soc_volt array's size same as arm opp number */ +- imx6_soc_volt = devm_kzalloc(cpu_dev, sizeof(*imx6_soc_volt) * num, GFP_KERNEL); +- if (imx6_soc_volt == NULL) { +- ret = -ENOMEM; +- goto free_freq_table; +- } +- +- prop = of_find_property(np, "fsl,soc-operating-points", NULL); +- if (!prop || !prop->value) +- goto soc_opp_out; +- +- /* +- * Each OPP is a set of tuples consisting of frequency and +- * voltage like . +- */ +- nr = prop->length / sizeof(u32); +- if (nr % 2 || (nr / 2) < num) +- goto soc_opp_out; +- +- for (j = 0; j < num; j++) { +- val = prop->value; +- for (i = 0; i < nr / 2; i++) { +- unsigned long freq = be32_to_cpup(val++); +- unsigned long volt = be32_to_cpup(val++); +- if (freq_table[j].frequency == freq) { +- imx6_soc_volt[soc_opp_count++] = volt; +- break; +- } +- } +- } +- +-soc_opp_out: +- /* use fixed soc opp volt if no valid soc opp info found in dtb */ +- if (soc_opp_count != num) { +- dev_warn(cpu_dev, "can NOT find valid fsl,soc-operating-points property in dtb, use default value!\n"); +- for (j = 0; j < num; j++) +- imx6_soc_volt[j] = PU_SOC_VOLTAGE_NORMAL; +- if (freq_table[num - 1].frequency * 1000 == FREQ_1P2_GHZ) +- imx6_soc_volt[num - 1] = PU_SOC_VOLTAGE_HIGH; +- } +- +- if (of_property_read_u32(np, "clock-latency", &transition_latency)) +- transition_latency = CPUFREQ_ETERNAL; +- +- /* +- * Calculate the ramp time for max voltage change in the +- * VDDSOC and VDDPU regulators. +- */ +- ret = regulator_set_voltage_time(soc_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); +- if (ret > 0) +- transition_latency += ret * 1000; +- ret = regulator_set_voltage_time(pu_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); +- if (ret > 0) +- transition_latency += ret * 1000; +- +- /* +- * OPP is maintained in order of increasing frequency, and +- * freq_table initialised from OPP is therefore sorted in the +- * same order. +- */ +- rcu_read_lock(); +- opp = dev_pm_opp_find_freq_exact(cpu_dev, +- freq_table[0].frequency * 1000, true); +- min_volt = dev_pm_opp_get_voltage(opp); +- opp = dev_pm_opp_find_freq_exact(cpu_dev, +- freq_table[--num].frequency * 1000, true); +- max_volt = dev_pm_opp_get_voltage(opp); +- rcu_read_unlock(); +- ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); +- if (ret > 0) +- transition_latency += ret * 1000; +- +- ret = cpufreq_register_driver(&imx6q_cpufreq_driver); +- if (ret) { +- dev_err(cpu_dev, "failed register driver: %d\n", ret); +- goto free_freq_table; +- } +- +- of_node_put(np); +- return 0; +- +-free_freq_table: +- dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); +-put_node: +- of_node_put(np); +- return ret; +-} +- +-static int imx6q_cpufreq_remove(struct platform_device *pdev) +-{ +- cpufreq_unregister_driver(&imx6q_cpufreq_driver); +- dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); +- +- return 0; +-} +- +-static struct platform_driver imx6q_cpufreq_platdrv = { +- .driver = { +- .name = "imx6q-cpufreq", +- .owner = THIS_MODULE, +- }, +- .probe = imx6q_cpufreq_probe, +- .remove = imx6q_cpufreq_remove, +-}; +-module_platform_driver(imx6q_cpufreq_platdrv); +- +-MODULE_AUTHOR("Shawn Guo "); +-MODULE_DESCRIPTION("Freescale i.MX6Q cpufreq driver"); +-MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/cpufreq/Kconfig linux-openelec/drivers/cpufreq/Kconfig +--- linux-3.14.36/drivers/cpufreq/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/cpufreq/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -91,6 +91,15 @@ + governor. If unsure have a look at the help section of the + driver. Fallback governor will be the performance governor. + ++config CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++ bool "interactive" ++ select CPU_FREQ_GOV_INTERACTIVE ++ help ++ Use the CPUFreq governor 'interactive' as default. This allows ++ you to get a full dynamic cpu frequency capable system by simply ++ loading your cpufreq low-level hardware driver, using the ++ 'interactive' governor for latency-sensitive workloads. ++ + config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE + bool "conservative" + select CPU_FREQ_GOV_CONSERVATIVE +@@ -157,6 +166,24 @@ + + For details, take a look at linux/Documentation/cpu-freq. + ++ If in doubt, say N. ++ ++config CPU_FREQ_GOV_INTERACTIVE ++ tristate "'interactive' cpufreq policy governor" ++ default n ++ help ++ 'interactive' - This driver adds a dynamic cpufreq policy governor ++ designed for latency-sensitive workloads. ++ ++ This governor attempts to reduce the latency of clock ++ increases so that the system is more responsive to ++ interactive workloads. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cpufreq_interactive. ++ ++ For details, take a look at linux/Documentation/cpu-freq. ++ + If in doubt, say N. + + config CPU_FREQ_GOV_CONSERVATIVE +diff -Nur linux-3.14.36/drivers/cpufreq/Kconfig.arm linux-openelec/drivers/cpufreq/Kconfig.arm +--- linux-3.14.36/drivers/cpufreq/Kconfig.arm 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/cpufreq/Kconfig.arm 2015-05-06 12:05:42.000000000 -0500 +@@ -4,7 +4,8 @@ + + config ARM_BIG_LITTLE_CPUFREQ + tristate "Generic ARM big LITTLE CPUfreq driver" +- depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK ++ depends on (BIG_LITTLE && ARM_CPU_TOPOLOGY) || (ARM64 && SMP) ++ depends on HAVE_CLK + select PM_OPP + help + This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. +@@ -95,7 +96,7 @@ + + If in doubt, say N. + +-config ARM_IMX6Q_CPUFREQ ++config ARM_IMX6_CPUFREQ + tristate "Freescale i.MX6 cpufreq support" + depends on ARCH_MXC + depends on REGULATOR_ANATOP +diff -Nur linux-3.14.36/drivers/cpufreq/Makefile linux-openelec/drivers/cpufreq/Makefile +--- linux-3.14.36/drivers/cpufreq/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/cpufreq/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -8,6 +8,7 @@ + obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o + obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o + obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o ++obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o + obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o + obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o + +@@ -55,7 +56,7 @@ + obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o + obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o + obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o +-obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o ++obj-$(CONFIG_ARM_IMX6_CPUFREQ) += imx6-cpufreq.o + obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o + obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o + obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +diff -Nur linux-3.14.36/drivers/crypto/caam/secvio.c linux-openelec/drivers/crypto/caam/secvio.c +--- linux-3.14.36/drivers/crypto/caam/secvio.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/crypto/caam/secvio.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,335 @@ ++ ++/* ++ * CAAM/SEC 4.x Security Violation Handler ++ * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved ++ */ ++ ++#include "compat.h" ++#include "intern.h" ++#include "secvio.h" ++#include "regs.h" ++ ++/* ++ * These names are associated with each violation handler. ++ * The source names were taken from MX6, and are based on recommendations ++ * for most common SoCs. ++ */ ++static const u8 *violation_src_name[] = { ++ "CAAM Security Violation", ++ "JTAG Alarm", ++ "Watchdog", ++ "(reserved)", ++ "External Boot", ++ "Tamper Detect", ++}; ++ ++/* Top-level security violation interrupt */ ++static irqreturn_t caam_secvio_interrupt(int irq, void *snvsdev) ++{ ++ struct device *dev = snvsdev; ++ struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev); ++ u32 irqstate; ++ ++ /* Check the HP secvio status register */ ++ irqstate = rd_reg32(&svpriv->svregs->hp.secvio_status) | ++ HP_SECVIOST_SECVIOMASK; ++ ++ if (!irqstate) ++ return IRQ_NONE; ++ ++ /* Mask out one or more causes for deferred service */ ++ clrbits32(&svpriv->svregs->hp.secvio_int_ctl, irqstate); ++ ++ /* Now ACK causes */ ++ setbits32(&svpriv->svregs->hp.secvio_status, irqstate); ++ ++ /* And run deferred service */ ++ preempt_disable(); ++ tasklet_schedule(&svpriv->irqtask[smp_processor_id()]); ++ preempt_enable(); ++ ++ return IRQ_HANDLED; ++} ++ ++/* Deferred service handler. Tasklet arg is simply the SNVS dev */ ++static void caam_secvio_dispatch(unsigned long indev) ++{ ++ struct device *dev = (struct device *)indev; ++ struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev); ++ unsigned long flags, cause; ++ int i; ++ ++ ++ /* ++ * Capture the interrupt cause, using masked interrupts as ++ * identification. This only works if all are enabled; if ++ * this changes in the future, a "cause queue" will have to ++ * be built ++ */ ++ cause = rd_reg32(&svpriv->svregs->hp.secvio_int_ctl) & ++ (HP_SECVIO_INTEN_SRC5 | HP_SECVIO_INTEN_SRC4 | ++ HP_SECVIO_INTEN_SRC3 | HP_SECVIO_INTEN_SRC2 | ++ HP_SECVIO_INTEN_SRC1 | HP_SECVIO_INTEN_SRC0); ++ ++ /* Look through causes, call each handler if exists */ ++ for (i = 0; i < MAX_SECVIO_SOURCES; i++) ++ if (cause & (1 << i)) { ++ spin_lock_irqsave(&svpriv->svlock, flags); ++ svpriv->intsrc[i].handler(dev, i, ++ svpriv->intsrc[i].ext); ++ spin_unlock_irqrestore(&svpriv->svlock, flags); ++ }; ++ ++ /* Re-enable now-serviced interrupts */ ++ setbits32(&svpriv->svregs->hp.secvio_int_ctl, cause); ++} ++ ++/* ++ * Default cause handler, used in lieu of an application-defined handler. ++ * All it does at this time is print a console message. It could force a halt. ++ */ ++static void caam_secvio_default(struct device *dev, u32 cause, void *ext) ++{ ++ struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev); ++ ++ dev_err(dev, "Unhandled Security Violation Interrupt %d = %s\n", ++ cause, svpriv->intsrc[cause].intname); ++} ++ ++/* ++ * Install an application-defined handler for a specified cause ++ * Arguments: ++ * - dev points to SNVS-owning device ++ * - cause interrupt source cause ++ * - handler application-defined handler, gets called with dev ++ * source cause, and locally-defined handler argument ++ * - cause_description points to a string to override the default cause ++ * name, this can be used as an alternate for error ++ * messages and such. If left NULL, the default ++ * description string is used. ++ * - ext pointer to any extra data needed by the handler. ++ */ ++int caam_secvio_install_handler(struct device *dev, enum secvio_cause cause, ++ void (*handler)(struct device *dev, u32 cause, ++ void *ext), ++ u8 *cause_description, void *ext) ++{ ++ unsigned long flags; ++ struct caam_drv_private_secvio *svpriv; ++ ++ svpriv = dev_get_drvdata(dev); ++ ++ if ((handler == NULL) || (cause > SECVIO_CAUSE_SOURCE_5)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&svpriv->svlock, flags); ++ svpriv->intsrc[cause].handler = handler; ++ if (cause_description != NULL) ++ svpriv->intsrc[cause].intname = cause_description; ++ if (ext != NULL) ++ svpriv->intsrc[cause].ext = ext; ++ spin_unlock_irqrestore(&svpriv->svlock, flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL(caam_secvio_install_handler); ++ ++/* ++ * Remove an application-defined handler for a specified cause (and, by ++ * implication, restore the "default". ++ * Arguments: ++ * - dev points to SNVS-owning device ++ * - cause interrupt source cause ++ */ ++int caam_secvio_remove_handler(struct device *dev, enum secvio_cause cause) ++{ ++ unsigned long flags; ++ struct caam_drv_private_secvio *svpriv; ++ ++ svpriv = dev_get_drvdata(dev); ++ ++ if (cause > SECVIO_CAUSE_SOURCE_5) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&svpriv->svlock, flags); ++ svpriv->intsrc[cause].intname = violation_src_name[cause]; ++ svpriv->intsrc[cause].handler = caam_secvio_default; ++ svpriv->intsrc[cause].ext = NULL; ++ spin_unlock_irqrestore(&svpriv->svlock, flags); ++ return 0; ++} ++EXPORT_SYMBOL(caam_secvio_remove_handler); ++ ++int caam_secvio_startup(struct platform_device *pdev) ++{ ++ struct device *ctrldev, *svdev; ++ struct caam_drv_private *ctrlpriv; ++ struct caam_drv_private_secvio *svpriv; ++ struct platform_device *svpdev; ++ struct device_node *np; ++ const void *prop; ++ int i, error, secvio_inten_src; ++ ++ ctrldev = &pdev->dev; ++ ctrlpriv = dev_get_drvdata(ctrldev); ++ /* ++ * Set up the private block for secure memory ++ * Only one instance is possible ++ */ ++ svpriv = kzalloc(sizeof(struct caam_drv_private_secvio), GFP_KERNEL); ++ if (svpriv == NULL) { ++ dev_err(ctrldev, "can't alloc private mem for secvio\n"); ++ return -ENOMEM; ++ } ++ svpriv->parentdev = ctrldev; ++ ++ /* Create the security violation dev */ ++#ifdef CONFIG_OF ++ ++ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-secvio"); ++ if (!np) ++ return -ENODEV; ++ ++ ctrlpriv->secvio_irq = of_irq_to_resource(np, 0, NULL); ++ ++ prop = of_get_property(np, "secvio_src", NULL); ++ if (prop) ++ secvio_inten_src = of_read_ulong(prop, 1); ++ else ++ secvio_inten_src = HP_SECVIO_INTEN_ALL; ++ ++ printk(KERN_ERR "secvio_inten_src = %x\n", secvio_inten_src); ++ ++ svpdev = of_platform_device_create(np, NULL, ctrldev); ++ if (!svpdev) ++ return -ENODEV; ++ ++#else ++ svpdev = platform_device_register_data(ctrldev, "caam_secvio", 0, ++ svpriv, ++ sizeof(struct caam_drv_private_secvio)); ++ ++ secvio_inten_src = HP_SECVIO_INTEN_ALL; ++#endif ++ if (svpdev == NULL) { ++ kfree(svpriv); ++ return -EINVAL; ++ } ++ svdev = &svpdev->dev; ++ dev_set_drvdata(svdev, svpriv); ++ ctrlpriv->secviodev = svdev; ++ svpriv->svregs = ctrlpriv->snvs; ++ ++ /* ++ * Now we have all the dev data set up. Init interrupt ++ * source descriptions ++ */ ++ for (i = 0; i < MAX_SECVIO_SOURCES; i++) { ++ svpriv->intsrc[i].intname = violation_src_name[i]; ++ svpriv->intsrc[i].handler = caam_secvio_default; ++ } ++ ++ /* Connect main handler */ ++ for_each_possible_cpu(i) ++ tasklet_init(&svpriv->irqtask[i], caam_secvio_dispatch, ++ (unsigned long)svdev); ++ ++ error = request_irq(ctrlpriv->secvio_irq, caam_secvio_interrupt, ++ IRQF_SHARED, "caam_secvio", svdev); ++ if (error) { ++ dev_err(svdev, "can't connect secvio interrupt\n"); ++ irq_dispose_mapping(ctrlpriv->secvio_irq); ++ ctrlpriv->secvio_irq = 0; ++ return -EINVAL; ++ } ++ ++ /* Enable all sources */ ++ wr_reg32(&svpriv->svregs->hp.secvio_int_ctl, secvio_inten_src); ++ ++ dev_info(svdev, "security violation service handlers armed\n"); ++ ++ return 0; ++} ++ ++void caam_secvio_shutdown(struct platform_device *pdev) ++{ ++ struct device *ctrldev, *svdev; ++ struct caam_drv_private *priv; ++ struct caam_drv_private_secvio *svpriv; ++ int i; ++ ++ ctrldev = &pdev->dev; ++ priv = dev_get_drvdata(ctrldev); ++ svdev = priv->secviodev; ++ svpriv = dev_get_drvdata(svdev); ++ ++ /* Shut off all sources */ ++ ++ wr_reg32(&svpriv->svregs->hp.secvio_int_ctl, 0); ++ ++ /* Remove tasklets and release interrupt */ ++ for_each_possible_cpu(i) ++ tasklet_kill(&svpriv->irqtask[i]); ++ ++ free_irq(priv->secvio_irq, svdev); ++ ++ kfree(svpriv); ++} ++ ++ ++#ifdef CONFIG_OF ++static void __exit caam_secvio_exit(void) ++{ ++ struct device_node *dev_node; ++ struct platform_device *pdev; ++ ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); ++ if (!dev_node) { ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); ++ if (!dev_node) ++ return; ++ } ++ ++ pdev = of_find_device_by_node(dev_node); ++ if (!pdev) ++ return; ++ ++ of_node_get(dev_node); ++ ++ caam_secvio_shutdown(pdev); ++ ++} ++ ++static int __init caam_secvio_init(void) ++{ ++ struct device_node *dev_node; ++ struct platform_device *pdev; ++ ++ /* ++ * Do of_find_compatible_node() then of_find_device_by_node() ++ * once a functional device tree is available ++ */ ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); ++ if (!dev_node) { ++ dev_node = of_find_compatible_node(NULL, NULL, ++ "arm,imx6-caam-secvio"); ++ if (!dev_node) ++ return -ENODEV; ++ } ++ ++ pdev = of_find_device_by_node(dev_node); ++ if (!pdev) ++ return -ENODEV; ++ ++ of_node_put(dev_node); ++ ++ return caam_secvio_startup(pdev); ++} ++ ++module_init(caam_secvio_init); ++module_exit(caam_secvio_exit); ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_DESCRIPTION("FSL CAAM/SNVS Security Violation Handler"); ++MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD"); ++#endif +diff -Nur linux-3.14.36/drivers/crypto/caam/secvio.h linux-openelec/drivers/crypto/caam/secvio.h +--- linux-3.14.36/drivers/crypto/caam/secvio.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/crypto/caam/secvio.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,64 @@ ++ ++/* ++ * CAAM Security Violation Handler ++ * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved ++ */ ++ ++#ifndef SECVIO_H ++#define SECVIO_H ++ ++#include "snvsregs.h" ++ ++ ++/* ++ * Defines the published interfaces to install/remove application-specified ++ * handlers for catching violations ++ */ ++ ++#define MAX_SECVIO_SOURCES 6 ++ ++/* these are the untranslated causes */ ++enum secvio_cause { ++ SECVIO_CAUSE_SOURCE_0, ++ SECVIO_CAUSE_SOURCE_1, ++ SECVIO_CAUSE_SOURCE_2, ++ SECVIO_CAUSE_SOURCE_3, ++ SECVIO_CAUSE_SOURCE_4, ++ SECVIO_CAUSE_SOURCE_5 ++}; ++ ++/* These are common "recommended" cause definitions for most devices */ ++#define SECVIO_CAUSE_CAAM_VIOLATION SECVIO_CAUSE_SOURCE_0 ++#define SECVIO_CAUSE JTAG_ALARM SECVIO_CAUSE_SOURCE_1 ++#define SECVIO_CAUSE_WATCHDOG SECVIO_CAUSE_SOURCE_2 ++#define SECVIO_CAUSE_EXTERNAL_BOOT SECVIO_CAUSE_SOURCE_4 ++#define SECVIO_CAUSE_TAMPER_DETECT SECVIO_CAUSE_SOURCE_5 ++ ++int caam_secvio_install_handler(struct device *dev, enum secvio_cause cause, ++ void (*handler)(struct device *dev, u32 cause, ++ void *ext), ++ u8 *cause_description, void *ext); ++int caam_secvio_remove_handler(struct device *dev, enum secvio_cause cause); ++ ++/* ++ * Private data definitions for the secvio "driver" ++ */ ++ ++struct secvio_int_src { ++ const u8 *intname; /* Points to a descriptive name for source */ ++ void *ext; /* Extended data to pass to the handler */ ++ void (*handler)(struct device *dev, u32 cause, void *ext); ++}; ++ ++struct caam_drv_private_secvio { ++ struct device *parentdev; /* points back to the controller */ ++ spinlock_t svlock ____cacheline_aligned; ++ struct tasklet_struct irqtask[NR_CPUS]; ++ struct snvs_full __iomem *svregs; /* both HP and LP domains */ ++ ++ /* Registered handlers for each violation */ ++ struct secvio_int_src intsrc[MAX_SECVIO_SOURCES]; ++ ++}; ++ ++#endif /* SECVIO_H */ +diff -Nur linux-3.14.36/drivers/crypto/caam/sm.h linux-openelec/drivers/crypto/caam/sm.h +--- linux-3.14.36/drivers/crypto/caam/sm.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/crypto/caam/sm.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,88 @@ ++ ++/* ++ * CAAM Secure Memory/Keywrap API Definitions ++ * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. ++ */ ++ ++#ifndef SM_H ++#define SM_H ++ ++ ++/* Storage access permissions */ ++#define SM_PERM_READ 0x01 ++#define SM_PERM_WRITE 0x02 ++#define SM_PERM_BLOB 0x03 ++ ++ ++/* Keystore maintenance functions */ ++void sm_init_keystore(struct device *dev); ++u32 sm_detect_keystore_units(struct device *dev); ++int sm_establish_keystore(struct device *dev, u32 unit); ++void sm_release_keystore(struct device *dev, u32 unit); ++void caam_sm_shutdown(struct platform_device *pdev); ++int caam_sm_example_init(struct platform_device *pdev); ++ ++/* Keystore accessor functions */ ++extern int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size, ++ u32 *slot); ++extern int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot); ++extern int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot, ++ const u8 *key_data, u32 key_length); ++extern int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot, ++ u32 key_length, u8 *key_data); ++extern int sm_keystore_slot_encapsulate(struct device *dev, u32 unit, ++ u32 inslot, u32 outslot, u16 secretlen, ++ u8 *keymod, u16 keymodlen); ++extern int sm_keystore_slot_decapsulate(struct device *dev, u32 unit, ++ u32 inslot, u32 outslot, u16 secretlen, ++ u8 *keymod, u16 keymodlen); ++ ++/* Data structure to hold per-slot information */ ++struct keystore_data_slot_info { ++ u8 allocated; /* Track slot assignments */ ++ u32 key_length; /* Size of the key */ ++}; ++ ++/* Data structure to hold keystore information */ ++struct keystore_data { ++ void *base_address; /* Base of the Secure Partition */ ++ u32 slot_count; /* Number of slots in the keystore */ ++ struct keystore_data_slot_info *slot; /* Per-slot information */ ++}; ++ ++/* store the detected attributes of a secure memory page */ ++struct sm_page_descriptor { ++ u16 phys_pagenum; /* may be discontiguous */ ++ u16 own_part; /* Owning partition */ ++ void *pg_base; /* Calculated virtual address */ ++ struct keystore_data *ksdata; ++}; ++ ++struct caam_drv_private_sm { ++ struct device *parentdev; /* this ends up as the controller */ ++ struct device *smringdev; /* ring that owns this instance */ ++ spinlock_t kslock ____cacheline_aligned; ++ ++ /* Default parameters for geometry */ ++ u32 max_pages; /* maximum pages this instance can support */ ++ u32 top_partition; /* highest partition number in this instance */ ++ u32 top_page; /* highest page number in this instance */ ++ u32 page_size; /* page size */ ++ u32 slot_size; /* selected size of each storage block */ ++ ++ /* Partition/Page Allocation Map */ ++ u32 localpages; /* Number of pages we can access */ ++ struct sm_page_descriptor *pagedesc; /* Allocated per-page */ ++ ++ /* Installed handlers for keystore access */ ++ int (*data_init)(struct device *dev, u32 unit); ++ void (*data_cleanup)(struct device *dev, u32 unit); ++ int (*slot_alloc)(struct device *dev, u32 unit, u32 size, u32 *slot); ++ int (*slot_dealloc)(struct device *dev, u32 unit, u32 slot); ++ void *(*slot_get_address)(struct device *dev, u32 unit, u32 handle); ++ u32 (*slot_get_base)(struct device *dev, u32 unit, u32 handle); ++ u32 (*slot_get_offset)(struct device *dev, u32 unit, u32 handle); ++ u32 (*slot_get_slot_size)(struct device *dev, u32 unit, u32 handle); ++}; ++ ++#endif /* SM_H */ +diff -Nur linux-3.14.36/drivers/crypto/caam/sm_store.c linux-openelec/drivers/crypto/caam/sm_store.c +--- linux-3.14.36/drivers/crypto/caam/sm_store.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/crypto/caam/sm_store.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,896 @@ ++ ++/* ++ * CAAM Secure Memory Storage Interface ++ * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. ++ * ++ * Loosely based on the SHW Keystore API for SCC/SCC2 ++ * Experimental implementation and NOT intended for upstream use. Expect ++ * this interface to be amended significantly in the future once it becomes ++ * integrated into live applications. ++ * ++ * Known issues: ++ * ++ * - Executes one instance of an secure memory "driver". This is tied to the ++ * fact that job rings can't run as standalone instances in the present ++ * configuration. ++ * ++ * - It does not expose a userspace interface. The value of a userspace ++ * interface for access to secrets is a point for further architectural ++ * discussion. ++ * ++ * - Partition/permission management is not part of this interface. It ++ * depends on some level of "knowledge" agreed upon between bootloader, ++ * provisioning applications, and OS-hosted software (which uses this ++ * driver). ++ * ++ * - No means of identifying the location or purpose of secrets managed by ++ * this interface exists; "slot location" and format of a given secret ++ * needs to be agreed upon between bootloader, provisioner, and OS-hosted ++ * application. ++ */ ++ ++#include "compat.h" ++#include "regs.h" ++#include "jr.h" ++#include "desc.h" ++#include "intern.h" ++#include "error.h" ++#include "sm.h" ++ ++#ifdef SM_DEBUG_CONT ++void sm_show_page(struct device *dev, struct sm_page_descriptor *pgdesc) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ u32 i, *smdata; ++ ++ dev_info(dev, "physical page %d content at 0x%08x\n", ++ pgdesc->phys_pagenum, pgdesc->pg_base); ++ smdata = pgdesc->pg_base; ++ for (i = 0; i < (smpriv->page_size / sizeof(u32)); i += 4) ++ dev_info(dev, "[0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ (u32)&smdata[i], smdata[i], smdata[i+1], smdata[i+2], ++ smdata[i+3]); ++} ++#endif ++ ++/* ++ * Construct a secure memory blob encapsulation job descriptor ++ * ++ * - desc pointer to hold new (to be allocated) pointer to the generated ++ * descriptor for later use. Calling thread can kfree the ++ * descriptor after execution. ++ * - keymod Physical pointer to key modifier (contiguous piece). ++ * - keymodsz Size of key modifier in bytes (should normally be 8). ++ * - secretbuf Physical pointer (within an accessible secure memory page) ++ * of the secret to be encapsulated. ++ * - outbuf Physical pointer (within an accessible secure memory page) ++ * of the encapsulated output. This will be larger than the ++ * input secret because of the added encapsulation data. ++ * - secretsz Size of input secret, in bytes. ++ * - auth If nonzero, use AES-CCM for encapsulation, else use ECB ++ * ++ * Note: this uses 32-bit pointers at present ++ */ ++#define INITIAL_DESCSZ 16 /* size of tmp buffer for descriptor const. */ ++static int blob_encap_desc(u32 **desc, dma_addr_t keymod, u16 keymodsz, ++ dma_addr_t secretbuf, dma_addr_t outbuf, ++ u16 secretsz, bool auth) ++{ ++ u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; ++ u16 dsize, idx; ++ ++ memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32)); ++ idx = 1; ++ ++ /* Load key modifier */ ++ tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY | ++ ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) | ++ (keymodsz & LDST_LEN_MASK); ++ ++ tmpdesc[idx++] = (u32)keymod; ++ ++ /* Encapsulate to secure memory */ ++ tmpdesc[idx++] = CMD_SEQ_IN_PTR | secretsz; ++ tmpdesc[idx++] = (u32)secretbuf; ++ ++ /* Add space for BKEK and MAC tag */ ++ tmpdesc[idx++] = CMD_SEQ_IN_PTR | (secretsz + (32 + 16)); ++ ++ tmpdesc[idx++] = (u32)outbuf; ++ tmpdesc[idx] = CMD_OPERATION | OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB | ++ OP_PCL_BLOB_PTXT_SECMEM; ++ if (auth) ++ tmpdesc[idx] |= OP_PCL_BLOB_EKT; ++ ++ idx++; ++ tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK); ++ dsize = idx * sizeof(u32); ++ ++ tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA); ++ if (tdesc == NULL) ++ return 0; ++ ++ memcpy(tdesc, tmpdesc, dsize); ++ *desc = tdesc; ++ return dsize; ++} ++ ++/* ++ * Construct a secure memory blob decapsulation job descriptor ++ * ++ * - desc pointer to hold new (to be allocated) pointer to the generated ++ * descriptor for later use. Calling thread can kfree the ++ * descriptor after execution. ++ * - keymod Physical pointer to key modifier (contiguous piece). ++ * - keymodsz Size of key modifier in bytes (should normally be 16). ++ * - blobbuf Physical pointer (within an accessible secure memory page) ++ * of the blob to be decapsulated. ++ * - outbuf Physical pointer (within an accessible secure memory page) ++ * of the decapsulated output. ++ * - secretsz Size of input blob, in bytes. ++ * - auth If nonzero, assume AES-CCM for decapsulation, else use ECB ++ * ++ * Note: this uses 32-bit pointers at present ++ */ ++static int blob_decap_desc(u32 **desc, dma_addr_t keymod, u16 keymodsz, ++ dma_addr_t blobbuf, dma_addr_t outbuf, ++ u16 blobsz, bool auth) ++{ ++ u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; ++ u16 dsize, idx; ++ ++ memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32)); ++ idx = 1; ++ ++ /* Load key modifier */ ++ tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY | ++ ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) | ++ (keymodsz & LDST_LEN_MASK); ++ ++ tmpdesc[idx++] = (u32)keymod; ++ ++ /* Compensate BKEK + MAC tag */ ++ tmpdesc[idx++] = CMD_SEQ_IN_PTR | (blobsz + 32 + 16); ++ ++ tmpdesc[idx++] = (u32)blobbuf; ++ tmpdesc[idx++] = CMD_SEQ_OUT_PTR | blobsz; ++ tmpdesc[idx++] = (u32)outbuf; ++ ++ /* Decapsulate from secure memory partition to black blob */ ++ tmpdesc[idx] = CMD_OPERATION | OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB | ++ OP_PCL_BLOB_PTXT_SECMEM | OP_PCL_BLOB_BLACK; ++ if (auth) ++ tmpdesc[idx] |= OP_PCL_BLOB_EKT; ++ ++ idx++; ++ tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK); ++ dsize = idx * sizeof(u32); ++ ++ tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA); ++ if (tdesc == NULL) ++ return 0; ++ ++ memcpy(tdesc, tmpdesc, dsize); ++ *desc = tdesc; ++ return dsize; ++} ++ ++/* ++ * Pseudo-synchronous ring access functions for carrying out key ++ * encapsulation and decapsulation ++ */ ++ ++struct sm_key_job_result { ++ int error; ++ struct completion completion; ++}; ++ ++void sm_key_job_done(struct device *dev, u32 *desc, u32 err, void *context) ++{ ++ struct sm_key_job_result *res = context; ++ ++ res->error = err; /* save off the error for postprocessing */ ++ complete(&res->completion); /* mark us complete */ ++} ++ ++static int sm_key_job(struct device *ksdev, u32 *jobdesc) ++{ ++ struct sm_key_job_result testres; ++ struct caam_drv_private_sm *kspriv; ++ int rtn = 0; ++ ++ kspriv = dev_get_drvdata(ksdev); ++ ++ init_completion(&testres.completion); ++ ++ rtn = caam_jr_enqueue(kspriv->smringdev, jobdesc, sm_key_job_done, ++ &testres); ++ if (!rtn) { ++ wait_for_completion_interruptible(&testres.completion); ++ rtn = testres.error; ++ } ++ return rtn; ++} ++ ++/* ++ * Following section establishes the default methods for keystore access ++ * They are NOT intended for use external to this module ++ * ++ * In the present version, these are the only means for the higher-level ++ * interface to deal with the mechanics of accessing the phyiscal keystore ++ */ ++ ++ ++int slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; ++ u32 i; ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_alloc(): requesting slot for %d bytes\n", size); ++#endif ++ ++ if (size > smpriv->slot_size) ++ return -EKEYREJECTED; ++ ++ for (i = 0; i < ksdata->slot_count; i++) { ++ if (ksdata->slot[i].allocated == 0) { ++ ksdata->slot[i].allocated = 1; ++ (*slot) = i; ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_alloc(): new slot %d allocated\n", ++ *slot); ++#endif ++ return 0; ++ } ++ } ++ ++ return -ENOSPC; ++} ++EXPORT_SYMBOL(slot_alloc); ++ ++int slot_dealloc(struct device *dev, u32 unit, u32 slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; ++ u8 __iomem *slotdata; ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_dealloc(): releasing slot %d\n", slot); ++#endif ++ if (slot >= ksdata->slot_count) ++ return -EINVAL; ++ slotdata = ksdata->base_address + slot * smpriv->slot_size; ++ ++ if (ksdata->slot[slot].allocated == 1) { ++ /* Forcibly overwrite the data from the keystore */ ++ memset(ksdata->base_address + slot * smpriv->slot_size, 0, ++ smpriv->slot_size); ++ ++ ksdata->slot[slot].allocated = 0; ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_dealloc(): slot %d released\n", slot); ++#endif ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++EXPORT_SYMBOL(slot_dealloc); ++ ++void *slot_get_address(struct device *dev, u32 unit, u32 slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; ++ ++ if (slot >= ksdata->slot_count) ++ return NULL; ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_get_address(): slot %d is 0x%08x\n", slot, ++ (u32)ksdata->base_address + slot * smpriv->slot_size); ++#endif ++ ++ return ksdata->base_address + slot * smpriv->slot_size; ++} ++ ++u32 slot_get_base(struct device *dev, u32 unit, u32 slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; ++ ++ /* ++ * There could potentially be more than one secure partition object ++ * associated with this keystore. For now, there is just one. ++ */ ++ ++ (void)slot; ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_get_base(): slot %d = 0x%08x\n", ++ slot, (u32)ksdata->base_address); ++#endif ++ ++ return (u32)(ksdata->base_address); ++} ++ ++u32 slot_get_offset(struct device *dev, u32 unit, u32 slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; ++ ++ if (slot >= ksdata->slot_count) ++ return -EINVAL; ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_get_offset(): slot %d = %d\n", slot, ++ slot * smpriv->slot_size); ++#endif ++ ++ return slot * smpriv->slot_size; ++} ++ ++u32 slot_get_slot_size(struct device *dev, u32 unit, u32 slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "slot_get_slot_size(): slot %d = %d\n", slot, ++ smpriv->slot_size); ++#endif ++ /* All slots are the same size in the default implementation */ ++ return smpriv->slot_size; ++} ++ ++ ++ ++int kso_init_data(struct device *dev, u32 unit) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ int retval = -EINVAL; ++ struct keystore_data *keystore_data = NULL; ++ u32 slot_count; ++ u32 keystore_data_size; ++ ++ /* ++ * Calculate the required size of the keystore data structure, based ++ * on the number of keys that can fit in the partition. ++ */ ++ slot_count = smpriv->page_size / smpriv->slot_size; ++#ifdef SM_DEBUG ++ dev_info(dev, "kso_init_data: %d slots initializing\n", slot_count); ++#endif ++ ++ keystore_data_size = sizeof(struct keystore_data) + ++ slot_count * ++ sizeof(struct keystore_data_slot_info); ++ ++ keystore_data = kzalloc(keystore_data_size, GFP_KERNEL); ++ ++ if (keystore_data == NULL) { ++ retval = -ENOSPC; ++ goto out; ++ } ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "kso_init_data: keystore data size = %d\n", ++ keystore_data_size); ++#endif ++ ++ /* ++ * Place the slot information structure directly after the keystore data ++ * structure. ++ */ ++ keystore_data->slot = (struct keystore_data_slot_info *) ++ (keystore_data + 1); ++ keystore_data->slot_count = slot_count; ++ ++ smpriv->pagedesc[unit].ksdata = keystore_data; ++ smpriv->pagedesc[unit].ksdata->base_address = ++ smpriv->pagedesc[unit].pg_base; ++ ++ retval = 0; ++ ++out: ++ if (retval != 0) ++ if (keystore_data != NULL) ++ kfree(keystore_data); ++ ++ ++ return retval; ++} ++ ++void kso_cleanup_data(struct device *dev, u32 unit) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ struct keystore_data *keystore_data = NULL; ++ ++ if (smpriv->pagedesc[unit].ksdata != NULL) ++ keystore_data = smpriv->pagedesc[unit].ksdata; ++ ++ /* Release the allocated keystore management data */ ++ kfree(smpriv->pagedesc[unit].ksdata); ++ ++ return; ++} ++ ++ ++ ++/* ++ * Keystore management section ++ */ ++ ++void sm_init_keystore(struct device *dev) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ ++ smpriv->data_init = kso_init_data; ++ smpriv->data_cleanup = kso_cleanup_data; ++ smpriv->slot_alloc = slot_alloc; ++ smpriv->slot_dealloc = slot_dealloc; ++ smpriv->slot_get_address = slot_get_address; ++ smpriv->slot_get_base = slot_get_base; ++ smpriv->slot_get_offset = slot_get_offset; ++ smpriv->slot_get_slot_size = slot_get_slot_size; ++#ifdef SM_DEBUG ++ dev_info(dev, "sm_init_keystore(): handlers installed\n"); ++#endif ++} ++EXPORT_SYMBOL(sm_init_keystore); ++ ++/* Return available pages/units */ ++u32 sm_detect_keystore_units(struct device *dev) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ ++ return smpriv->localpages; ++} ++EXPORT_SYMBOL(sm_detect_keystore_units); ++ ++/* ++ * Do any keystore specific initializations ++ */ ++int sm_establish_keystore(struct device *dev, u32 unit) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "sm_establish_keystore(): unit %d initializing\n", unit); ++#endif ++ ++ if (smpriv->data_init == NULL) ++ return -EINVAL; ++ ++ /* Call the data_init function for any user setup */ ++ return smpriv->data_init(dev, unit); ++} ++EXPORT_SYMBOL(sm_establish_keystore); ++ ++void sm_release_keystore(struct device *dev, u32 unit) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ ++#ifdef SM_DEBUG ++ dev_info(dev, "sm_establish_keystore(): unit %d releasing\n", unit); ++#endif ++ if ((smpriv != NULL) && (smpriv->data_cleanup != NULL)) ++ smpriv->data_cleanup(dev, unit); ++ ++ return; ++} ++EXPORT_SYMBOL(sm_release_keystore); ++ ++/* ++ * Subsequent interfacce (sm_keystore_*) forms the accessor interfacce to ++ * the keystore ++ */ ++int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ int retval = -EINVAL; ++ ++ spin_lock(&smpriv->kslock); ++ ++ if ((smpriv->slot_alloc == NULL) || ++ (smpriv->pagedesc[unit].ksdata == NULL)) ++ goto out; ++ ++ retval = smpriv->slot_alloc(dev, unit, size, slot); ++ ++out: ++ spin_unlock(&smpriv->kslock); ++ return retval; ++} ++EXPORT_SYMBOL(sm_keystore_slot_alloc); ++ ++int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ int retval = -EINVAL; ++ ++ spin_lock(&smpriv->kslock); ++ ++ if ((smpriv->slot_alloc == NULL) || ++ (smpriv->pagedesc[unit].ksdata == NULL)) ++ goto out; ++ ++ retval = smpriv->slot_dealloc(dev, unit, slot); ++out: ++ spin_unlock(&smpriv->kslock); ++ return retval; ++} ++EXPORT_SYMBOL(sm_keystore_slot_dealloc); ++ ++int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot, ++ const u8 *key_data, u32 key_length) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ int retval = -EINVAL; ++ u32 slot_size; ++ u32 i; ++ u8 __iomem *slot_location; ++ ++ spin_lock(&smpriv->kslock); ++ ++ slot_size = smpriv->slot_get_slot_size(dev, unit, slot); ++ ++ if (key_length > slot_size) { ++ retval = -EFBIG; ++ goto out; ++ } ++ ++ slot_location = smpriv->slot_get_address(dev, unit, slot); ++ ++ for (i = 0; i < key_length; i++) ++ slot_location[i] = key_data[i]; ++ ++ retval = 0; ++ ++out: ++ spin_unlock(&smpriv->kslock); ++ return retval; ++} ++EXPORT_SYMBOL(sm_keystore_slot_load); ++ ++int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot, ++ u32 key_length, u8 *key_data) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ int retval = -EINVAL; ++ u8 __iomem *slot_addr; ++ u32 slot_size; ++ ++ spin_lock(&smpriv->kslock); ++ ++ slot_addr = smpriv->slot_get_address(dev, unit, slot); ++ slot_size = smpriv->slot_get_slot_size(dev, unit, slot); ++ ++ if (key_length > slot_size) { ++ retval = -EKEYREJECTED; ++ goto out; ++ } ++ ++ memcpy(key_data, slot_addr, key_length); ++ retval = 0; ++ ++out: ++ spin_unlock(&smpriv->kslock); ++ return retval; ++} ++EXPORT_SYMBOL(sm_keystore_slot_read); ++ ++int sm_keystore_slot_encapsulate(struct device *dev, u32 unit, u32 inslot, ++ u32 outslot, u16 secretlen, u8 *keymod, ++ u16 keymodlen) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ int retval = 0; ++ u32 slot_length, dsize, jstat; ++ u32 __iomem *encapdesc = NULL; ++ u8 __iomem *lkeymod, *inpslotaddr, *outslotaddr; ++ dma_addr_t keymod_dma; ++ ++ /* Ensure that the full blob will fit in the key slot */ ++ slot_length = smpriv->slot_get_slot_size(dev, unit, outslot); ++ if ((secretlen + 48) > slot_length) ++ goto out; ++ ++ /* Get the base addresses of both keystore slots */ ++ inpslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, inslot); ++ outslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, outslot); ++ ++ /* Build the key modifier */ ++ lkeymod = kmalloc(keymodlen, GFP_KERNEL | GFP_DMA); ++ memcpy(lkeymod, keymod, keymodlen); ++ keymod_dma = dma_map_single(dev, lkeymod, keymodlen, DMA_TO_DEVICE); ++ dma_sync_single_for_device(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); ++ ++ /* Build the encapsulation job descriptor */ ++ dsize = blob_encap_desc(&encapdesc, keymod_dma, keymodlen, ++ __pa(inpslotaddr), __pa(outslotaddr), ++ secretlen, 0); ++ if (!dsize) { ++ dev_err(dev, "can't alloc an encap descriptor\n"); ++ retval = -ENOMEM; ++ goto out; ++ } ++ jstat = sm_key_job(dev, encapdesc); ++ ++ dma_unmap_single(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); ++ kfree(encapdesc); ++ ++out: ++ return retval; ++ ++} ++EXPORT_SYMBOL(sm_keystore_slot_encapsulate); ++ ++int sm_keystore_slot_decapsulate(struct device *dev, u32 unit, u32 inslot, ++ u32 outslot, u16 secretlen, u8 *keymod, ++ u16 keymodlen) ++{ ++ struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); ++ int retval = 0; ++ u32 slot_length, dsize, jstat; ++ u32 __iomem *decapdesc = NULL; ++ u8 __iomem *lkeymod, *inpslotaddr, *outslotaddr; ++ dma_addr_t keymod_dma; ++ ++ /* Ensure that the decap data will fit in the key slot */ ++ slot_length = smpriv->slot_get_slot_size(dev, unit, outslot); ++ if (secretlen > slot_length) ++ goto out; ++ ++ /* Get the base addresses of both keystore slots */ ++ inpslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, inslot); ++ outslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, outslot); ++ ++ /* Build the key modifier */ ++ lkeymod = kmalloc(keymodlen, GFP_KERNEL | GFP_DMA); ++ memcpy(lkeymod, keymod, keymodlen); ++ keymod_dma = dma_map_single(dev, lkeymod, keymodlen, DMA_TO_DEVICE); ++ dma_sync_single_for_device(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); ++ ++ /* Build the decapsulation job descriptor */ ++ dsize = blob_decap_desc(&decapdesc, keymod_dma, keymodlen, ++ __pa(inpslotaddr), __pa(outslotaddr), ++ secretlen, 0); ++ if (!dsize) { ++ dev_err(dev, "can't alloc a decap descriptor\n"); ++ retval = -ENOMEM; ++ goto out; ++ } ++ jstat = sm_key_job(dev, decapdesc); ++ ++ dma_unmap_single(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); ++ kfree(decapdesc); ++ ++out: ++ return retval; ++ ++} ++EXPORT_SYMBOL(sm_keystore_slot_decapsulate); ++ ++ ++/* ++ * Initialization/shutdown subsystem ++ * Assumes statically-invoked startup/shutdown from the controller driver ++ * for the present time, to be reworked when a device tree becomes ++ * available. This code will not modularize in present form. ++ * ++ * Also, simply uses ring 0 for execution at the present ++ */ ++ ++int caam_sm_startup(struct platform_device *pdev) ++{ ++ struct device *ctrldev, *smdev; ++ struct caam_drv_private *ctrlpriv; ++ struct caam_drv_private_sm *smpriv; ++ struct caam_drv_private_jr *jrpriv; /* need this for reg page */ ++ struct platform_device *sm_pdev; ++ struct sm_page_descriptor *lpagedesc; ++ u32 page, pgstat, lpagect, detectedpage; ++ ++ struct device_node *np; ++ ctrldev = &pdev->dev; ++ ctrlpriv = dev_get_drvdata(ctrldev); ++ ++ /* ++ * Set up the private block for secure memory ++ * Only one instance is possible ++ */ ++ smpriv = kzalloc(sizeof(struct caam_drv_private_sm), GFP_KERNEL); ++ if (smpriv == NULL) { ++ dev_err(ctrldev, "can't alloc private mem for secure memory\n"); ++ return -ENOMEM; ++ } ++ smpriv->parentdev = ctrldev; /* copy of parent dev is handy */ ++ ++ /* Create the dev */ ++#ifdef CONFIG_OF ++ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-sm"); ++ sm_pdev = of_platform_device_create(np, "caam_sm", ctrldev); ++#else ++ sm_pdev = platform_device_register_data(ctrldev, "caam_sm", 0, ++ smpriv, ++ sizeof(struct caam_drv_private_sm)); ++#endif ++ if (sm_pdev == NULL) { ++ kfree(smpriv); ++ return -EINVAL; ++ } ++ smdev = &sm_pdev->dev; ++ dev_set_drvdata(smdev, smpriv); ++ ctrlpriv->smdev = smdev; ++ ++ /* ++ * Collect configuration limit data for reference ++ * This batch comes from the partition data/vid registers in perfmon ++ */ ++ smpriv->max_pages = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart) ++ & SMPART_MAX_NUMPG_MASK) >> ++ SMPART_MAX_NUMPG_SHIFT) + 1; ++ smpriv->top_partition = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart) ++ & SMPART_MAX_PNUM_MASK) >> ++ SMPART_MAX_PNUM_SHIFT) + 1; ++ smpriv->top_page = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart) ++ & SMPART_MAX_PG_MASK) >> SMPART_MAX_PG_SHIFT) + 1; ++ smpriv->page_size = 1024 << ((rd_reg32(&ctrlpriv->ctrl->perfmon.smvid) ++ & SMVID_PG_SIZE_MASK) >> SMVID_PG_SIZE_SHIFT); ++ smpriv->slot_size = 1 << CONFIG_CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE; ++ ++#ifdef SM_DEBUG ++ dev_info(smdev, "max pages = %d, top partition = %d\n", ++ smpriv->max_pages, smpriv->top_partition); ++ dev_info(smdev, "top page = %d, page size = %d (total = %d)\n", ++ smpriv->top_page, smpriv->page_size, ++ smpriv->top_page * smpriv->page_size); ++ dev_info(smdev, "selected slot size = %d\n", smpriv->slot_size); ++#endif ++ ++ /* ++ * Now probe for partitions/pages to which we have access. Note that ++ * these have likely been set up by a bootloader or platform ++ * provisioning application, so we have to assume that we "inherit" ++ * a configuration and work within the constraints of what it might be. ++ * ++ * Assume use of the zeroth ring in the present iteration (until ++ * we can divorce the controller and ring drivers, and then assign ++ * an SM instance to any ring instance). ++ */ ++ smpriv->smringdev = ctrlpriv->jrdev[0]; ++ jrpriv = dev_get_drvdata(smpriv->smringdev); ++ lpagect = 0; ++ lpagedesc = kzalloc(sizeof(struct sm_page_descriptor) ++ * smpriv->max_pages, GFP_KERNEL); ++ if (lpagedesc == NULL) { ++ kfree(smpriv); ++ return -ENOMEM; ++ } ++ ++ for (page = 0; page < smpriv->max_pages; page++) { ++ wr_reg32(&jrpriv->rregs->sm_cmd, ++ ((page << SMC_PAGE_SHIFT) & SMC_PAGE_MASK) | ++ (SMC_CMD_PAGE_INQUIRY & SMC_CMD_MASK)); ++ pgstat = rd_reg32(&jrpriv->rregs->sm_status); ++ if (((pgstat & SMCS_PGWON_MASK) >> SMCS_PGOWN_SHIFT) ++ == SMCS_PGOWN_OWNED) { /* our page? */ ++ lpagedesc[page].phys_pagenum = ++ (pgstat & SMCS_PAGE_MASK) >> SMCS_PAGE_SHIFT; ++ lpagedesc[page].own_part = ++ (pgstat & SMCS_PART_SHIFT) >> SMCS_PART_MASK; ++ lpagedesc[page].pg_base = ctrlpriv->sm_base + ++ ((smpriv->page_size * page) / sizeof(u32)); ++ lpagect++; ++#ifdef SM_DEBUG ++ dev_info(smdev, ++ "physical page %d, owning partition = %d\n", ++ lpagedesc[page].phys_pagenum, ++ lpagedesc[page].own_part); ++#endif ++ } ++ } ++ ++ smpriv->pagedesc = kzalloc(sizeof(struct sm_page_descriptor) * lpagect, ++ GFP_KERNEL); ++ if (smpriv->pagedesc == NULL) { ++ kfree(lpagedesc); ++ kfree(smpriv); ++ return -ENOMEM; ++ } ++ smpriv->localpages = lpagect; ++ ++ detectedpage = 0; ++ for (page = 0; page < smpriv->max_pages; page++) { ++ if (lpagedesc[page].pg_base != NULL) { /* e.g. live entry */ ++ memcpy(&smpriv->pagedesc[detectedpage], ++ &lpagedesc[page], ++ sizeof(struct sm_page_descriptor)); ++#ifdef SM_DEBUG_CONT ++ sm_show_page(smdev, &smpriv->pagedesc[detectedpage]); ++#endif ++ detectedpage++; ++ } ++ } ++ ++ kfree(lpagedesc); ++ ++ sm_init_keystore(smdev); ++ ++ return 0; ++} ++ ++void caam_sm_shutdown(struct platform_device *pdev) ++{ ++ struct device *ctrldev, *smdev; ++ struct caam_drv_private *priv; ++ struct caam_drv_private_sm *smpriv; ++ ++ ctrldev = &pdev->dev; ++ priv = dev_get_drvdata(ctrldev); ++ smdev = priv->smdev; ++ smpriv = dev_get_drvdata(smdev); ++ ++ kfree(smpriv->pagedesc); ++ kfree(smpriv); ++} ++EXPORT_SYMBOL(caam_sm_shutdown); ++#ifdef CONFIG_OF ++static void __exit caam_sm_exit(void) ++{ ++ struct device_node *dev_node; ++ struct platform_device *pdev; ++ ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); ++ if (!dev_node) { ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); ++ if (!dev_node) ++ return; ++ } ++ ++ pdev = of_find_device_by_node(dev_node); ++ if (!pdev) ++ return; ++ ++ of_node_put(dev_node); ++ ++ caam_sm_shutdown(pdev); ++ ++ return; ++} ++ ++static int __init caam_sm_init(void) ++{ ++ struct device_node *dev_node; ++ struct platform_device *pdev; ++ ++ /* ++ * Do of_find_compatible_node() then of_find_device_by_node() ++ * once a functional device tree is available ++ */ ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); ++ if (!dev_node) { ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); ++ if (!dev_node) ++ return -ENODEV; ++ } ++ ++ pdev = of_find_device_by_node(dev_node); ++ if (!pdev) ++ return -ENODEV; ++ ++ of_node_get(dev_node); ++ ++ caam_sm_startup(pdev); ++ ++ return 0; ++} ++ ++module_init(caam_sm_init); ++module_exit(caam_sm_exit); ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_DESCRIPTION("FSL CAAM Secure Memory / Keystore"); ++MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD"); ++#endif +diff -Nur linux-3.14.36/drivers/crypto/caam/sm_test.c linux-openelec/drivers/crypto/caam/sm_test.c +--- linux-3.14.36/drivers/crypto/caam/sm_test.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/crypto/caam/sm_test.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,844 @@ ++/* ++ * Secure Memory / Keystore Exemplification Module ++ * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved ++ * ++ * Serves as a functional example, and as a self-contained unit test for ++ * the functionality contained in sm_store.c. ++ * ++ * The example function, caam_sm_example_init(), runs a thread that: ++ * ++ * - initializes a set of fixed keys ++ * - stores one copy in clear buffers ++ * - stores them again in secure memory ++ * - extracts stored keys back out for use ++ * - intializes 3 data buffers for a test: ++ * (1) containing cleartext ++ * (2) to hold ciphertext encrypted with an extracted black key ++ * (3) to hold extracted cleartext decrypted with an equivalent clear key ++ * ++ * The function then builds simple job descriptors that reference the key ++ * material and buffers as initialized, and executes an encryption job ++ * with a black key, and a decryption job using a the same key held in the ++ * clear. The output of the decryption job is compared to the original ++ * cleartext; if they don't compare correctly, one can assume a key problem ++ * exists, where the function will exit with an error. ++ * ++ * This module can use a substantial amount of refactoring, which may occur ++ * after the API gets some mileage. Furthermore, expect this module to ++ * eventually disappear once the API is integrated into "real" software. ++ */ ++ ++#include "compat.h" ++#include "intern.h" ++#include "desc.h" ++#include "error.h" ++#include "jr.h" ++#include "sm.h" ++ ++static u8 skeymod[] = { ++ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, ++ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 ++}; ++static u8 symkey[] = { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, ++ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, ++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, ++ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f ++}; ++ ++static u8 symdata[] = { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x0f, 0x06, 0x07, ++ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, ++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, ++ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ++ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, ++ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ++ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, ++ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, ++ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, ++ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, ++ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, ++ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, ++ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, ++ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, ++ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, ++ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, ++ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, ++ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, ++ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, ++ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, ++ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, ++ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, ++ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, ++ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, ++ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, ++ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, ++ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, ++ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, ++ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, ++ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, ++ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff ++}; ++ ++static int mk_job_desc(u32 *desc, dma_addr_t key, u16 keysz, dma_addr_t indata, ++ dma_addr_t outdata, u16 sz, u32 cipherdir, u32 keymode) ++{ ++ desc[1] = CMD_KEY | CLASS_1 | (keysz & KEY_LENGTH_MASK) | keymode; ++ desc[2] = (u32)key; ++ desc[3] = CMD_OPERATION | OP_TYPE_CLASS1_ALG | OP_ALG_AAI_ECB | ++ cipherdir; ++ desc[4] = CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | ++ FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1 | sz; ++ desc[5] = (u32)indata; ++ desc[6] = CMD_FIFO_STORE | FIFOST_TYPE_MESSAGE_DATA | sz; ++ desc[7] = (u32)outdata; ++ ++ desc[0] = CMD_DESC_HDR | HDR_ONE | (8 & HDR_DESCLEN_MASK); ++ return 8 * sizeof(u32); ++} ++ ++struct exec_test_result { ++ int error; ++ struct completion completion; ++}; ++ ++void exec_test_done(struct device *dev, u32 *desc, u32 err, void *context) ++{ ++ struct exec_test_result *res = context; ++ ++ if (err) { ++ char tmp[CAAM_ERROR_STR_MAX]; ++ dev_err(dev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); ++ } ++ ++ res->error = err; ++ complete(&res->completion); ++} ++ ++static int exec_test_job(struct device *ksdev, u32 *jobdesc) ++{ ++ struct exec_test_result testres; ++ struct caam_drv_private_sm *kspriv; ++ int rtn = 0; ++ ++ kspriv = dev_get_drvdata(ksdev); ++ ++ init_completion(&testres.completion); ++ ++ rtn = caam_jr_enqueue(kspriv->smringdev, jobdesc, exec_test_done, ++ &testres); ++ if (!rtn) { ++ wait_for_completion_interruptible(&testres.completion); ++ rtn = testres.error; ++ } ++ return rtn; ++} ++ ++ ++int caam_sm_example_init(struct platform_device *pdev) ++{ ++ struct device *ctrldev, *ksdev; ++ struct caam_drv_private *ctrlpriv; ++ struct caam_drv_private_sm *kspriv; ++ u32 unit, units, jdescsz; ++ int stat, jstat, rtnval = 0; ++ u8 __iomem *syminp, *symint, *symout = NULL; ++ dma_addr_t syminp_dma, symint_dma, symout_dma; ++ u8 __iomem *black_key_des, *black_key_aes128; ++ u8 __iomem *black_key_aes256; ++ dma_addr_t black_key_des_dma, black_key_aes128_dma; ++ dma_addr_t black_key_aes256_dma; ++ u8 __iomem *clear_key_des, *clear_key_aes128, *clear_key_aes256; ++ dma_addr_t clear_key_des_dma, clear_key_aes128_dma; ++ dma_addr_t clear_key_aes256_dma; ++ u32 __iomem *jdesc; ++ u32 keyslot_des, keyslot_aes128, keyslot_aes256 = 0; ++ ++ jdesc = NULL; ++ black_key_des = black_key_aes128 = black_key_aes256 = NULL; ++ clear_key_des = clear_key_aes128 = clear_key_aes256 = NULL; ++ ++ /* We can lose this cruft once we can get a pdev by name */ ++ ctrldev = &pdev->dev; ++ ctrlpriv = dev_get_drvdata(ctrldev); ++ ksdev = ctrlpriv->smdev; ++ kspriv = dev_get_drvdata(ksdev); ++ if (kspriv == NULL) ++ return -ENODEV; ++ ++ /* Now that we have the dev for the single SM instance, connect */ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test_init() running\n"); ++#endif ++ /* Probe to see what keystores are available to us */ ++ units = sm_detect_keystore_units(ksdev); ++ if (!units) ++ dev_err(ksdev, "caam_sm_test: no keystore units available\n"); ++ ++ /* ++ * MX6 bootloader stores some stuff in unit 0, so let's ++ * use 1 or above ++ */ ++ if (units < 2) { ++ dev_err(ksdev, "caam_sm_test: insufficient keystore units\n"); ++ return -ENODEV; ++ } ++ unit = 1; ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: %d keystore units available\n", units); ++#endif ++ ++ /* Initialize/Establish Keystore */ ++ sm_establish_keystore(ksdev, unit); /* Initalize store in #1 */ ++ ++ /* ++ * Top of main test thread ++ */ ++ ++ /* Allocate test data blocks (input, intermediate, output) */ ++ syminp = kmalloc(256, GFP_KERNEL | GFP_DMA); ++ symint = kmalloc(256, GFP_KERNEL | GFP_DMA); ++ symout = kmalloc(256, GFP_KERNEL | GFP_DMA); ++ if ((syminp == NULL) || (symint == NULL) || (symout == NULL)) { ++ rtnval = -ENOMEM; ++ dev_err(ksdev, "caam_sm_test: can't get test data buffers\n"); ++ goto freemem; ++ } ++ ++ /* Allocate storage for 3 black keys: encapsulated 8, 16, 32 */ ++ black_key_des = kmalloc(16, GFP_KERNEL | GFP_DMA); /* padded to 16... */ ++ black_key_aes128 = kmalloc(16, GFP_KERNEL | GFP_DMA); ++ black_key_aes256 = kmalloc(16, GFP_KERNEL | GFP_DMA); ++ if ((black_key_des == NULL) || (black_key_aes128 == NULL) || ++ (black_key_aes256 == NULL)) { ++ rtnval = -ENOMEM; ++ dev_err(ksdev, "caam_sm_test: can't black key buffers\n"); ++ goto freemem; ++ } ++ ++ clear_key_des = kmalloc(8, GFP_KERNEL | GFP_DMA); ++ clear_key_aes128 = kmalloc(16, GFP_KERNEL | GFP_DMA); ++ clear_key_aes256 = kmalloc(32, GFP_KERNEL | GFP_DMA); ++ if ((clear_key_des == NULL) || (clear_key_aes128 == NULL) || ++ (clear_key_aes256 == NULL)) { ++ rtnval = -ENOMEM; ++ dev_err(ksdev, "caam_sm_test: can't get clear key buffers\n"); ++ goto freemem; ++ } ++ ++ /* Allocate storage for job descriptor */ ++ jdesc = kmalloc(8 * sizeof(u32), GFP_KERNEL | GFP_DMA); ++ if (jdesc == NULL) { ++ rtnval = -ENOMEM; ++ dev_err(ksdev, "caam_sm_test: can't get descriptor buffers\n"); ++ goto freemem; ++ } ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: all buffers allocated\n"); ++#endif ++ ++ /* Load up input data block, clear outputs */ ++ memcpy(syminp, symdata, 256); ++ memset(symint, 0, 256); ++ memset(symout, 0, 256); ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ syminp[0], syminp[1], syminp[2], syminp[3], ++ syminp[4], syminp[5], syminp[6], syminp[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[0], symint[1], symint[2], symint[3], ++ symint[4], symint[5], symint[6], symint[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symout[0], symout[1], symout[2], symout[3], ++ symout[4], symout[5], symout[6], symout[7]); ++ ++ dev_info(ksdev, "caam_sm_test: data buffers initialized\n"); ++#endif ++ ++ /* Load up clear keys */ ++ memcpy(clear_key_des, symkey, 8); ++ memcpy(clear_key_aes128, symkey, 16); ++ memcpy(clear_key_aes256, symkey, 32); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: all clear keys loaded\n"); ++#endif ++ ++ /* ++ * Place clear keys in keystore. ++ * All the interesting stuff happens here. ++ */ ++ /* 8 bit DES key */ ++ stat = sm_keystore_slot_alloc(ksdev, unit, 8, &keyslot_des); ++ if (stat) ++ goto freemem; ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: 8 byte key slot in %d\n", keyslot_des); ++#endif ++ stat = sm_keystore_slot_load(ksdev, unit, keyslot_des, clear_key_des, ++ 8); ++ if (stat) { ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: can't load 8 byte key in %d\n", ++ keyslot_des); ++#endif ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); ++ goto freemem; ++ } ++ ++ /* 16 bit AES key */ ++ stat = sm_keystore_slot_alloc(ksdev, unit, 16, &keyslot_aes128); ++ if (stat) { ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); ++ goto freemem; ++ } ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: 16 byte key slot in %d\n", ++ keyslot_aes128); ++#endif ++ stat = sm_keystore_slot_load(ksdev, unit, keyslot_aes128, ++ clear_key_aes128, 16); ++ if (stat) { ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: can't load 16 byte key in %d\n", ++ keyslot_aes128); ++#endif ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); ++ goto freemem; ++ } ++ ++ /* 32 bit AES key */ ++ stat = sm_keystore_slot_alloc(ksdev, unit, 32, &keyslot_aes256); ++ if (stat) { ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); ++ goto freemem; ++ } ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: 32 byte key slot in %d\n", ++ keyslot_aes256); ++#endif ++ stat = sm_keystore_slot_load(ksdev, unit, keyslot_aes256, ++ clear_key_aes256, 32); ++ if (stat) { ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: can't load 32 byte key in %d\n", ++ keyslot_aes128); ++#endif ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes256); ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); ++ sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); ++ goto freemem; ++ } ++ ++ /* Encapsulate all keys as SM blobs */ ++ stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_des, ++ keyslot_des, 8, skeymod, 8); ++ if (stat) { ++ dev_info(ksdev, "caam_sm_test: can't encapsulate DES key\n"); ++ goto freekeys; ++ } ++ ++ stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_aes128, ++ keyslot_aes128, 16, skeymod, 8); ++ if (stat) { ++ dev_info(ksdev, "caam_sm_test: can't encapsulate AES128 key\n"); ++ goto freekeys; ++ } ++ ++ stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_aes256, ++ keyslot_aes256, 32, skeymod, 8); ++ if (stat) { ++ dev_info(ksdev, "caam_sm_test: can't encapsulate AES256 key\n"); ++ goto freekeys; ++ } ++ ++ /* Now decapsulate as black key blobs */ ++ stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_des, ++ keyslot_des, 8, skeymod, 8); ++ if (stat) { ++ dev_info(ksdev, "caam_sm_test: can't decapsulate DES key\n"); ++ goto freekeys; ++ } ++ ++ stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_aes128, ++ keyslot_aes128, 16, skeymod, 8); ++ if (stat) { ++ dev_info(ksdev, "caam_sm_test: can't decapsulate AES128 key\n"); ++ goto freekeys; ++ } ++ ++ stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_aes256, ++ keyslot_aes256, 32, skeymod, 8); ++ if (stat) { ++ dev_info(ksdev, "caam_sm_test: can't decapsulate AES128 key\n"); ++ goto freekeys; ++ } ++ ++ /* Extract 8/16/32 byte black keys */ ++ sm_keystore_slot_read(ksdev, unit, keyslot_des, 8, black_key_des); ++ sm_keystore_slot_read(ksdev, unit, keyslot_aes128, 16, ++ black_key_aes128); ++ sm_keystore_slot_read(ksdev, unit, keyslot_aes256, 32, ++ black_key_aes256); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: all black keys extracted\n"); ++#endif ++ ++ /* DES encrypt using 8 byte black key */ ++ black_key_des_dma = dma_map_single(ksdev, black_key_des, 8, ++ DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, black_key_des_dma, 8, DMA_TO_DEVICE); ++ syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE); ++ symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE); ++ ++ jdescsz = mk_job_desc(jdesc, black_key_des_dma, 8, syminp_dma, ++ symint_dma, 256, ++ OP_ALG_ENCRYPT | OP_ALG_ALGSEL_DES, 0); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "jobdesc:\n"); ++ dev_info(ksdev, "0x%08x\n", jdesc[0]); ++ dev_info(ksdev, "0x%08x\n", jdesc[1]); ++ dev_info(ksdev, "0x%08x\n", jdesc[2]); ++ dev_info(ksdev, "0x%08x\n", jdesc[3]); ++ dev_info(ksdev, "0x%08x\n", jdesc[4]); ++ dev_info(ksdev, "0x%08x\n", jdesc[5]); ++ dev_info(ksdev, "0x%08x\n", jdesc[6]); ++ dev_info(ksdev, "0x%08x\n", jdesc[7]); ++#endif ++ ++ jstat = exec_test_job(ksdev, jdesc); ++ ++ dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE); ++ dma_unmap_single(ksdev, black_key_des_dma, 8, DMA_TO_DEVICE); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "input block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ syminp[0], syminp[1], syminp[2], syminp[3], ++ syminp[4], syminp[5], syminp[6], syminp[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ syminp[8], syminp[9], syminp[10], syminp[11], ++ syminp[12], syminp[13], syminp[14], syminp[15]); ++ dev_info(ksdev, "intermediate block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[0], symint[1], symint[2], symint[3], ++ symint[4], symint[5], symint[6], symint[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[8], symint[9], symint[10], symint[11], ++ symint[12], symint[13], symint[14], symint[15]); ++ dev_info(ksdev, "caam_sm_test: encrypt cycle with 8 byte key\n"); ++#endif ++ ++ /* DES decrypt using 8 byte clear key */ ++ clear_key_des_dma = dma_map_single(ksdev, clear_key_des, 8, ++ DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, clear_key_des_dma, 8, DMA_TO_DEVICE); ++ symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE); ++ symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE); ++ ++ jdescsz = mk_job_desc(jdesc, clear_key_des_dma, 8, symint_dma, ++ symout_dma, 256, ++ OP_ALG_DECRYPT | OP_ALG_ALGSEL_DES, 0); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "jobdesc:\n"); ++ dev_info(ksdev, "0x%08x\n", jdesc[0]); ++ dev_info(ksdev, "0x%08x\n", jdesc[1]); ++ dev_info(ksdev, "0x%08x\n", jdesc[2]); ++ dev_info(ksdev, "0x%08x\n", jdesc[3]); ++ dev_info(ksdev, "0x%08x\n", jdesc[4]); ++ dev_info(ksdev, "0x%08x\n", jdesc[5]); ++ dev_info(ksdev, "0x%08x\n", jdesc[6]); ++ dev_info(ksdev, "0x%08x\n", jdesc[7]); ++#endif ++ ++ jstat = exec_test_job(ksdev, jdesc); ++ ++ dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE); ++ dma_unmap_single(ksdev, clear_key_des_dma, 8, DMA_TO_DEVICE); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "intermediate block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[0], symint[1], symint[2], symint[3], ++ symint[4], symint[5], symint[6], symint[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[8], symint[9], symint[10], symint[11], ++ symint[12], symint[13], symint[14], symint[15]); ++ dev_info(ksdev, "decrypted block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symout[0], symout[1], symout[2], symout[3], ++ symout[4], symout[5], symout[6], symout[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symout[8], symout[9], symout[10], symout[11], ++ symout[12], symout[13], symout[14], symout[15]); ++ dev_info(ksdev, "caam_sm_test: decrypt cycle with 8 byte key\n"); ++#endif ++ ++ /* Check result */ ++ if (memcmp(symout, syminp, 256)) { ++ dev_info(ksdev, "caam_sm_test: 8-byte key test mismatch\n"); ++ rtnval = -1; ++ goto freekeys; ++ } else ++ dev_info(ksdev, "caam_sm_test: 8-byte key test match OK\n"); ++ ++ /* AES-128 encrypt using 16 byte black key */ ++ black_key_aes128_dma = dma_map_single(ksdev, black_key_aes128, 16, ++ DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, black_key_aes128_dma, 16, ++ DMA_TO_DEVICE); ++ syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE); ++ symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE); ++ ++ jdescsz = mk_job_desc(jdesc, black_key_aes128_dma, 16, syminp_dma, ++ symint_dma, 256, ++ OP_ALG_ENCRYPT | OP_ALG_ALGSEL_AES, 0); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "jobdesc:\n"); ++ dev_info(ksdev, "0x%08x\n", jdesc[0]); ++ dev_info(ksdev, "0x%08x\n", jdesc[1]); ++ dev_info(ksdev, "0x%08x\n", jdesc[2]); ++ dev_info(ksdev, "0x%08x\n", jdesc[3]); ++ dev_info(ksdev, "0x%08x\n", jdesc[4]); ++ dev_info(ksdev, "0x%08x\n", jdesc[5]); ++ dev_info(ksdev, "0x%08x\n", jdesc[6]); ++ dev_info(ksdev, "0x%08x\n", jdesc[7]); ++#endif ++ ++ jstat = exec_test_job(ksdev, jdesc); ++ ++ dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE); ++ dma_unmap_single(ksdev, black_key_aes128_dma, 16, DMA_TO_DEVICE); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "input block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ syminp[0], syminp[1], syminp[2], syminp[3], ++ syminp[4], syminp[5], syminp[6], syminp[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ syminp[8], syminp[9], syminp[10], syminp[11], ++ syminp[12], syminp[13], syminp[14], syminp[15]); ++ dev_info(ksdev, "intermediate block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[0], symint[1], symint[2], symint[3], ++ symint[4], symint[5], symint[6], symint[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[8], symint[9], symint[10], symint[11], ++ symint[12], symint[13], symint[14], symint[15]); ++ dev_info(ksdev, "caam_sm_test: encrypt cycle with 16 byte key\n"); ++#endif ++ ++ /* AES-128 decrypt using 16 byte clear key */ ++ clear_key_aes128_dma = dma_map_single(ksdev, clear_key_aes128, 16, ++ DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, clear_key_aes128_dma, 16, ++ DMA_TO_DEVICE); ++ symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE); ++ symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE); ++ ++ jdescsz = mk_job_desc(jdesc, clear_key_aes128_dma, 16, symint_dma, ++ symout_dma, 256, ++ OP_ALG_DECRYPT | OP_ALG_ALGSEL_AES, 0); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "jobdesc:\n"); ++ dev_info(ksdev, "0x%08x\n", jdesc[0]); ++ dev_info(ksdev, "0x%08x\n", jdesc[1]); ++ dev_info(ksdev, "0x%08x\n", jdesc[2]); ++ dev_info(ksdev, "0x%08x\n", jdesc[3]); ++ dev_info(ksdev, "0x%08x\n", jdesc[4]); ++ dev_info(ksdev, "0x%08x\n", jdesc[5]); ++ dev_info(ksdev, "0x%08x\n", jdesc[6]); ++ dev_info(ksdev, "0x%08x\n", jdesc[7]); ++#endif ++ jstat = exec_test_job(ksdev, jdesc); ++ ++ dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE); ++ dma_unmap_single(ksdev, clear_key_aes128_dma, 16, DMA_TO_DEVICE); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "intermediate block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[0], symint[1], symint[2], symint[3], ++ symint[4], symint[5], symint[6], symint[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[8], symint[9], symint[10], symint[11], ++ symint[12], symint[13], symint[14], symint[15]); ++ dev_info(ksdev, "decrypted block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symout[0], symout[1], symout[2], symout[3], ++ symout[4], symout[5], symout[6], symout[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symout[8], symout[9], symout[10], symout[11], ++ symout[12], symout[13], symout[14], symout[15]); ++ dev_info(ksdev, "caam_sm_test: decrypt cycle with 16 byte key\n"); ++#endif ++ ++ /* Check result */ ++ if (memcmp(symout, syminp, 256)) { ++ dev_info(ksdev, "caam_sm_test: 16-byte key test mismatch\n"); ++ rtnval = -1; ++ goto freekeys; ++ } else ++ dev_info(ksdev, "caam_sm_test: 16-byte key test match OK\n"); ++ ++ /* AES-256 encrypt using 32 byte black key */ ++ black_key_aes256_dma = dma_map_single(ksdev, black_key_aes256, 32, ++ DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, black_key_aes256_dma, 32, ++ DMA_TO_DEVICE); ++ syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE); ++ symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE); ++ ++ jdescsz = mk_job_desc(jdesc, black_key_aes256_dma, 32, syminp_dma, ++ symint_dma, 256, ++ OP_ALG_ENCRYPT | OP_ALG_ALGSEL_AES, 0); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "jobdesc:\n"); ++ dev_info(ksdev, "0x%08x\n", jdesc[0]); ++ dev_info(ksdev, "0x%08x\n", jdesc[1]); ++ dev_info(ksdev, "0x%08x\n", jdesc[2]); ++ dev_info(ksdev, "0x%08x\n", jdesc[3]); ++ dev_info(ksdev, "0x%08x\n", jdesc[4]); ++ dev_info(ksdev, "0x%08x\n", jdesc[5]); ++ dev_info(ksdev, "0x%08x\n", jdesc[6]); ++ dev_info(ksdev, "0x%08x\n", jdesc[7]); ++#endif ++ ++ jstat = exec_test_job(ksdev, jdesc); ++ ++ dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE); ++ dma_unmap_single(ksdev, black_key_aes256_dma, 32, DMA_TO_DEVICE); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "input block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ syminp[0], syminp[1], syminp[2], syminp[3], ++ syminp[4], syminp[5], syminp[6], syminp[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ syminp[8], syminp[9], syminp[10], syminp[11], ++ syminp[12], syminp[13], syminp[14], syminp[15]); ++ dev_info(ksdev, "intermediate block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[0], symint[1], symint[2], symint[3], ++ symint[4], symint[5], symint[6], symint[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[8], symint[9], symint[10], symint[11], ++ symint[12], symint[13], symint[14], symint[15]); ++ dev_info(ksdev, "caam_sm_test: encrypt cycle with 32 byte key\n"); ++#endif ++ ++ /* AES-256 decrypt using 32-byte black key */ ++ clear_key_aes256_dma = dma_map_single(ksdev, clear_key_aes256, 32, ++ DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, clear_key_aes256_dma, 32, ++ DMA_TO_DEVICE); ++ symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE); ++ dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE); ++ symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE); ++ ++ jdescsz = mk_job_desc(jdesc, clear_key_aes256_dma, 32, symint_dma, ++ symout_dma, 256, ++ OP_ALG_DECRYPT | OP_ALG_ALGSEL_AES, 0); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "jobdesc:\n"); ++ dev_info(ksdev, "0x%08x\n", jdesc[0]); ++ dev_info(ksdev, "0x%08x\n", jdesc[1]); ++ dev_info(ksdev, "0x%08x\n", jdesc[2]); ++ dev_info(ksdev, "0x%08x\n", jdesc[3]); ++ dev_info(ksdev, "0x%08x\n", jdesc[4]); ++ dev_info(ksdev, "0x%08x\n", jdesc[5]); ++ dev_info(ksdev, "0x%08x\n", jdesc[6]); ++ dev_info(ksdev, "0x%08x\n", jdesc[7]); ++#endif ++ ++ jstat = exec_test_job(ksdev, jdesc); ++ ++ dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE); ++ dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE); ++ dma_unmap_single(ksdev, clear_key_aes256_dma, 32, DMA_TO_DEVICE); ++ ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "intermediate block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[0], symint[1], symint[2], symint[3], ++ symint[4], symint[5], symint[6], symint[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symint[8], symint[9], symint[10], symint[11], ++ symint[12], symint[13], symint[14], symint[15]); ++ dev_info(ksdev, "decrypted block:\n"); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symout[0], symout[1], symout[2], symout[3], ++ symout[4], symout[5], symout[6], symout[7]); ++ dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ ++ "0x%02x 0x%02x 0x%02x 0x%02x\n", ++ symout[8], symout[9], symout[10], symout[11], ++ symout[12], symout[13], symout[14], symout[15]); ++ dev_info(ksdev, "caam_sm_test: decrypt cycle with 32 byte key\n"); ++#endif ++ ++ /* Check result */ ++ if (memcmp(symout, syminp, 256)) { ++ dev_info(ksdev, "caam_sm_test: 32-byte key test mismatch\n"); ++ rtnval = -1; ++ goto freekeys; ++ } else ++ dev_info(ksdev, "caam_sm_test: 32-byte key test match OK\n"); ++ ++ ++ /* Remove 8/16/32 byte keys from keystore */ ++freekeys: ++ stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); ++ if (stat) ++ dev_info(ksdev, "caam_sm_test: can't release slot %d\n", ++ keyslot_des); ++ ++ stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); ++ if (stat) ++ dev_info(ksdev, "caam_sm_test: can't release slot %d\n", ++ keyslot_aes128); ++ ++ stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes256); ++ if (stat) ++ dev_info(ksdev, "caam_sm_test: can't release slot %d\n", ++ keyslot_aes256); ++ ++ ++ /* Free resources */ ++freemem: ++#ifdef SM_TEST_DETAIL ++ dev_info(ksdev, "caam_sm_test: cleaning up\n"); ++#endif ++ kfree(syminp); ++ kfree(symint); ++ kfree(symout); ++ kfree(clear_key_des); ++ kfree(clear_key_aes128); ++ kfree(clear_key_aes256); ++ kfree(black_key_des); ++ kfree(black_key_aes128); ++ kfree(black_key_aes256); ++ kfree(jdesc); ++ ++ /* Disconnect from keystore and leave */ ++ sm_release_keystore(ksdev, unit); ++ ++ return rtnval; ++} ++EXPORT_SYMBOL(caam_sm_example_init); ++ ++void caam_sm_example_shutdown(void) ++{ ++ /* unused in present version */ ++ struct device_node *dev_node; ++ struct platform_device *pdev; ++ ++ /* ++ * Do of_find_compatible_node() then of_find_device_by_node() ++ * once a functional device tree is available ++ */ ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); ++ if (!dev_node) { ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); ++ if (!dev_node) ++ return; ++ } ++ ++ pdev = of_find_device_by_node(dev_node); ++ if (!pdev) ++ return; ++ ++ of_node_get(dev_node); ++ ++} ++ ++static int __init caam_sm_test_init(void) ++{ ++ struct device_node *dev_node; ++ struct platform_device *pdev; ++ ++ /* ++ * Do of_find_compatible_node() then of_find_device_by_node() ++ * once a functional device tree is available ++ */ ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); ++ if (!dev_node) { ++ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); ++ if (!dev_node) ++ return -ENODEV; ++ } ++ ++ pdev = of_find_device_by_node(dev_node); ++ if (!pdev) ++ return -ENODEV; ++ ++ of_node_put(dev_node); ++ ++ caam_sm_example_init(pdev); ++ ++ return 0; ++} ++ ++ ++/* Module-based initialization needs to wait for dev tree */ ++#ifdef CONFIG_OF ++module_init(caam_sm_test_init); ++module_exit(caam_sm_example_shutdown); ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_DESCRIPTION("FSL CAAM Keystore Usage Example"); ++MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD"); ++#endif +diff -Nur linux-3.14.36/drivers/crypto/caam/snvsregs.h linux-openelec/drivers/crypto/caam/snvsregs.h +--- linux-3.14.36/drivers/crypto/caam/snvsregs.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/crypto/caam/snvsregs.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,237 @@ ++/* ++ * SNVS hardware register-level view ++ * ++ * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved ++ */ ++ ++#ifndef SNVSREGS_H ++#define SNVSREGS_H ++ ++#include ++#include ++ ++/* ++ * SNVS High Power Domain ++ * Includes security violations, HA counter, RTC, alarm ++ */ ++struct snvs_hp { ++ u32 lock; ++ u32 cmd; ++ u32 ctl; ++ u32 secvio_int_en; /* Security Violation Interrupt Enable */ ++ u32 secvio_int_ctl; /* Security Violation Interrupt Control */ ++ u32 status; ++ u32 secvio_status; /* Security Violation Status */ ++ u32 ha_counteriv; /* High Assurance Counter IV */ ++ u32 ha_counter; /* High Assurance Counter */ ++ u32 rtc_msb; /* Real Time Clock/Counter MSB */ ++ u32 rtc_lsb; /* Real Time Counter LSB */ ++ u32 time_alarm_msb; /* Time Alarm MSB */ ++ u32 time_alarm_lsb; /* Time Alarm LSB */ ++}; ++ ++#define HP_LOCK_HAC_LCK 0x00040000 ++#define HP_LOCK_HPSICR_LCK 0x00020000 ++#define HP_LOCK_HPSVCR_LCK 0x00010000 ++#define HP_LOCK_MKEYSEL_LCK 0x00000200 ++#define HP_LOCK_TAMPCFG_LCK 0x00000100 ++#define HP_LOCK_TAMPFLT_LCK 0x00000080 ++#define HP_LOCK_SECVIO_LCK 0x00000040 ++#define HP_LOCK_GENP_LCK 0x00000020 ++#define HP_LOCK_MONOCTR_LCK 0x00000010 ++#define HP_LOCK_CALIB_LCK 0x00000008 ++#define HP_LOCK_SRTC_LCK 0x00000004 ++#define HP_LOCK_ZMK_RD_LCK 0x00000002 ++#define HP_LOCK_ZMK_WT_LCK 0x00000001 ++ ++#define HP_CMD_NONPRIV_AXS 0x80000000 ++#define HP_CMD_HAC_STOP 0x00080000 ++#define HP_CMD_HAC_CLEAR 0x00040000 ++#define HP_CMD_HAC_LOAD 0x00020000 ++#define HP_CMD_HAC_CFG_EN 0x00010000 ++#define HP_CMD_SNVS_MSTR_KEY 0x00002000 ++#define HP_CMD_PROG_ZMK 0x00001000 ++#define HP_CMD_SW_LPSV 0x00000400 ++#define HP_CMD_SW_FSV 0x00000200 ++#define HP_CMD_SW_SV 0x00000100 ++#define HP_CMD_LP_SWR_DIS 0x00000020 ++#define HP_CMD_LP_SWR 0x00000010 ++#define HP_CMD_SSM_SFNS_DIS 0x00000004 ++#define HP_CMD_SSM_ST_DIS 0x00000002 ++#define HP_CMD_SMM_ST 0x00000001 ++ ++#define HP_CTL_TIME_SYNC 0x00010000 ++#define HP_CTL_CAL_VAL_SHIFT 10 ++#define HP_CTL_CAL_VAL_MASK (0x1f << HP_CTL_CALIB_SHIFT) ++#define HP_CTL_CALIB_EN 0x00000100 ++#define HP_CTL_PI_FREQ_SHIFT 4 ++#define HP_CTL_PI_FREQ_MASK (0xf << HP_CTL_PI_FREQ_SHIFT) ++#define HP_CTL_PI_EN 0x00000008 ++#define HP_CTL_TIMEALARM_EN 0x00000002 ++#define HP_CTL_RTC_EN 0x00000001 ++ ++#define HP_SECVIO_INTEN_EN 0x10000000 ++#define HP_SECVIO_INTEN_SRC5 0x00000020 ++#define HP_SECVIO_INTEN_SRC4 0x00000010 ++#define HP_SECVIO_INTEN_SRC3 0x00000008 ++#define HP_SECVIO_INTEN_SRC2 0x00000004 ++#define HP_SECVIO_INTEN_SRC1 0x00000002 ++#define HP_SECVIO_INTEN_SRC0 0x00000001 ++#define HP_SECVIO_INTEN_ALL 0x8000003f ++ ++#define HP_SECVIO_ICTL_CFG_SHIFT 30 ++#define HP_SECVIO_ICTL_CFG_MASK (0x3 << HP_SECVIO_ICTL_CFG_SHIFT) ++#define HP_SECVIO_ICTL_CFG5_SHIFT 5 ++#define HP_SECVIO_ICTL_CFG5_MASK (0x3 << HP_SECVIO_ICTL_CFG5_SHIFT) ++#define HP_SECVIO_ICTL_CFG_DISABLE 0 ++#define HP_SECVIO_ICTL_CFG_NONFATAL 1 ++#define HP_SECVIO_ICTL_CFG_FATAL 2 ++#define HP_SECVIO_ICTL_CFG4_FATAL 0x00000010 ++#define HP_SECVIO_ICTL_CFG3_FATAL 0x00000008 ++#define HP_SECVIO_ICTL_CFG2_FATAL 0x00000004 ++#define HP_SECVIO_ICTL_CFG1_FATAL 0x00000002 ++#define HP_SECVIO_ICTL_CFG0_FATAL 0x00000001 ++ ++#define HP_STATUS_ZMK_ZERO 0x80000000 ++#define HP_STATUS_OTPMK_ZERO 0x08000000 ++#define HP_STATUS_OTPMK_SYN_SHIFT 16 ++#define HP_STATUS_OTPMK_SYN_MASK (0x1ff << HP_STATUS_OTPMK_SYN_SHIFT) ++#define HP_STATUS_SSM_ST_SHIFT 8 ++#define HP_STATUS_SSM_ST_MASK (0xf << HP_STATUS_SSM_ST_SHIFT) ++#define HP_STATUS_SSM_ST_INIT 0 ++#define HP_STATUS_SSM_ST_HARDFAIL 1 ++#define HP_STATUS_SSM_ST_SOFTFAIL 3 ++#define HP_STATUS_SSM_ST_INITINT 8 ++#define HP_STATUS_SSM_ST_CHECK 9 ++#define HP_STATUS_SSM_ST_NONSECURE 11 ++#define HP_STATUS_SSM_ST_TRUSTED 13 ++#define HP_STATUS_SSM_ST_SECURE 15 ++ ++#define HP_SECVIOST_ZMK_ECC_FAIL 0x08000000 /* write to clear */ ++#define HP_SECVIOST_ZMK_SYN_SHIFT 16 ++#define HP_SECVIOST_ZMK_SYN_MASK (0x1ff << HP_SECVIOST_ZMK_SYN_SHIFT) ++#define HP_SECVIOST_SECVIO5 0x00000020 ++#define HP_SECVIOST_SECVIO4 0x00000010 ++#define HP_SECVIOST_SECVIO3 0x00000008 ++#define HP_SECVIOST_SECVIO2 0x00000004 ++#define HP_SECVIOST_SECVIO1 0x00000002 ++#define HP_SECVIOST_SECVIO0 0x00000001 ++#define HP_SECVIOST_SECVIOMASK 0x0000003f ++ ++/* ++ * SNVS Low Power Domain ++ * Includes glitch detector, SRTC, alarm, monotonic counter, ZMK ++ */ ++struct snvs_lp { ++ u32 lock; ++ u32 ctl; ++ u32 mstr_key_ctl; /* Master Key Control */ ++ u32 secvio_ctl; /* Security Violation Control */ ++ u32 tamper_filt_cfg; /* Tamper Glitch Filters Configuration */ ++ u32 tamper_det_cfg; /* Tamper Detectors Configuration */ ++ u32 status; ++ u32 srtc_msb; /* Secure Real Time Clock/Counter MSB */ ++ u32 srtc_lsb; /* Secure Real Time Clock/Counter LSB */ ++ u32 time_alarm; /* Time Alarm */ ++ u32 smc_msb; /* Secure Monotonic Counter MSB */ ++ u32 smc_lsb; /* Secure Monotonic Counter LSB */ ++ u32 pwr_glitch_det; /* Power Glitch Detector */ ++ u32 gen_purpose; ++ u32 zmk[8]; /* Zeroizable Master Key */ ++}; ++ ++#define LP_LOCK_MKEYSEL_LCK 0x00000200 ++#define LP_LOCK_TAMPDET_LCK 0x00000100 ++#define LP_LOCK_TAMPFLT_LCK 0x00000080 ++#define LP_LOCK_SECVIO_LCK 0x00000040 ++#define LP_LOCK_GENP_LCK 0x00000020 ++#define LP_LOCK_MONOCTR_LCK 0x00000010 ++#define LP_LOCK_CALIB_LCK 0x00000008 ++#define LP_LOCK_SRTC_LCK 0x00000004 ++#define LP_LOCK_ZMK_RD_LCK 0x00000002 ++#define LP_LOCK_ZMK_WT_LCK 0x00000001 ++ ++#define LP_CTL_CAL_VAL_SHIFT 10 ++#define LP_CTL_CAL_VAL_MASK (0x1f << LP_CTL_CAL_VAL_SHIFT) ++#define LP_CTL_CALIB_EN 0x00000100 ++#define LP_CTL_SRTC_INVAL_EN 0x00000010 ++#define LP_CTL_WAKE_INT_EN 0x00000008 ++#define LP_CTL_MONOCTR_EN 0x00000004 ++#define LP_CTL_TIMEALARM_EN 0x00000002 ++#define LP_CTL_SRTC_EN 0x00000001 ++ ++#define LP_MKEYCTL_ZMKECC_SHIFT 8 ++#define LP_MKEYCTL_ZMKECC_MASK (0xff << LP_MKEYCTL_ZMKECC_SHIFT) ++#define LP_MKEYCTL_ZMKECC_EN 0x00000010 ++#define LP_MKEYCTL_ZMKECC_VAL 0x00000008 ++#define LP_MKEYCTL_ZMKECC_PROG 0x00000004 ++#define LP_MKEYCTL_MKSEL_SHIFT 0 ++#define LP_MKEYCTL_MKSEL_MASK (3 << LP_MKEYCTL_MKSEL_SHIFT) ++#define LP_MKEYCTL_MK_OTP 0 ++#define LP_MKEYCTL_MK_ZMK 2 ++#define LP_MKEYCTL_MK_COMB 3 ++ ++#define LP_SECVIO_CTL_SRC5 0x20 ++#define LP_SECVIO_CTL_SRC4 0x10 ++#define LP_SECVIO_CTL_SRC3 0x08 ++#define LP_SECVIO_CTL_SRC2 0x04 ++#define LP_SECVIO_CTL_SRC1 0x02 ++#define LP_SECVIO_CTL_SRC0 0x01 ++ ++#define LP_TAMPFILT_EXT2_EN 0x80000000 ++#define LP_TAMPFILT_EXT2_SHIFT 24 ++#define LP_TAMPFILT_EXT2_MASK (0x1f << LP_TAMPFILT_EXT2_SHIFT) ++#define LP_TAMPFILT_EXT1_EN 0x00800000 ++#define LP_TAMPFILT_EXT1_SHIFT 16 ++#define LP_TAMPFILT_EXT1_MASK (0x1f << LP_TAMPFILT_EXT1_SHIFT) ++#define LP_TAMPFILT_WM_EN 0x00000080 ++#define LP_TAMPFILT_WM_SHIFT 0 ++#define LP_TAMPFILT_WM_MASK (0x1f << LP_TAMPFILT_WM_SHIFT) ++ ++#define LP_TAMPDET_OSC_BPS 0x10000000 ++#define LP_TAMPDET_VRC_SHIFT 24 ++#define LP_TAMPDET_VRC_MASK (3 << LP_TAMPFILT_VRC_SHIFT) ++#define LP_TAMPDET_HTDC_SHIFT 20 ++#define LP_TAMPDET_HTDC_MASK (3 << LP_TAMPFILT_HTDC_SHIFT) ++#define LP_TAMPDET_LTDC_SHIFT 16 ++#define LP_TAMPDET_LTDC_MASK (3 << LP_TAMPFILT_LTDC_SHIFT) ++#define LP_TAMPDET_POR_OBS 0x00008000 ++#define LP_TAMPDET_PFD_OBS 0x00004000 ++#define LP_TAMPDET_ET2_EN 0x00000400 ++#define LP_TAMPDET_ET1_EN 0x00000200 ++#define LP_TAMPDET_WMT2_EN 0x00000100 ++#define LP_TAMPDET_WMT1_EN 0x00000080 ++#define LP_TAMPDET_VT_EN 0x00000040 ++#define LP_TAMPDET_TT_EN 0x00000020 ++#define LP_TAMPDET_CT_EN 0x00000010 ++#define LP_TAMPDET_MCR_EN 0x00000004 ++#define LP_TAMPDET_SRTCR_EN 0x00000002 ++ ++#define LP_STATUS_SECURE ++#define LP_STATUS_NONSECURE ++#define LP_STATUS_SCANEXIT 0x00100000 /* all write 1 clear here on */ ++#define LP_STATUS_EXT_SECVIO 0x00010000 ++#define LP_STATUS_ET2 0x00000400 ++#define LP_STATUS_ET1 0x00000200 ++#define LP_STATUS_WMT2 0x00000100 ++#define LP_STATUS_WMT1 0x00000080 ++#define LP_STATUS_VTD 0x00000040 ++#define LP_STATUS_TTD 0x00000020 ++#define LP_STATUS_CTD 0x00000010 ++#define LP_STATUS_PGD 0x00000008 ++#define LP_STATUS_MCR 0x00000004 ++#define LP_STATUS_SRTCR 0x00000002 ++#define LP_STATUS_LPTA 0x00000001 ++ ++/* Full SNVS register page, including version/options */ ++struct snvs_full { ++ struct snvs_hp hp; ++ struct snvs_lp lp; ++ u32 rsvd[731]; /* deadspace 0x08c-0xbf7 */ ++ ++ /* Version / Revision / Option ID space - end of register page */ ++ u32 vid; /* 0xbf8 HP Version ID (VID 1) */ ++ u32 opt_rev; /* 0xbfc HP Options / Revision (VID 2) */ ++}; ++ ++#endif /* SNVSREGS_H */ +diff -Nur linux-3.14.36/drivers/dma/imx-sdma.c linux-openelec/drivers/dma/imx-sdma.c +--- linux-3.14.36/drivers/dma/imx-sdma.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/dma/imx-sdma.c 2015-05-06 12:05:42.000000000 -0500 +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -232,6 +233,14 @@ + + struct sdma_engine; + ++enum sdma_mode { ++ SDMA_MODE_INVALID = 0, ++ SDMA_MODE_LOOP, ++ SDMA_MODE_NORMAL, ++ SDMA_MODE_P2P, ++ SDMA_MODE_NO_BD, ++}; ++ + /** + * struct sdma_channel - housekeeping for a SDMA channel + * +@@ -244,6 +253,7 @@ + * @word_size peripheral access size + * @buf_tail ID of the buffer that was processed + * @num_bd max NUM_BD. number of descriptors currently handling ++ * @bd_iram flag indicating the memory location of buffer descriptor + */ + struct sdma_channel { + struct sdma_engine *sdma; +@@ -255,14 +265,19 @@ + enum dma_slave_buswidth word_size; + unsigned int buf_tail; + unsigned int num_bd; ++ unsigned int period_len; + struct sdma_buffer_descriptor *bd; + dma_addr_t bd_phys; ++ bool bd_iram; + unsigned int pc_from_device, pc_to_device; +- unsigned long flags; +- dma_addr_t per_address; ++ unsigned int device_to_device; ++ unsigned int other_script; ++ enum sdma_mode mode; ++ dma_addr_t per_address, per_address2; + unsigned long event_mask[2]; + unsigned long watermark_level; + u32 shp_addr, per_addr; ++ u32 data_addr1, data_addr2; + struct dma_chan chan; + spinlock_t lock; + struct dma_async_tx_descriptor desc; +@@ -272,8 +287,6 @@ + struct tasklet_struct tasklet; + }; + +-#define IMX_DMA_SG_LOOP BIT(0) +- + #define MAX_DMA_CHANNELS 32 + #define MXC_SDMA_DEFAULT_PRIORITY 1 + #define MXC_SDMA_MIN_PRIORITY 1 +@@ -325,6 +338,7 @@ + spinlock_t channel_0_lock; + u32 script_number; + struct sdma_script_start_addrs *script_addrs; ++ struct gen_pool *iram_pool; + const struct sdma_driver_data *drvdata; + }; + +@@ -540,12 +554,14 @@ + dma_addr_t buf_phys; + int ret; + unsigned long flags; ++ bool use_iram = true; + +- buf_virt = dma_alloc_coherent(NULL, +- size, +- &buf_phys, GFP_KERNEL); ++ buf_virt = gen_pool_dma_alloc(sdma->iram_pool, size, &buf_phys); + if (!buf_virt) { +- return -ENOMEM; ++ use_iram = false; ++ buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); ++ if (!buf_virt) ++ return -ENOMEM; + } + + spin_lock_irqsave(&sdma->channel_0_lock, flags); +@@ -562,7 +578,10 @@ + + spin_unlock_irqrestore(&sdma->channel_0_lock, flags); + +- dma_free_coherent(NULL, size, buf_virt, buf_phys); ++ if (use_iram) ++ gen_pool_free(sdma->iram_pool, (unsigned long)buf_virt, size); ++ else ++ dma_free_coherent(NULL, size, buf_virt, buf_phys); + + return ret; + } +@@ -593,6 +612,12 @@ + + static void sdma_handle_channel_loop(struct sdma_channel *sdmac) + { ++ if (sdmac->desc.callback) ++ sdmac->desc.callback(sdmac->desc.callback_param); ++} ++ ++static void sdma_update_channel_loop(struct sdma_channel *sdmac) ++{ + struct sdma_buffer_descriptor *bd; + + /* +@@ -607,15 +632,10 @@ + + if (bd->mode.status & BD_RROR) + sdmac->status = DMA_ERROR; +- else +- sdmac->status = DMA_IN_PROGRESS; + + bd->mode.status |= BD_DONE; + sdmac->buf_tail++; + sdmac->buf_tail %= sdmac->num_bd; +- +- if (sdmac->desc.callback) +- sdmac->desc.callback(sdmac->desc.callback_param); + } + } + +@@ -647,14 +667,31 @@ + sdmac->desc.callback(sdmac->desc.callback_param); + } + ++static void sdma_handle_other_intr(struct sdma_channel *sdmac) ++{ ++ if (sdmac->desc.callback) ++ sdmac->desc.callback(sdmac->desc.callback_param); ++} ++ + static void sdma_tasklet(unsigned long data) + { + struct sdma_channel *sdmac = (struct sdma_channel *) data; ++ struct sdma_engine *sdma = sdmac->sdma; + +- if (sdmac->flags & IMX_DMA_SG_LOOP) ++ switch (sdmac->mode) { ++ case SDMA_MODE_LOOP: + sdma_handle_channel_loop(sdmac); +- else ++ break; ++ case SDMA_MODE_NORMAL: + mxc_sdma_handle_channel_normal(sdmac); ++ break; ++ case SDMA_MODE_NO_BD: ++ sdma_handle_other_intr(sdmac); ++ break; ++ default: ++ dev_err(sdma->dev, "invalid SDMA MODE!\n"); ++ break; ++ } + } + + static irqreturn_t sdma_int_handler(int irq, void *dev_id) +@@ -671,6 +708,9 @@ + int channel = fls(stat) - 1; + struct sdma_channel *sdmac = &sdma->channel[channel]; + ++ if (sdmac->mode & SDMA_MODE_LOOP) ++ sdma_update_channel_loop(sdmac); ++ + tasklet_schedule(&sdmac->tasklet); + + __clear_bit(channel, &stat); +@@ -692,9 +732,12 @@ + * two peripherals or memory-to-memory transfers + */ + int per_2_per = 0, emi_2_emi = 0; ++ int other = 0; + + sdmac->pc_from_device = 0; + sdmac->pc_to_device = 0; ++ sdmac->device_to_device = 0; ++ sdmac->other_script = 0; + + switch (peripheral_type) { + case IMX_DMATYPE_MEMORY: +@@ -740,8 +783,8 @@ + emi_2_per = sdma->script_addrs->mcu_2_shp_addr; + break; + case IMX_DMATYPE_ASRC: +- per_2_emi = sdma->script_addrs->asrc_2_mcu_addr; +- emi_2_per = sdma->script_addrs->asrc_2_mcu_addr; ++ per_2_emi = sdma->script_addrs->shp_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_shp_addr; + per_2_per = sdma->script_addrs->per_2_per_addr; + break; + case IMX_DMATYPE_MSHC: +@@ -758,12 +801,17 @@ + case IMX_DMATYPE_IPU_MEMORY: + emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr; + break; ++ case IMX_DMATYPE_HDMI: ++ other = sdma->script_addrs->hdmi_dma_addr; ++ break; + default: + break; + } + + sdmac->pc_from_device = per_2_emi; + sdmac->pc_to_device = emi_2_per; ++ sdmac->device_to_device = per_2_per; ++ sdmac->other_script = other; + } + + static int sdma_load_context(struct sdma_channel *sdmac) +@@ -776,11 +824,14 @@ + int ret; + unsigned long flags; + +- if (sdmac->direction == DMA_DEV_TO_MEM) { ++ if (sdmac->direction == DMA_DEV_TO_MEM) + load_address = sdmac->pc_from_device; +- } else { ++ else if (sdmac->direction == DMA_DEV_TO_DEV) ++ load_address = sdmac->device_to_device; ++ else if (sdmac->direction == DMA_MEM_TO_DEV) + load_address = sdmac->pc_to_device; +- } ++ else ++ load_address = sdmac->other_script; + + if (load_address < 0) + return load_address; +@@ -800,11 +851,16 @@ + /* Send by context the event mask,base address for peripheral + * and watermark level + */ +- context->gReg[0] = sdmac->event_mask[1]; +- context->gReg[1] = sdmac->event_mask[0]; +- context->gReg[2] = sdmac->per_addr; +- context->gReg[6] = sdmac->shp_addr; +- context->gReg[7] = sdmac->watermark_level; ++ if (sdmac->peripheral_type == IMX_DMATYPE_HDMI) { ++ context->gReg[4] = sdmac->data_addr1; ++ context->gReg[6] = sdmac->data_addr2; ++ } else { ++ context->gReg[0] = sdmac->event_mask[1]; ++ context->gReg[1] = sdmac->event_mask[0]; ++ context->gReg[2] = sdmac->per_addr; ++ context->gReg[6] = sdmac->shp_addr; ++ context->gReg[7] = sdmac->watermark_level; ++ } + + bd0->mode.command = C0_SETDM; + bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; +@@ -829,6 +885,7 @@ + + static int sdma_config_channel(struct sdma_channel *sdmac) + { ++ struct imx_dma_data *data = sdmac->chan.private; + int ret; + + sdma_disable_channel(sdmac); +@@ -837,12 +894,19 @@ + sdmac->event_mask[1] = 0; + sdmac->shp_addr = 0; + sdmac->per_addr = 0; ++ sdmac->data_addr1 = 0; ++ sdmac->data_addr2 = 0; + + if (sdmac->event_id0) { + if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events) + return -EINVAL; + sdma_event_enable(sdmac, sdmac->event_id0); + } ++ if (sdmac->event_id1) { ++ if (sdmac->event_id1 >= sdmac->sdma->drvdata->num_events) ++ return -EINVAL; ++ sdma_event_enable(sdmac, sdmac->event_id1); ++ } + + switch (sdmac->peripheral_type) { + case IMX_DMATYPE_DSP: +@@ -862,19 +926,75 @@ + (sdmac->peripheral_type != IMX_DMATYPE_DSP)) { + /* Handle multiple event channels differently */ + if (sdmac->event_id1) { +- sdmac->event_mask[1] = BIT(sdmac->event_id1 % 32); +- if (sdmac->event_id1 > 31) +- __set_bit(31, &sdmac->watermark_level); +- sdmac->event_mask[0] = BIT(sdmac->event_id0 % 32); +- if (sdmac->event_id0 > 31) +- __set_bit(30, &sdmac->watermark_level); ++ if (sdmac->event_id0 > 31) { ++ sdmac->event_mask[0] |= 0; ++ __set_bit(28, &sdmac->watermark_level); ++ sdmac->event_mask[1] |= ++ BIT(sdmac->event_id0 % 32); ++ } else { ++ sdmac->event_mask[1] |= 0; ++ sdmac->event_mask[0] |= ++ BIT(sdmac->event_id0 % 32); ++ } ++ if (sdmac->event_id1 > 31) { ++ sdmac->event_mask[0] |= 0; ++ __set_bit(29, &sdmac->watermark_level); ++ sdmac->event_mask[1] |= ++ BIT(sdmac->event_id1 % 32); ++ } else { ++ sdmac->event_mask[1] |= 0; ++ sdmac->event_mask[0] |= ++ BIT(sdmac->event_id1 % 32); ++ } ++ /* BIT 11: ++ * 1 : Source on SPBA ++ * 0 : Source on AIPS ++ */ ++ __set_bit(11, &sdmac->watermark_level); ++ /* BIT 12: ++ * 1 : Destination on SPBA ++ * 0 : Destination on AIPS ++ */ ++ __set_bit(12, &sdmac->watermark_level); ++ __set_bit(31, &sdmac->watermark_level); ++ /* BIT 31: ++ * 1 : Amount of samples to be transferred is ++ * unknown and script will keep on transferring ++ * samples as long as both events are detected ++ * and script must be manually stopped by the ++ * application. ++ * 0 : The amount of samples to be is equal to ++ * the count field of mode word ++ * */ ++ __set_bit(25, &sdmac->watermark_level); ++ __clear_bit(24, &sdmac->watermark_level); + } else { +- __set_bit(sdmac->event_id0, sdmac->event_mask); ++ if (sdmac->event_id0 > 31) { ++ sdmac->event_mask[0] = 0; ++ sdmac->event_mask[1] |= ++ BIT(sdmac->event_id0 % 32); ++ } else { ++ sdmac->event_mask[0] |= ++ BIT(sdmac->event_id0 % 32); ++ sdmac->event_mask[1] = 0; ++ } + } + /* Watermark Level */ + sdmac->watermark_level |= sdmac->watermark_level; + /* Address */ +- sdmac->shp_addr = sdmac->per_address; ++ if (sdmac->direction == DMA_DEV_TO_DEV) { ++ sdmac->shp_addr = sdmac->per_address2; ++ sdmac->per_addr = sdmac->per_address; ++ } else if (sdmac->direction == DMA_TRANS_NONE) { ++ if (sdmac->peripheral_type != IMX_DMATYPE_HDMI || ++ !data->data_addr1 || !data->data_addr2) ++ return -EINVAL; ++ sdmac->data_addr1 = *(u32 *)data->data_addr1; ++ sdmac->data_addr2 = *(u32 *)data->data_addr2; ++ sdmac->watermark_level = 0; ++ } else { ++ sdmac->shp_addr = sdmac->per_address; ++ } + } else { + sdmac->watermark_level = 0; /* FIXME: M3_BASE_ADDRESS */ + } +@@ -906,10 +1026,15 @@ + int channel = sdmac->channel; + int ret = -EBUSY; + +- sdmac->bd = dma_alloc_coherent(NULL, PAGE_SIZE, &sdmac->bd_phys, GFP_KERNEL); ++ sdmac->bd_iram = true; ++ sdmac->bd = gen_pool_dma_alloc(sdma->iram_pool, PAGE_SIZE, &sdmac->bd_phys); + if (!sdmac->bd) { +- ret = -ENOMEM; +- goto out; ++ sdmac->bd_iram = false; ++ sdmac->bd = dma_alloc_coherent(NULL, PAGE_SIZE, &sdmac->bd_phys, GFP_KERNEL); ++ if (!sdmac->bd) { ++ ret = -ENOMEM; ++ goto out; ++ } + } + + memset(sdmac->bd, 0, PAGE_SIZE); +@@ -967,7 +1092,8 @@ + } + + sdmac->peripheral_type = data->peripheral_type; +- sdmac->event_id0 = data->dma_request; ++ sdmac->event_id0 = data->dma_request0; ++ sdmac->event_id1 = data->dma_request1; + + clk_enable(sdmac->sdma->clk_ipg); + clk_enable(sdmac->sdma->clk_ahb); +@@ -985,6 +1111,9 @@ + /* txd.flags will be overwritten in prep funcs */ + sdmac->desc.flags = DMA_CTRL_ACK; + ++ /* Set SDMA channel mode to unvalid to avoid misconfig */ ++ sdmac->mode = SDMA_MODE_INVALID; ++ + return 0; + } + +@@ -1005,7 +1134,10 @@ + + sdma_set_channel_priority(sdmac, 0); + +- dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); ++ if (sdmac->bd_iram) ++ gen_pool_free(sdma->iram_pool, (unsigned long)sdmac->bd, PAGE_SIZE); ++ else ++ dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); + + clk_disable(sdma->clk_ipg); + clk_disable(sdma->clk_ahb); +@@ -1026,7 +1158,7 @@ + return NULL; + sdmac->status = DMA_IN_PROGRESS; + +- sdmac->flags = 0; ++ sdmac->mode = SDMA_MODE_NORMAL; + + sdmac->buf_tail = 0; + +@@ -1119,9 +1251,9 @@ + { + struct sdma_channel *sdmac = to_sdma_chan(chan); + struct sdma_engine *sdma = sdmac->sdma; +- int num_periods = buf_len / period_len; + int channel = sdmac->channel; + int ret, i = 0, buf = 0; ++ int num_periods; + + dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); + +@@ -1131,13 +1263,35 @@ + sdmac->status = DMA_IN_PROGRESS; + + sdmac->buf_tail = 0; ++ sdmac->period_len = period_len; + +- sdmac->flags |= IMX_DMA_SG_LOOP; + sdmac->direction = direction; ++ ++ switch (sdmac->direction) { ++ case DMA_DEV_TO_DEV: ++ sdmac->mode = SDMA_MODE_P2P; ++ break; ++ case DMA_TRANS_NONE: ++ sdmac->mode = SDMA_MODE_NO_BD; ++ break; ++ case DMA_MEM_TO_DEV: ++ case DMA_DEV_TO_MEM: ++ sdmac->mode = SDMA_MODE_LOOP; ++ break; ++ default: ++ dev_err(sdma->dev, "invalid SDMA direction %d\n", direction); ++ return NULL; ++ } ++ + ret = sdma_load_context(sdmac); + if (ret) + goto err_out; + ++ if (period_len) ++ num_periods = buf_len / period_len; ++ else ++ return &sdmac->desc; ++ + if (num_periods > NUM_BD) { + dev_err(sdma->dev, "SDMA channel %d: maximum number of sg exceeded: %d > %d\n", + channel, num_periods, NUM_BD); +@@ -1202,18 +1356,31 @@ + sdma_disable_channel(sdmac); + return 0; + case DMA_SLAVE_CONFIG: +- if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { ++ if (dmaengine_cfg->direction == DMA_DEV_TO_DEV) { ++ sdmac->per_address = dmaengine_cfg->src_addr; ++ sdmac->per_address2 = dmaengine_cfg->dst_addr; ++ sdmac->watermark_level = 0; ++ sdmac->watermark_level |= ++ dmaengine_cfg->src_maxburst; ++ sdmac->watermark_level |= ++ dmaengine_cfg->dst_maxburst << 16; ++ sdmac->word_size = dmaengine_cfg->dst_addr_width; ++ } else if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + sdmac->per_address = dmaengine_cfg->src_addr; + sdmac->watermark_level = dmaengine_cfg->src_maxburst * + dmaengine_cfg->src_addr_width; + sdmac->word_size = dmaengine_cfg->src_addr_width; +- } else { ++ } else if (dmaengine_cfg->direction == DMA_MEM_TO_DEV) { + sdmac->per_address = dmaengine_cfg->dst_addr; + sdmac->watermark_level = dmaengine_cfg->dst_maxburst * + dmaengine_cfg->dst_addr_width; + sdmac->word_size = dmaengine_cfg->dst_addr_width; + } + sdmac->direction = dmaengine_cfg->direction; ++ if (dmaengine_cfg->dma_request0) ++ sdmac->event_id0 = dmaengine_cfg->dma_request0; ++ if (dmaengine_cfg->dma_request1) ++ sdmac->event_id1 = dmaengine_cfg->dma_request1; + return sdma_config_channel(sdmac); + default: + return -ENOSYS; +@@ -1227,9 +1394,15 @@ + struct dma_tx_state *txstate) + { + struct sdma_channel *sdmac = to_sdma_chan(chan); ++ u32 residue; ++ ++ if (sdmac->mode & SDMA_MODE_LOOP) ++ residue = (sdmac->num_bd - sdmac->buf_tail) * sdmac->period_len; ++ else ++ residue = sdmac->chn_count - sdmac->chn_real_count; + + dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, +- sdmac->chn_count - sdmac->chn_real_count); ++ residue); + + return sdmac->status; + } +@@ -1285,7 +1458,10 @@ + goto err_firmware; + switch (header->version_major) { + case 1: +- sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; ++ if (header->version_minor > 0) ++ sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2; ++ else ++ sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; + break; + case 2: + sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2; +@@ -1331,7 +1507,7 @@ + + static int __init sdma_init(struct sdma_engine *sdma) + { +- int i, ret; ++ int i, ret, ccbsize; + dma_addr_t ccb_phys; + + clk_enable(sdma->clk_ipg); +@@ -1340,14 +1516,17 @@ + /* Be sure SDMA has not started yet */ + writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); + +- sdma->channel_control = dma_alloc_coherent(NULL, +- MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + +- sizeof(struct sdma_context_data), +- &ccb_phys, GFP_KERNEL); ++ ccbsize = MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) ++ + sizeof(struct sdma_context_data); + ++ sdma->channel_control = gen_pool_dma_alloc(sdma->iram_pool, ccbsize, &ccb_phys); + if (!sdma->channel_control) { +- ret = -ENOMEM; +- goto err_dma_alloc; ++ sdma->channel_control = dma_alloc_coherent(NULL, ccbsize, ++ &ccb_phys, GFP_KERNEL); ++ if (!sdma->channel_control) { ++ ret = -ENOMEM; ++ goto err_dma_alloc; ++ } + } + + sdma->context = (void *)sdma->channel_control + +@@ -1422,9 +1601,10 @@ + if (dma_spec->args_count != 3) + return NULL; + +- data.dma_request = dma_spec->args[0]; ++ data.dma_request0 = dma_spec->args[0]; + data.peripheral_type = dma_spec->args[1]; + data.priority = dma_spec->args[2]; ++ data.dma_request1 = 0; + + return dma_request_channel(mask, sdma_filter_fn, &data); + } +@@ -1542,6 +1722,11 @@ + &sdma->dma_device.channels); + } + ++ if (np) ++ sdma->iram_pool = of_get_named_gen_pool(np, "iram", 0); ++ if (!sdma->iram_pool) ++ dev_warn(&pdev->dev, "no iram assigned, using external mem\n"); ++ + ret = sdma_init(sdma); + if (ret) + goto err_init; +diff -Nur linux-3.14.36/drivers/dma/Kconfig linux-openelec/drivers/dma/Kconfig +--- linux-3.14.36/drivers/dma/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/dma/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -137,6 +137,19 @@ + To avoid bloating the irq_desc[] array we allocate a sufficient + number of IRQ slots and map them dynamically to specific sources. + ++config MXC_PXP_V2 ++ bool "MXC PxP V2 support" ++ depends on ARM ++ select DMA_ENGINE ++ help ++ Support the PxP (Pixel Pipeline) on i.MX6 DualLite and i.MX6 SoloLite. ++ If unsure, select N. ++ ++config MXC_PXP_CLIENT_DEVICE ++ bool "MXC PxP Client Device" ++ default y ++ depends on MXC_PXP_V2 ++ + config TXX9_DMAC + tristate "Toshiba TXx9 SoC DMA support" + depends on MACH_TX49XX || MACH_TX39XX +diff -Nur linux-3.14.36/drivers/dma/Makefile linux-openelec/drivers/dma/Makefile +--- linux-3.14.36/drivers/dma/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/dma/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -18,6 +18,7 @@ + obj-$(CONFIG_DW_DMAC_CORE) += dw/ + obj-$(CONFIG_AT_HDMAC) += at_hdmac.o + obj-$(CONFIG_MX3_IPU) += ipu/ ++obj-$(CONFIG_MXC_PXP_V2) += pxp/ + obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o + obj-$(CONFIG_SH_DMAE_BASE) += sh/ + obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o +diff -Nur linux-3.14.36/drivers/dma/pxp/Makefile linux-openelec/drivers/dma/pxp/Makefile +--- linux-3.14.36/drivers/dma/pxp/Makefile 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/dma/pxp/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_MXC_PXP_V2) += pxp_dma_v2.o ++obj-$(CONFIG_MXC_PXP_CLIENT_DEVICE) += pxp_device.o +diff -Nur linux-3.14.36/drivers/dma/pxp/pxp_device.c linux-openelec/drivers/dma/pxp/pxp_device.c +--- linux-3.14.36/drivers/dma/pxp/pxp_device.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/dma/pxp/pxp_device.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,765 @@ ++/* ++ * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BUFFER_HASH_ORDER 4 ++ ++static struct pxp_buffer_hash bufhash; ++static struct pxp_irq_info irq_info[NR_PXP_VIRT_CHANNEL]; ++ ++static int pxp_ht_create(struct pxp_buffer_hash *hash, int order) ++{ ++ unsigned long i; ++ unsigned long table_size; ++ ++ table_size = 1U << order; ++ ++ hash->order = order; ++ hash->hash_table = kmalloc(sizeof(*hash->hash_table) * table_size, GFP_KERNEL); ++ ++ if (!hash->hash_table) { ++ pr_err("%s: Out of memory for hash table\n", __func__); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < table_size; i++) ++ INIT_HLIST_HEAD(&hash->hash_table[i]); ++ ++ return 0; ++} ++ ++static int pxp_ht_insert_item(struct pxp_buffer_hash *hash, ++ struct pxp_buf_obj *new) ++{ ++ unsigned long hashkey; ++ struct hlist_head *h_list; ++ ++ hashkey = hash_long(new->offset >> PAGE_SHIFT, hash->order); ++ h_list = &hash->hash_table[hashkey]; ++ ++ spin_lock(&hash->hash_lock); ++ hlist_add_head_rcu(&new->item, h_list); ++ spin_unlock(&hash->hash_lock); ++ ++ return 0; ++} ++ ++static int pxp_ht_remove_item(struct pxp_buffer_hash *hash, ++ struct pxp_buf_obj *obj) ++{ ++ spin_lock(&hash->hash_lock); ++ hlist_del_init_rcu(&obj->item); ++ spin_unlock(&hash->hash_lock); ++ return 0; ++} ++ ++static struct hlist_node *pxp_ht_find_key(struct pxp_buffer_hash *hash, ++ unsigned long key) ++{ ++ struct pxp_buf_obj *entry; ++ struct hlist_head *h_list; ++ unsigned long hashkey; ++ ++ hashkey = hash_long(key, hash->order); ++ h_list = &hash->hash_table[hashkey]; ++ ++ hlist_for_each_entry_rcu(entry, h_list, item) { ++ if (entry->offset >> PAGE_SHIFT == key) ++ return &entry->item; ++ } ++ ++ return NULL; ++} ++ ++static void pxp_ht_destroy(struct pxp_buffer_hash *hash) ++{ ++ kfree(hash->hash_table); ++ hash->hash_table = NULL; ++} ++ ++static int pxp_buffer_handle_create(struct pxp_file *file_priv, ++ struct pxp_buf_obj *obj, ++ uint32_t *handlep) ++{ ++ int ret; ++ ++ idr_preload(GFP_KERNEL); ++ spin_lock(&file_priv->buffer_lock); ++ ++ ret = idr_alloc(&file_priv->buffer_idr, obj, 1, 0, GFP_NOWAIT); ++ ++ spin_unlock(&file_priv->buffer_lock); ++ idr_preload_end(); ++ ++ if (ret < 0) ++ return ret; ++ ++ *handlep = ret; ++ ++ return 0; ++} ++ ++static struct pxp_buf_obj * ++pxp_buffer_object_lookup(struct pxp_file *file_priv, ++ uint32_t handle) ++{ ++ struct pxp_buf_obj *obj; ++ ++ spin_lock(&file_priv->buffer_lock); ++ ++ obj = idr_find(&file_priv->buffer_idr, handle); ++ if (!obj) { ++ spin_unlock(&file_priv->buffer_lock); ++ return NULL; ++ } ++ ++ spin_unlock(&file_priv->buffer_lock); ++ ++ return obj; ++} ++ ++static int pxp_buffer_handle_delete(struct pxp_file *file_priv, ++ uint32_t handle) ++{ ++ struct pxp_buf_obj *obj; ++ ++ spin_lock(&file_priv->buffer_lock); ++ ++ obj = idr_find(&file_priv->buffer_idr, handle); ++ if (!obj) { ++ spin_unlock(&file_priv->buffer_lock); ++ return -EINVAL; ++ } ++ ++ idr_remove(&file_priv->buffer_idr, handle); ++ spin_unlock(&file_priv->buffer_lock); ++ ++ return 0; ++} ++ ++static int pxp_channel_handle_create(struct pxp_file *file_priv, ++ struct pxp_chan_obj *obj, ++ uint32_t *handlep) ++{ ++ int ret; ++ ++ idr_preload(GFP_KERNEL); ++ spin_lock(&file_priv->channel_lock); ++ ++ ret = idr_alloc(&file_priv->channel_idr, obj, 0, 0, GFP_NOWAIT); ++ ++ spin_unlock(&file_priv->channel_lock); ++ idr_preload_end(); ++ ++ if (ret < 0) ++ return ret; ++ ++ *handlep = ret; ++ ++ return 0; ++} ++ ++static struct pxp_chan_obj * ++pxp_channel_object_lookup(struct pxp_file *file_priv, ++ uint32_t handle) ++{ ++ struct pxp_chan_obj *obj; ++ ++ spin_lock(&file_priv->channel_lock); ++ ++ obj = idr_find(&file_priv->channel_idr, handle); ++ if (!obj) { ++ spin_unlock(&file_priv->channel_lock); ++ return NULL; ++ } ++ ++ spin_unlock(&file_priv->channel_lock); ++ ++ return obj; ++} ++ ++static int pxp_channel_handle_delete(struct pxp_file *file_priv, ++ uint32_t handle) ++{ ++ struct pxp_chan_obj *obj; ++ ++ spin_lock(&file_priv->channel_lock); ++ ++ obj = idr_find(&file_priv->channel_idr, handle); ++ if (!obj) { ++ spin_unlock(&file_priv->channel_lock); ++ return -EINVAL; ++ } ++ ++ idr_remove(&file_priv->channel_idr, handle); ++ spin_unlock(&file_priv->channel_lock); ++ ++ return 0; ++} ++ ++static int pxp_alloc_dma_buffer(struct pxp_buf_obj *obj) ++{ ++ obj->virtual = dma_alloc_coherent(NULL, PAGE_ALIGN(obj->size), ++ (dma_addr_t *) (&obj->offset), ++ GFP_DMA | GFP_KERNEL); ++ pr_debug("[ALLOC] mem alloc phys_addr = 0x%lx\n", obj->offset); ++ ++ if (obj->virtual == NULL) { ++ printk(KERN_ERR "Physical memory allocation error!\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void pxp_free_dma_buffer(struct pxp_buf_obj *obj) ++{ ++ if (obj->virtual != NULL) { ++ dma_free_coherent(0, PAGE_ALIGN(obj->size), ++ obj->virtual, (dma_addr_t)obj->offset); ++ } ++} ++ ++static int ++pxp_buffer_object_free(int id, void *ptr, void *data) ++{ ++ struct pxp_file *file_priv = data; ++ struct pxp_buf_obj *obj = ptr; ++ int ret; ++ ++ ret = pxp_buffer_handle_delete(file_priv, obj->handle); ++ if (ret < 0) ++ return ret; ++ ++ pxp_ht_remove_item(&bufhash, obj); ++ pxp_free_dma_buffer(obj); ++ kfree(obj); ++ ++ return 0; ++} ++ ++static int ++pxp_channel_object_free(int id, void *ptr, void *data) ++{ ++ struct pxp_file *file_priv = data; ++ struct pxp_chan_obj *obj = ptr; ++ int chan_id; ++ ++ chan_id = obj->chan->chan_id; ++ wait_event(irq_info[chan_id].waitq, ++ atomic_read(&irq_info[chan_id].irq_pending) == 0); ++ ++ pxp_channel_handle_delete(file_priv, obj->handle); ++ dma_release_channel(obj->chan); ++ kfree(obj); ++ ++ return 0; ++} ++ ++static void pxp_free_buffers(struct pxp_file *file_priv) ++{ ++ idr_for_each(&file_priv->buffer_idr, ++ &pxp_buffer_object_free, file_priv); ++ idr_destroy(&file_priv->buffer_idr); ++} ++ ++static void pxp_free_channels(struct pxp_file *file_priv) ++{ ++ idr_for_each(&file_priv->channel_idr, ++ &pxp_channel_object_free, file_priv); ++ idr_destroy(&file_priv->channel_idr); ++} ++ ++/* Callback function triggered after PxP receives an EOF interrupt */ ++static void pxp_dma_done(void *arg) ++{ ++ struct pxp_tx_desc *tx_desc = to_tx_desc(arg); ++ struct dma_chan *chan = tx_desc->txd.chan; ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ int chan_id = pxp_chan->dma_chan.chan_id; ++ ++ pr_debug("DMA Done ISR, chan_id %d\n", chan_id); ++ ++ atomic_dec(&irq_info[chan_id].irq_pending); ++ irq_info[chan_id].hist_status = tx_desc->hist_status; ++ ++ wake_up(&(irq_info[chan_id].waitq)); ++} ++ ++static int pxp_ioc_config_chan(struct pxp_file *priv, unsigned long arg) ++{ ++ struct scatterlist sg[3]; ++ struct pxp_tx_desc *desc; ++ struct dma_async_tx_descriptor *txd; ++ struct pxp_config_data pxp_conf; ++ dma_cookie_t cookie; ++ int handle, chan_id; ++ int i, length, ret; ++ struct dma_chan *chan; ++ struct pxp_chan_obj *obj; ++ ++ ret = copy_from_user(&pxp_conf, ++ (struct pxp_config_data *)arg, ++ sizeof(struct pxp_config_data)); ++ if (ret) ++ return -EFAULT; ++ ++ handle = pxp_conf.handle; ++ obj = pxp_channel_object_lookup(priv, handle); ++ if (!obj) ++ return -EINVAL; ++ chan = obj->chan; ++ chan_id = chan->chan_id; ++ ++ sg_init_table(sg, 3); ++ ++ txd = chan->device->device_prep_slave_sg(chan, ++ sg, 3, ++ DMA_TO_DEVICE, ++ DMA_PREP_INTERRUPT, ++ NULL); ++ if (!txd) { ++ pr_err("Error preparing a DMA transaction descriptor.\n"); ++ return -EIO; ++ } ++ ++ txd->callback_param = txd; ++ txd->callback = pxp_dma_done; ++ ++ desc = to_tx_desc(txd); ++ ++ length = desc->len; ++ for (i = 0; i < length; i++) { ++ if (i == 0) { /* S0 */ ++ memcpy(&desc->proc_data, ++ &pxp_conf.proc_data, ++ sizeof(struct pxp_proc_data)); ++ memcpy(&desc->layer_param.s0_param, ++ &pxp_conf.s0_param, ++ sizeof(struct pxp_layer_param)); ++ } else if (i == 1) { /* Output */ ++ memcpy(&desc->layer_param.out_param, ++ &pxp_conf.out_param, ++ sizeof(struct pxp_layer_param)); ++ } else { ++ /* OverLay */ ++ memcpy(&desc->layer_param.ol_param, ++ &pxp_conf.ol_param, ++ sizeof(struct pxp_layer_param)); ++ } ++ ++ desc = desc->next; ++ } ++ ++ cookie = txd->tx_submit(txd); ++ if (cookie < 0) { ++ pr_err("Error tx_submit\n"); ++ return -EIO; ++ } ++ ++ atomic_inc(&irq_info[chan_id].irq_pending); ++ ++ return 0; ++} ++ ++static int pxp_device_open(struct inode *inode, struct file *filp) ++{ ++ struct pxp_file *priv; ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ ++ if (!priv) ++ return -ENOMEM; ++ ++ filp->private_data = priv; ++ priv->filp = filp; ++ ++ idr_init(&priv->buffer_idr); ++ spin_lock_init(&priv->buffer_lock); ++ ++ idr_init(&priv->channel_idr); ++ spin_lock_init(&priv->channel_lock); ++ ++ return 0; ++} ++ ++static int pxp_device_release(struct inode *inode, struct file *filp) ++{ ++ struct pxp_file *priv = filp->private_data; ++ ++ if (priv) { ++ pxp_free_channels(priv); ++ pxp_free_buffers(priv); ++ kfree(priv); ++ filp->private_data = NULL; ++ } ++ ++ return 0; ++} ++ ++static int pxp_device_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int request_size; ++ struct hlist_node *node; ++ struct pxp_buf_obj *obj; ++ ++ request_size = vma->vm_end - vma->vm_start; ++ ++ pr_debug("start=0x%x, pgoff=0x%x, size=0x%x\n", ++ (unsigned int)(vma->vm_start), (unsigned int)(vma->vm_pgoff), ++ request_size); ++ ++ node = pxp_ht_find_key(&bufhash, vma->vm_pgoff); ++ if (!node) ++ return -EINVAL; ++ ++ obj = list_entry(node, struct pxp_buf_obj, item); ++ if (obj->offset + (obj->size >> PAGE_SHIFT) < ++ (vma->vm_pgoff + vma_pages(vma))) ++ return -ENOMEM; ++ ++ switch (obj->mem_type) { ++ case MEMORY_TYPE_UNCACHED: ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ break; ++ case MEMORY_TYPE_WC: ++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); ++ break; ++ case MEMORY_TYPE_CACHED: ++ break; ++ default: ++ pr_err("%s: invalid memory type!\n", __func__); ++ return -EINVAL; ++ } ++ ++ return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, ++ request_size, vma->vm_page_prot) ? -EAGAIN : 0; ++} ++ ++static bool chan_filter(struct dma_chan *chan, void *arg) ++{ ++ if (imx_dma_is_pxp(chan)) ++ return true; ++ else ++ return false; ++} ++ ++static long pxp_device_ioctl(struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ struct pxp_file *file_priv = filp->private_data; ++ ++ switch (cmd) { ++ case PXP_IOC_GET_CHAN: ++ { ++ int ret; ++ struct dma_chan *chan = NULL; ++ dma_cap_mask_t mask; ++ struct pxp_chan_obj *obj = NULL; ++ ++ pr_debug("drv: PXP_IOC_GET_CHAN Line %d\n", __LINE__); ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ dma_cap_set(DMA_PRIVATE, mask); ++ ++ chan = dma_request_channel(mask, chan_filter, NULL); ++ if (!chan) { ++ pr_err("Unsccessfully received channel!\n"); ++ return -EBUSY; ++ } ++ ++ pr_debug("Successfully received channel." ++ "chan_id %d\n", chan->chan_id); ++ ++ obj = kzalloc(sizeof(*obj), GFP_KERNEL); ++ if (!obj) { ++ dma_release_channel(chan); ++ return -ENOMEM; ++ } ++ obj->chan = chan; ++ ++ ret = pxp_channel_handle_create(file_priv, obj, ++ &obj->handle); ++ if (ret) { ++ dma_release_channel(chan); ++ kfree(obj); ++ return ret; ++ } ++ ++ init_waitqueue_head(&(irq_info[chan->chan_id].waitq)); ++ if (put_user(obj->handle, (u32 __user *) arg)) { ++ pxp_channel_handle_delete(file_priv, obj->handle); ++ dma_release_channel(chan); ++ kfree(obj); ++ return -EFAULT; ++ } ++ ++ break; ++ } ++ case PXP_IOC_PUT_CHAN: ++ { ++ int handle; ++ struct pxp_chan_obj *obj; ++ ++ if (get_user(handle, (u32 __user *) arg)) ++ return -EFAULT; ++ ++ pr_debug("%d release handle %d\n", __LINE__, handle); ++ ++ obj = pxp_channel_object_lookup(file_priv, handle); ++ if (!obj) ++ return -EINVAL; ++ ++ pxp_channel_handle_delete(file_priv, obj->handle); ++ dma_release_channel(obj->chan); ++ kfree(obj); ++ ++ break; ++ } ++ case PXP_IOC_CONFIG_CHAN: ++ { ++ int ret; ++ ++ ret = pxp_ioc_config_chan(file_priv, arg); ++ if (ret) ++ return ret; ++ ++ break; ++ } ++ case PXP_IOC_START_CHAN: ++ { ++ int handle; ++ struct pxp_chan_obj *obj = NULL; ++ ++ if (get_user(handle, (u32 __user *) arg)) ++ return -EFAULT; ++ ++ obj = pxp_channel_object_lookup(file_priv, handle); ++ if (!obj) ++ return -EINVAL; ++ ++ dma_async_issue_pending(obj->chan); ++ ++ break; ++ } ++ case PXP_IOC_GET_PHYMEM: ++ { ++ struct pxp_mem_desc buffer; ++ struct pxp_buf_obj *obj; ++ ++ ret = copy_from_user(&buffer, ++ (struct pxp_mem_desc *)arg, ++ sizeof(struct pxp_mem_desc)); ++ if (ret) ++ return -EFAULT; ++ ++ pr_debug("[ALLOC] mem alloc size = 0x%x\n", ++ buffer.size); ++ ++ obj = kzalloc(sizeof(*obj), GFP_KERNEL); ++ if (!obj) ++ return -ENOMEM; ++ obj->size = buffer.size; ++ obj->mem_type = buffer.mtype; ++ ++ ret = pxp_alloc_dma_buffer(obj); ++ if (ret == -1) { ++ printk(KERN_ERR ++ "Physical memory allocation error!\n"); ++ kfree(obj); ++ return ret; ++ } ++ ++ ret = pxp_buffer_handle_create(file_priv, obj, &obj->handle); ++ if (ret) { ++ pxp_free_dma_buffer(obj); ++ kfree(obj); ++ return ret; ++ } ++ buffer.handle = obj->handle; ++ buffer.phys_addr = obj->offset; ++ ++ ret = copy_to_user((void __user *)arg, &buffer, ++ sizeof(struct pxp_mem_desc)); ++ if (ret) { ++ pxp_buffer_handle_delete(file_priv, buffer.handle); ++ pxp_free_dma_buffer(obj); ++ kfree(obj); ++ return -EFAULT; ++ } ++ ++ pxp_ht_insert_item(&bufhash, obj); ++ ++ break; ++ } ++ case PXP_IOC_PUT_PHYMEM: ++ { ++ struct pxp_mem_desc pxp_mem; ++ struct pxp_buf_obj *obj; ++ ++ ret = copy_from_user(&pxp_mem, ++ (struct pxp_mem_desc *)arg, ++ sizeof(struct pxp_mem_desc)); ++ if (ret) ++ return -EACCES; ++ ++ obj = pxp_buffer_object_lookup(file_priv, pxp_mem.handle); ++ if (!obj) ++ return -EINVAL; ++ ++ ret = pxp_buffer_handle_delete(file_priv, obj->handle); ++ if (ret) ++ return ret; ++ ++ pxp_ht_remove_item(&bufhash, obj); ++ pxp_free_dma_buffer(obj); ++ kfree(obj); ++ ++ break; ++ } ++ case PXP_IOC_FLUSH_PHYMEM: ++ { ++ int ret; ++ struct pxp_mem_flush flush; ++ struct pxp_buf_obj *obj; ++ ++ ret = copy_from_user(&flush, ++ (struct pxp_mem_flush *)arg, ++ sizeof(struct pxp_mem_flush)); ++ if (ret) ++ return -EACCES; ++ ++ obj = pxp_buffer_object_lookup(file_priv, flush.handle); ++ if (!obj) ++ return -EINVAL; ++ ++ switch (flush.type) { ++ case CACHE_CLEAN: ++ dma_sync_single_for_device(NULL, obj->offset, ++ obj->size, DMA_TO_DEVICE); ++ break; ++ case CACHE_INVALIDATE: ++ dma_sync_single_for_device(NULL, obj->offset, ++ obj->size, DMA_FROM_DEVICE); ++ break; ++ case CACHE_FLUSH: ++ dma_sync_single_for_device(NULL, obj->offset, ++ obj->size, DMA_TO_DEVICE); ++ dma_sync_single_for_device(NULL, obj->offset, ++ obj->size, DMA_FROM_DEVICE); ++ break; ++ default: ++ pr_err("%s: invalid cache flush type\n", __func__); ++ return -EINVAL; ++ } ++ ++ break; ++ } ++ case PXP_IOC_WAIT4CMPLT: ++ { ++ struct pxp_chan_handle chan_handle; ++ int ret, chan_id, handle; ++ struct pxp_chan_obj *obj = NULL; ++ ++ ret = copy_from_user(&chan_handle, ++ (struct pxp_chan_handle *)arg, ++ sizeof(struct pxp_chan_handle)); ++ if (ret) ++ return -EFAULT; ++ ++ handle = chan_handle.handle; ++ obj = pxp_channel_object_lookup(file_priv, handle); ++ if (!obj) ++ return -EINVAL; ++ chan_id = obj->chan->chan_id; ++ ++ ret = wait_event_interruptible ++ (irq_info[chan_id].waitq, ++ (atomic_read(&irq_info[chan_id].irq_pending) == 0)); ++ if (ret < 0) { ++ printk(KERN_WARNING ++ "WAIT4CMPLT: signal received.\n"); ++ return -ERESTARTSYS; ++ } ++ ++ chan_handle.hist_status = irq_info[chan_id].hist_status; ++ ret = copy_to_user((struct pxp_chan_handle *)arg, ++ &chan_handle, ++ sizeof(struct pxp_chan_handle)); ++ if (ret) ++ return -EFAULT; ++ break; ++ } ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static const struct file_operations pxp_device_fops = { ++ .open = pxp_device_open, ++ .release = pxp_device_release, ++ .unlocked_ioctl = pxp_device_ioctl, ++ .mmap = pxp_device_mmap, ++}; ++ ++static struct miscdevice pxp_device_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "pxp_device", ++ .fops = &pxp_device_fops, ++}; ++ ++int register_pxp_device(void) ++{ ++ int ret; ++ ++ ret = misc_register(&pxp_device_miscdev); ++ if (ret) ++ return ret; ++ ++ ret = pxp_ht_create(&bufhash, BUFFER_HASH_ORDER); ++ if (ret) ++ return ret; ++ spin_lock_init(&(bufhash.hash_lock)); ++ ++ pr_debug("PxP_Device registered Successfully\n"); ++ return 0; ++} ++ ++void unregister_pxp_device(void) ++{ ++ pxp_ht_destroy(&bufhash); ++ misc_deregister(&pxp_device_miscdev); ++} +diff -Nur linux-3.14.36/drivers/dma/pxp/pxp_dma_v2.c linux-openelec/drivers/dma/pxp/pxp_dma_v2.c +--- linux-3.14.36/drivers/dma/pxp/pxp_dma_v2.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/dma/pxp/pxp_dma_v2.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1854 @@ ++/* ++ * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++/* ++ * Based on STMP378X PxP driver ++ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "regs-pxp_v2.h" ++ ++#define PXP_DOWNSCALE_THRESHOLD 0x4000 ++ ++static LIST_HEAD(head); ++static int timeout_in_ms = 600; ++static unsigned int block_size; ++static struct kmem_cache *tx_desc_cache; ++ ++struct pxp_dma { ++ struct dma_device dma; ++}; ++ ++struct pxps { ++ struct platform_device *pdev; ++ struct clk *clk; ++ void __iomem *base; ++ int irq; /* PXP IRQ to the CPU */ ++ ++ spinlock_t lock; ++ struct mutex clk_mutex; ++ int clk_stat; ++#define CLK_STAT_OFF 0 ++#define CLK_STAT_ON 1 ++ int pxp_ongoing; ++ int lut_state; ++ ++ struct device *dev; ++ struct pxp_dma pxp_dma; ++ struct pxp_channel channel[NR_PXP_VIRT_CHANNEL]; ++ struct work_struct work; ++ ++ /* describes most recent processing configuration */ ++ struct pxp_config_data pxp_conf_state; ++ ++ /* to turn clock off when pxp is inactive */ ++ struct timer_list clk_timer; ++ ++ /* for pxp config dispatch asynchronously*/ ++ struct task_struct *dispatch; ++ wait_queue_head_t thread_waitq; ++ struct completion complete; ++}; ++ ++#define to_pxp_dma(d) container_of(d, struct pxp_dma, dma) ++#define to_tx_desc(tx) container_of(tx, struct pxp_tx_desc, txd) ++#define to_pxp_channel(d) container_of(d, struct pxp_channel, dma_chan) ++#define to_pxp(id) container_of(id, struct pxps, pxp_dma) ++ ++#define PXP_DEF_BUFS 2 ++#define PXP_MIN_PIX 8 ++ ++static uint32_t pxp_s0_formats[] = { ++ PXP_PIX_FMT_RGB32, ++ PXP_PIX_FMT_RGB565, ++ PXP_PIX_FMT_RGB555, ++ PXP_PIX_FMT_YUV420P, ++ PXP_PIX_FMT_YUV422P, ++}; ++ ++/* ++ * PXP common functions ++ */ ++static void dump_pxp_reg(struct pxps *pxp) ++{ ++ dev_dbg(pxp->dev, "PXP_CTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CTRL)); ++ dev_dbg(pxp->dev, "PXP_STAT 0x%x", ++ __raw_readl(pxp->base + HW_PXP_STAT)); ++ dev_dbg(pxp->dev, "PXP_OUT_CTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_CTRL)); ++ dev_dbg(pxp->dev, "PXP_OUT_BUF 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_BUF)); ++ dev_dbg(pxp->dev, "PXP_OUT_BUF2 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_BUF2)); ++ dev_dbg(pxp->dev, "PXP_OUT_PITCH 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_PITCH)); ++ dev_dbg(pxp->dev, "PXP_OUT_LRC 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_LRC)); ++ dev_dbg(pxp->dev, "PXP_OUT_PS_ULC 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_PS_ULC)); ++ dev_dbg(pxp->dev, "PXP_OUT_PS_LRC 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_PS_LRC)); ++ dev_dbg(pxp->dev, "PXP_OUT_AS_ULC 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_AS_ULC)); ++ dev_dbg(pxp->dev, "PXP_OUT_AS_LRC 0x%x", ++ __raw_readl(pxp->base + HW_PXP_OUT_AS_LRC)); ++ dev_dbg(pxp->dev, "PXP_PS_CTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_CTRL)); ++ dev_dbg(pxp->dev, "PXP_PS_BUF 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_BUF)); ++ dev_dbg(pxp->dev, "PXP_PS_UBUF 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_UBUF)); ++ dev_dbg(pxp->dev, "PXP_PS_VBUF 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_VBUF)); ++ dev_dbg(pxp->dev, "PXP_PS_PITCH 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_PITCH)); ++ dev_dbg(pxp->dev, "PXP_PS_BACKGROUND 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_BACKGROUND)); ++ dev_dbg(pxp->dev, "PXP_PS_SCALE 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_SCALE)); ++ dev_dbg(pxp->dev, "PXP_PS_OFFSET 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_OFFSET)); ++ dev_dbg(pxp->dev, "PXP_PS_CLRKEYLOW 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_CLRKEYLOW)); ++ dev_dbg(pxp->dev, "PXP_PS_CLRKEYHIGH 0x%x", ++ __raw_readl(pxp->base + HW_PXP_PS_CLRKEYHIGH)); ++ dev_dbg(pxp->dev, "PXP_AS_CTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_AS_CTRL)); ++ dev_dbg(pxp->dev, "PXP_AS_BUF 0x%x", ++ __raw_readl(pxp->base + HW_PXP_AS_BUF)); ++ dev_dbg(pxp->dev, "PXP_AS_PITCH 0x%x", ++ __raw_readl(pxp->base + HW_PXP_AS_PITCH)); ++ dev_dbg(pxp->dev, "PXP_AS_CLRKEYLOW 0x%x", ++ __raw_readl(pxp->base + HW_PXP_AS_CLRKEYLOW)); ++ dev_dbg(pxp->dev, "PXP_AS_CLRKEYHIGH 0x%x", ++ __raw_readl(pxp->base + HW_PXP_AS_CLRKEYHIGH)); ++ dev_dbg(pxp->dev, "PXP_CSC1_COEF0 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC1_COEF0)); ++ dev_dbg(pxp->dev, "PXP_CSC1_COEF1 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC1_COEF1)); ++ dev_dbg(pxp->dev, "PXP_CSC1_COEF2 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC1_COEF2)); ++ dev_dbg(pxp->dev, "PXP_CSC2_CTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC2_CTRL)); ++ dev_dbg(pxp->dev, "PXP_CSC2_COEF0 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC2_COEF0)); ++ dev_dbg(pxp->dev, "PXP_CSC2_COEF1 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC2_COEF1)); ++ dev_dbg(pxp->dev, "PXP_CSC2_COEF2 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC2_COEF2)); ++ dev_dbg(pxp->dev, "PXP_CSC2_COEF3 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC2_COEF3)); ++ dev_dbg(pxp->dev, "PXP_CSC2_COEF4 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC2_COEF4)); ++ dev_dbg(pxp->dev, "PXP_CSC2_COEF5 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CSC2_COEF5)); ++ dev_dbg(pxp->dev, "PXP_LUT_CTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_LUT_CTRL)); ++ dev_dbg(pxp->dev, "PXP_LUT_ADDR 0x%x", ++ __raw_readl(pxp->base + HW_PXP_LUT_ADDR)); ++ dev_dbg(pxp->dev, "PXP_LUT_DATA 0x%x", ++ __raw_readl(pxp->base + HW_PXP_LUT_DATA)); ++ dev_dbg(pxp->dev, "PXP_LUT_EXTMEM 0x%x", ++ __raw_readl(pxp->base + HW_PXP_LUT_EXTMEM)); ++ dev_dbg(pxp->dev, "PXP_CFA 0x%x", ++ __raw_readl(pxp->base + HW_PXP_CFA)); ++ dev_dbg(pxp->dev, "PXP_HIST_CTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST_CTRL)); ++ dev_dbg(pxp->dev, "PXP_HIST2_PARAM 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST2_PARAM)); ++ dev_dbg(pxp->dev, "PXP_HIST4_PARAM 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST4_PARAM)); ++ dev_dbg(pxp->dev, "PXP_HIST8_PARAM0 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST8_PARAM0)); ++ dev_dbg(pxp->dev, "PXP_HIST8_PARAM1 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST8_PARAM1)); ++ dev_dbg(pxp->dev, "PXP_HIST16_PARAM0 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST16_PARAM0)); ++ dev_dbg(pxp->dev, "PXP_HIST16_PARAM1 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST16_PARAM1)); ++ dev_dbg(pxp->dev, "PXP_HIST16_PARAM2 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST16_PARAM2)); ++ dev_dbg(pxp->dev, "PXP_HIST16_PARAM3 0x%x", ++ __raw_readl(pxp->base + HW_PXP_HIST16_PARAM3)); ++ dev_dbg(pxp->dev, "PXP_POWER 0x%x", ++ __raw_readl(pxp->base + HW_PXP_POWER)); ++ dev_dbg(pxp->dev, "PXP_NEXT 0x%x", ++ __raw_readl(pxp->base + HW_PXP_NEXT)); ++ dev_dbg(pxp->dev, "PXP_DEBUGCTRL 0x%x", ++ __raw_readl(pxp->base + HW_PXP_DEBUGCTRL)); ++ dev_dbg(pxp->dev, "PXP_DEBUG 0x%x", ++ __raw_readl(pxp->base + HW_PXP_DEBUG)); ++ dev_dbg(pxp->dev, "PXP_VERSION 0x%x", ++ __raw_readl(pxp->base + HW_PXP_VERSION)); ++} ++ ++static bool is_yuv(u32 pix_fmt) ++{ ++ if ((pix_fmt == PXP_PIX_FMT_YUYV) | ++ (pix_fmt == PXP_PIX_FMT_UYVY) | ++ (pix_fmt == PXP_PIX_FMT_YVYU) | ++ (pix_fmt == PXP_PIX_FMT_VYUY) | ++ (pix_fmt == PXP_PIX_FMT_Y41P) | ++ (pix_fmt == PXP_PIX_FMT_YUV444) | ++ (pix_fmt == PXP_PIX_FMT_NV12) | ++ (pix_fmt == PXP_PIX_FMT_NV16) | ++ (pix_fmt == PXP_PIX_FMT_NV61) | ++ (pix_fmt == PXP_PIX_FMT_GREY) | ++ (pix_fmt == PXP_PIX_FMT_GY04) | ++ (pix_fmt == PXP_PIX_FMT_YVU410P) | ++ (pix_fmt == PXP_PIX_FMT_YUV410P) | ++ (pix_fmt == PXP_PIX_FMT_YVU420P) | ++ (pix_fmt == PXP_PIX_FMT_YUV420P) | ++ (pix_fmt == PXP_PIX_FMT_YUV420P2) | ++ (pix_fmt == PXP_PIX_FMT_YVU422P) | ++ (pix_fmt == PXP_PIX_FMT_YUV422P)) { ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++static void pxp_set_ctrl(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_proc_data *proc_data = &pxp_conf->proc_data; ++ u32 ctrl; ++ u32 fmt_ctrl; ++ int need_swap = 0; /* to support YUYV and YVYU formats */ ++ ++ /* Configure S0 input format */ ++ switch (pxp_conf->s0_param.pixel_fmt) { ++ case PXP_PIX_FMT_RGB32: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__RGB888; ++ break; ++ case PXP_PIX_FMT_RGB565: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__RGB565; ++ break; ++ case PXP_PIX_FMT_RGB555: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__RGB555; ++ break; ++ case PXP_PIX_FMT_YUV420P: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV420; ++ break; ++ case PXP_PIX_FMT_YVU420P: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV420; ++ break; ++ case PXP_PIX_FMT_GREY: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__Y8; ++ break; ++ case PXP_PIX_FMT_GY04: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__Y4; ++ break; ++ case PXP_PIX_FMT_YUV422P: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV422; ++ break; ++ case PXP_PIX_FMT_UYVY: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__UYVY1P422; ++ break; ++ case PXP_PIX_FMT_YUYV: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__UYVY1P422; ++ need_swap = 1; ++ break; ++ case PXP_PIX_FMT_VYUY: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__VYUY1P422; ++ break; ++ case PXP_PIX_FMT_YVYU: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__VYUY1P422; ++ need_swap = 1; ++ break; ++ case PXP_PIX_FMT_NV12: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV2P420; ++ break; ++ case PXP_PIX_FMT_NV21: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YVU2P420; ++ break; ++ case PXP_PIX_FMT_NV16: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV2P422; ++ break; ++ case PXP_PIX_FMT_NV61: ++ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YVU2P422; ++ break; ++ default: ++ fmt_ctrl = 0; ++ } ++ ++ ctrl = BF_PXP_PS_CTRL_FORMAT(fmt_ctrl) | BF_PXP_PS_CTRL_SWAP(need_swap); ++ __raw_writel(ctrl, pxp->base + HW_PXP_PS_CTRL_SET); ++ ++ /* Configure output format based on out_channel format */ ++ switch (pxp_conf->out_param.pixel_fmt) { ++ case PXP_PIX_FMT_RGB32: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB888; ++ break; ++ case PXP_PIX_FMT_BGRA32: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__ARGB8888; ++ break; ++ case PXP_PIX_FMT_RGB24: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB888P; ++ break; ++ case PXP_PIX_FMT_RGB565: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB565; ++ break; ++ case PXP_PIX_FMT_RGB555: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB555; ++ break; ++ case PXP_PIX_FMT_GREY: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__Y8; ++ break; ++ case PXP_PIX_FMT_GY04: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__Y4; ++ break; ++ case PXP_PIX_FMT_UYVY: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__UYVY1P422; ++ break; ++ case PXP_PIX_FMT_VYUY: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__VYUY1P422; ++ break; ++ case PXP_PIX_FMT_NV12: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YUV2P420; ++ break; ++ case PXP_PIX_FMT_NV21: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YVU2P420; ++ break; ++ case PXP_PIX_FMT_NV16: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YUV2P422; ++ break; ++ case PXP_PIX_FMT_NV61: ++ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YVU2P422; ++ break; ++ default: ++ fmt_ctrl = 0; ++ } ++ ++ ctrl = BF_PXP_OUT_CTRL_FORMAT(fmt_ctrl); ++ __raw_writel(ctrl, pxp->base + HW_PXP_OUT_CTRL); ++ ++ ctrl = 0; ++ if (proc_data->scaling) ++ ; ++ if (proc_data->vflip) ++ ctrl |= BM_PXP_CTRL_VFLIP; ++ if (proc_data->hflip) ++ ctrl |= BM_PXP_CTRL_HFLIP; ++ if (proc_data->rotate) { ++ ctrl |= BF_PXP_CTRL_ROTATE(proc_data->rotate / 90); ++ if (proc_data->rot_pos) ++ ctrl |= BM_PXP_CTRL_ROT_POS; ++ } ++ ++ /* In default, the block size is set to 8x8 ++ * But block size can be set to 16x16 due to ++ * blocksize variable modification ++ */ ++ ctrl |= block_size << 23; ++ ++ __raw_writel(ctrl, pxp->base + HW_PXP_CTRL); ++} ++ ++static int pxp_start(struct pxps *pxp) ++{ ++ __raw_writel(BM_PXP_CTRL_IRQ_ENABLE, pxp->base + HW_PXP_CTRL_SET); ++ __raw_writel(BM_PXP_CTRL_ENABLE, pxp->base + HW_PXP_CTRL_SET); ++ dump_pxp_reg(pxp); ++ ++ return 0; ++} ++ ++static void pxp_set_outbuf(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_layer_param *out_params = &pxp_conf->out_param; ++ ++ __raw_writel(out_params->paddr, pxp->base + HW_PXP_OUT_BUF); ++ ++ __raw_writel(BF_PXP_OUT_LRC_X(out_params->width - 1) | ++ BF_PXP_OUT_LRC_Y(out_params->height - 1), ++ pxp->base + HW_PXP_OUT_LRC); ++ ++ if (out_params->pixel_fmt == PXP_PIX_FMT_RGB24) { ++ __raw_writel(out_params->stride * 3, ++ pxp->base + HW_PXP_OUT_PITCH); ++ } else if (out_params->pixel_fmt == PXP_PIX_FMT_BGRA32 || ++ out_params->pixel_fmt == PXP_PIX_FMT_RGB32) { ++ __raw_writel(out_params->stride << 2, ++ pxp->base + HW_PXP_OUT_PITCH); ++ } else if (out_params->pixel_fmt == PXP_PIX_FMT_RGB565) { ++ __raw_writel(out_params->stride << 1, ++ pxp->base + HW_PXP_OUT_PITCH); ++ } else if (out_params->pixel_fmt == PXP_PIX_FMT_UYVY || ++ (out_params->pixel_fmt == PXP_PIX_FMT_VYUY)) { ++ __raw_writel(out_params->stride << 1, ++ pxp->base + HW_PXP_OUT_PITCH); ++ } else if (out_params->pixel_fmt == PXP_PIX_FMT_GREY || ++ out_params->pixel_fmt == PXP_PIX_FMT_NV12 || ++ out_params->pixel_fmt == PXP_PIX_FMT_NV21 || ++ out_params->pixel_fmt == PXP_PIX_FMT_NV16 || ++ out_params->pixel_fmt == PXP_PIX_FMT_NV61) { ++ __raw_writel(out_params->stride, ++ pxp->base + HW_PXP_OUT_PITCH); ++ } else if (out_params->pixel_fmt == PXP_PIX_FMT_GY04) { ++ __raw_writel(out_params->stride >> 1, ++ pxp->base + HW_PXP_OUT_PITCH); ++ } else { ++ __raw_writel(0, pxp->base + HW_PXP_OUT_PITCH); ++ } ++ ++ /* set global alpha if necessary */ ++ if (out_params->global_alpha_enable) { ++ __raw_writel(out_params->global_alpha << 24, ++ pxp->base + HW_PXP_OUT_CTRL_SET); ++ __raw_writel(BM_PXP_OUT_CTRL_ALPHA_OUTPUT, ++ pxp->base + HW_PXP_OUT_CTRL_SET); ++ } ++} ++ ++static void pxp_set_s0colorkey(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_layer_param *s0_params = &pxp_conf->s0_param; ++ ++ /* Low and high are set equal. V4L does not allow a chromakey range */ ++ if (s0_params->color_key_enable == 0 || s0_params->color_key == -1) { ++ /* disable color key */ ++ __raw_writel(0xFFFFFF, pxp->base + HW_PXP_PS_CLRKEYLOW); ++ __raw_writel(0, pxp->base + HW_PXP_PS_CLRKEYHIGH); ++ } else { ++ __raw_writel(s0_params->color_key, ++ pxp->base + HW_PXP_PS_CLRKEYLOW); ++ __raw_writel(s0_params->color_key, ++ pxp->base + HW_PXP_PS_CLRKEYHIGH); ++ } ++} ++ ++static void pxp_set_olcolorkey(int layer_no, struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_layer_param *ol_params = &pxp_conf->ol_param[layer_no]; ++ ++ /* Low and high are set equal. V4L does not allow a chromakey range */ ++ if (ol_params->color_key_enable != 0 && ol_params->color_key != -1) { ++ __raw_writel(ol_params->color_key, ++ pxp->base + HW_PXP_AS_CLRKEYLOW); ++ __raw_writel(ol_params->color_key, ++ pxp->base + HW_PXP_AS_CLRKEYHIGH); ++ } else { ++ /* disable color key */ ++ __raw_writel(0xFFFFFF, pxp->base + HW_PXP_AS_CLRKEYLOW); ++ __raw_writel(0, pxp->base + HW_PXP_AS_CLRKEYHIGH); ++ } ++} ++ ++static void pxp_set_oln(int layer_no, struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_layer_param *olparams_data = &pxp_conf->ol_param[layer_no]; ++ dma_addr_t phys_addr = olparams_data->paddr; ++ u32 pitch = olparams_data->stride ? olparams_data->stride : ++ olparams_data->width; ++ ++ __raw_writel(phys_addr, pxp->base + HW_PXP_AS_BUF); ++ ++ /* Fixme */ ++ if (olparams_data->width == 0 && olparams_data->height == 0) { ++ __raw_writel(0xffffffff, pxp->base + HW_PXP_OUT_AS_ULC); ++ __raw_writel(0x0, pxp->base + HW_PXP_OUT_AS_LRC); ++ } else { ++ __raw_writel(0x0, pxp->base + HW_PXP_OUT_AS_ULC); ++ if (pxp_conf->proc_data.rotate == 90 || ++ pxp_conf->proc_data.rotate == 270) { ++ if (pxp_conf->proc_data.rot_pos == 1) { ++ __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->height - 1) | ++ BF_PXP_OUT_AS_LRC_Y(olparams_data->width - 1), ++ pxp->base + HW_PXP_OUT_AS_LRC); ++ } else { ++ __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->width - 1) | ++ BF_PXP_OUT_AS_LRC_Y(olparams_data->height - 1), ++ pxp->base + HW_PXP_OUT_AS_LRC); ++ } ++ } else { ++ __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->width - 1) | ++ BF_PXP_OUT_AS_LRC_Y(olparams_data->height - 1), ++ pxp->base + HW_PXP_OUT_AS_LRC); ++ } ++ } ++ ++ if ((olparams_data->pixel_fmt == PXP_PIX_FMT_BGRA32) | ++ (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB32)) { ++ __raw_writel(pitch << 2, ++ pxp->base + HW_PXP_AS_PITCH); ++ } else if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB565) { ++ __raw_writel(pitch << 1, ++ pxp->base + HW_PXP_AS_PITCH); ++ } else { ++ __raw_writel(0, pxp->base + HW_PXP_AS_PITCH); ++ } ++} ++ ++static void pxp_set_olparam(int layer_no, struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_layer_param *olparams_data = &pxp_conf->ol_param[layer_no]; ++ u32 olparam; ++ ++ olparam = BF_PXP_AS_CTRL_ALPHA(olparams_data->global_alpha); ++ if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB32) { ++ olparam |= ++ BF_PXP_AS_CTRL_FORMAT(BV_PXP_AS_CTRL_FORMAT__RGB888); ++ } else if (olparams_data->pixel_fmt == PXP_PIX_FMT_BGRA32) { ++ olparam |= ++ BF_PXP_AS_CTRL_FORMAT(BV_PXP_AS_CTRL_FORMAT__ARGB8888); ++ if (!olparams_data->combine_enable) { ++ olparam |= ++ BF_PXP_AS_CTRL_ALPHA_CTRL ++ (BV_PXP_AS_CTRL_ALPHA_CTRL__ROPs); ++ olparam |= 0x3 << 16; ++ } ++ } else if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB565) { ++ olparam |= ++ BF_PXP_AS_CTRL_FORMAT(BV_PXP_AS_CTRL_FORMAT__RGB565); ++ } ++ if (olparams_data->global_alpha_enable) { ++ if (olparams_data->global_override) { ++ olparam |= ++ BF_PXP_AS_CTRL_ALPHA_CTRL ++ (BV_PXP_AS_CTRL_ALPHA_CTRL__Override); ++ } else { ++ olparam |= ++ BF_PXP_AS_CTRL_ALPHA_CTRL ++ (BV_PXP_AS_CTRL_ALPHA_CTRL__Multiply); ++ } ++ if (olparams_data->alpha_invert) ++ olparam |= BM_PXP_AS_CTRL_ALPHA_INVERT; ++ } ++ if (olparams_data->color_key_enable) ++ olparam |= BM_PXP_AS_CTRL_ENABLE_COLORKEY; ++ ++ __raw_writel(olparam, pxp->base + HW_PXP_AS_CTRL); ++} ++ ++static void pxp_set_s0param(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_proc_data *proc_data = &pxp_conf->proc_data; ++ u32 s0param; ++ ++ /* contains the coordinate for the PS in the OUTPUT buffer. */ ++ if ((pxp_conf->s0_param).width == 0 && ++ (pxp_conf->s0_param).height == 0) { ++ __raw_writel(0xffffffff, pxp->base + HW_PXP_OUT_PS_ULC); ++ __raw_writel(0x0, pxp->base + HW_PXP_OUT_PS_LRC); ++ } else { ++ s0param = BF_PXP_OUT_PS_ULC_X(proc_data->drect.left); ++ s0param |= BF_PXP_OUT_PS_ULC_Y(proc_data->drect.top); ++ __raw_writel(s0param, pxp->base + HW_PXP_OUT_PS_ULC); ++ s0param = BF_PXP_OUT_PS_LRC_X(proc_data->drect.left + ++ proc_data->drect.width - 1); ++ s0param |= BF_PXP_OUT_PS_LRC_Y(proc_data->drect.top + ++ proc_data->drect.height - 1); ++ __raw_writel(s0param, pxp->base + HW_PXP_OUT_PS_LRC); ++ } ++} ++ ++/* crop behavior is re-designed in h/w. */ ++static void pxp_set_s0crop(struct pxps *pxp) ++{ ++ /* ++ * place-holder, it's implemented in other functions in this driver. ++ * Refer to "Clipping source images" section in RM for detail. ++ */ ++} ++ ++static int pxp_set_scaling(struct pxps *pxp) ++{ ++ int ret = 0; ++ u32 xscale, yscale, s0scale; ++ u32 decx, decy, xdec = 0, ydec = 0; ++ struct pxp_proc_data *proc_data = &pxp->pxp_conf_state.proc_data; ++ ++ if (((proc_data->srect.width == proc_data->drect.width) && ++ (proc_data->srect.height == proc_data->drect.height)) || ++ ((proc_data->srect.width == 0) && (proc_data->srect.height == 0))) { ++ proc_data->scaling = 0; ++ __raw_writel(0x10001000, pxp->base + HW_PXP_PS_SCALE); ++ __raw_writel(0, pxp->base + HW_PXP_PS_CTRL); ++ goto out; ++ } ++ ++ proc_data->scaling = 1; ++ decx = proc_data->srect.width / proc_data->drect.width; ++ decy = proc_data->srect.height / proc_data->drect.height; ++ if (decx > 0) { ++ if (decx >= 2 && decx < 4) { ++ decx = 2; ++ xdec = 1; ++ } else if (decx >= 4 && decx < 8) { ++ decx = 4; ++ xdec = 2; ++ } else if (decx >= 8) { ++ decx = 8; ++ xdec = 3; ++ } ++ xscale = proc_data->srect.width * 0x1000 / ++ (proc_data->drect.width * decx); ++ } else ++ xscale = proc_data->srect.width * 0x1000 / ++ proc_data->drect.width; ++ if (decy > 0) { ++ if (decy >= 2 && decy < 4) { ++ decy = 2; ++ ydec = 1; ++ } else if (decy >= 4 && decy < 8) { ++ decy = 4; ++ ydec = 2; ++ } else if (decy >= 8) { ++ decy = 8; ++ ydec = 3; ++ } ++ yscale = proc_data->srect.height * 0x1000 / ++ (proc_data->drect.height * decy); ++ } else ++ yscale = proc_data->srect.height * 0x1000 / ++ proc_data->drect.height; ++ ++ __raw_writel((xdec << 10) | (ydec << 8), pxp->base + HW_PXP_PS_CTRL); ++ ++ if (xscale > PXP_DOWNSCALE_THRESHOLD) ++ xscale = PXP_DOWNSCALE_THRESHOLD; ++ if (yscale > PXP_DOWNSCALE_THRESHOLD) ++ yscale = PXP_DOWNSCALE_THRESHOLD; ++ s0scale = BF_PXP_PS_SCALE_YSCALE(yscale) | ++ BF_PXP_PS_SCALE_XSCALE(xscale); ++ __raw_writel(s0scale, pxp->base + HW_PXP_PS_SCALE); ++ ++out: ++ pxp_set_ctrl(pxp); ++ ++ return ret; ++} ++ ++static void pxp_set_bg(struct pxps *pxp) ++{ ++ __raw_writel(pxp->pxp_conf_state.proc_data.bgcolor, ++ pxp->base + HW_PXP_PS_BACKGROUND); ++} ++ ++static void pxp_set_lut(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ int lut_op = pxp_conf->proc_data.lut_transform; ++ u32 reg_val; ++ int i; ++ bool use_cmap = (lut_op & PXP_LUT_USE_CMAP) ? true : false; ++ u8 *cmap = pxp_conf->proc_data.lut_map; ++ u32 entry_src; ++ u32 pix_val; ++ u8 entry[4]; ++ ++ /* ++ * If LUT already configured as needed, return... ++ * Unless CMAP is needed and it has been updated. ++ */ ++ if ((pxp->lut_state == lut_op) && ++ !(use_cmap && pxp_conf->proc_data.lut_map_updated)) ++ return; ++ ++ if (lut_op == PXP_LUT_NONE) { ++ __raw_writel(BM_PXP_LUT_CTRL_BYPASS, ++ pxp->base + HW_PXP_LUT_CTRL); ++ } else if (((lut_op & PXP_LUT_INVERT) != 0) ++ && ((lut_op & PXP_LUT_BLACK_WHITE) != 0)) { ++ /* Fill out LUT table with inverted monochromized values */ ++ ++ /* clear bypass bit, set lookup mode & out mode */ ++ __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE ++ (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | ++ BF_PXP_LUT_CTRL_OUT_MODE ++ (BV_PXP_LUT_CTRL_OUT_MODE__Y8), ++ pxp->base + HW_PXP_LUT_CTRL); ++ ++ /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ ++ __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); ++ ++ /* LUT address pointer auto-increments after each data write */ ++ for (pix_val = 0; pix_val < 256; pix_val += 4) { ++ for (i = 0; i < 4; i++) { ++ entry_src = use_cmap ? ++ cmap[pix_val + i] : pix_val + i; ++ entry[i] = (entry_src < 0x80) ? 0xFF : 0x00; ++ } ++ reg_val = (entry[3] << 24) | (entry[2] << 16) | ++ (entry[1] << 8) | entry[0]; ++ __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); ++ } ++ } else if ((lut_op & PXP_LUT_INVERT) != 0) { ++ /* Fill out LUT table with 8-bit inverted values */ ++ ++ /* clear bypass bit, set lookup mode & out mode */ ++ __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE ++ (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | ++ BF_PXP_LUT_CTRL_OUT_MODE ++ (BV_PXP_LUT_CTRL_OUT_MODE__Y8), ++ pxp->base + HW_PXP_LUT_CTRL); ++ ++ /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ ++ __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); ++ ++ /* LUT address pointer auto-increments after each data write */ ++ for (pix_val = 0; pix_val < 256; pix_val += 4) { ++ for (i = 0; i < 4; i++) { ++ entry_src = use_cmap ? ++ cmap[pix_val + i] : pix_val + i; ++ entry[i] = ~entry_src & 0xFF; ++ } ++ reg_val = (entry[3] << 24) | (entry[2] << 16) | ++ (entry[1] << 8) | entry[0]; ++ __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); ++ } ++ } else if ((lut_op & PXP_LUT_BLACK_WHITE) != 0) { ++ /* Fill out LUT table with 8-bit monochromized values */ ++ ++ /* clear bypass bit, set lookup mode & out mode */ ++ __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE ++ (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | ++ BF_PXP_LUT_CTRL_OUT_MODE ++ (BV_PXP_LUT_CTRL_OUT_MODE__Y8), ++ pxp->base + HW_PXP_LUT_CTRL); ++ ++ /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ ++ __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); ++ ++ /* LUT address pointer auto-increments after each data write */ ++ for (pix_val = 0; pix_val < 256; pix_val += 4) { ++ for (i = 0; i < 4; i++) { ++ entry_src = use_cmap ? ++ cmap[pix_val + i] : pix_val + i; ++ entry[i] = (entry_src < 0x80) ? 0x00 : 0xFF; ++ } ++ reg_val = (entry[3] << 24) | (entry[2] << 16) | ++ (entry[1] << 8) | entry[0]; ++ __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); ++ } ++ } else if (use_cmap) { ++ /* Fill out LUT table using colormap values */ ++ ++ /* clear bypass bit, set lookup mode & out mode */ ++ __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE ++ (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | ++ BF_PXP_LUT_CTRL_OUT_MODE ++ (BV_PXP_LUT_CTRL_OUT_MODE__Y8), ++ pxp->base + HW_PXP_LUT_CTRL); ++ ++ /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ ++ __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); ++ ++ /* LUT address pointer auto-increments after each data write */ ++ for (pix_val = 0; pix_val < 256; pix_val += 4) { ++ for (i = 0; i < 4; i++) ++ entry[i] = cmap[pix_val + i]; ++ reg_val = (entry[3] << 24) | (entry[2] << 16) | ++ (entry[1] << 8) | entry[0]; ++ __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); ++ } ++ } ++ ++ pxp->lut_state = lut_op; ++} ++ ++static void pxp_set_csc(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_layer_param *s0_params = &pxp_conf->s0_param; ++ struct pxp_layer_param *ol_params = &pxp_conf->ol_param[0]; ++ struct pxp_layer_param *out_params = &pxp_conf->out_param; ++ ++ bool input_is_YUV = is_yuv(s0_params->pixel_fmt); ++ bool output_is_YUV = is_yuv(out_params->pixel_fmt); ++ ++ if (input_is_YUV && output_is_YUV) { ++ /* ++ * Input = YUV, Output = YUV ++ * No CSC unless we need to do combining ++ */ ++ if (ol_params->combine_enable) { ++ /* Must convert to RGB for combining with RGB overlay */ ++ ++ /* CSC1 - YUV->RGB */ ++ __raw_writel(0x04030000, pxp->base + HW_PXP_CSC1_COEF0); ++ __raw_writel(0x01230208, pxp->base + HW_PXP_CSC1_COEF1); ++ __raw_writel(0x076b079c, pxp->base + HW_PXP_CSC1_COEF2); ++ ++ /* CSC2 - RGB->YUV */ ++ __raw_writel(0x4, pxp->base + HW_PXP_CSC2_CTRL); ++ __raw_writel(0x0096004D, pxp->base + HW_PXP_CSC2_COEF0); ++ __raw_writel(0x05DA001D, pxp->base + HW_PXP_CSC2_COEF1); ++ __raw_writel(0x007005B6, pxp->base + HW_PXP_CSC2_COEF2); ++ __raw_writel(0x057C009E, pxp->base + HW_PXP_CSC2_COEF3); ++ __raw_writel(0x000005E6, pxp->base + HW_PXP_CSC2_COEF4); ++ __raw_writel(0x00000000, pxp->base + HW_PXP_CSC2_COEF5); ++ } else { ++ /* Input & Output both YUV, so bypass both CSCs */ ++ ++ /* CSC1 - Bypass */ ++ __raw_writel(0x40000000, pxp->base + HW_PXP_CSC1_COEF0); ++ ++ /* CSC2 - Bypass */ ++ __raw_writel(0x1, pxp->base + HW_PXP_CSC2_CTRL); ++ } ++ } else if (input_is_YUV && !output_is_YUV) { ++ /* ++ * Input = YUV, Output = RGB ++ * Use CSC1 to convert to RGB ++ */ ++ ++ /* CSC1 - YUV->RGB */ ++ __raw_writel(0x84ab01f0, pxp->base + HW_PXP_CSC1_COEF0); ++ __raw_writel(0x01980204, pxp->base + HW_PXP_CSC1_COEF1); ++ __raw_writel(0x0730079c, pxp->base + HW_PXP_CSC1_COEF2); ++ ++ /* CSC2 - Bypass */ ++ __raw_writel(0x1, pxp->base + HW_PXP_CSC2_CTRL); ++ } else if (!input_is_YUV && output_is_YUV) { ++ /* ++ * Input = RGB, Output = YUV ++ * Use CSC2 to convert to YUV ++ */ ++ ++ /* CSC1 - Bypass */ ++ __raw_writel(0x40000000, pxp->base + HW_PXP_CSC1_COEF0); ++ ++ /* CSC2 - RGB->YUV */ ++ __raw_writel(0x4, pxp->base + HW_PXP_CSC2_CTRL); ++ __raw_writel(0x0096004D, pxp->base + HW_PXP_CSC2_COEF0); ++ __raw_writel(0x05DA001D, pxp->base + HW_PXP_CSC2_COEF1); ++ __raw_writel(0x007005B6, pxp->base + HW_PXP_CSC2_COEF2); ++ __raw_writel(0x057C009E, pxp->base + HW_PXP_CSC2_COEF3); ++ __raw_writel(0x000005E6, pxp->base + HW_PXP_CSC2_COEF4); ++ __raw_writel(0x00000000, pxp->base + HW_PXP_CSC2_COEF5); ++ } else { ++ /* ++ * Input = RGB, Output = RGB ++ * Input & Output both RGB, so bypass both CSCs ++ */ ++ ++ /* CSC1 - Bypass */ ++ __raw_writel(0x40000000, pxp->base + HW_PXP_CSC1_COEF0); ++ ++ /* CSC2 - Bypass */ ++ __raw_writel(0x1, pxp->base + HW_PXP_CSC2_CTRL); ++ } ++ ++ /* YCrCb colorspace */ ++ /* Not sure when we use this...no YCrCb formats are defined for PxP */ ++ /* ++ __raw_writel(0x84ab01f0, HW_PXP_CSCCOEFF0_ADDR); ++ __raw_writel(0x01230204, HW_PXP_CSCCOEFF1_ADDR); ++ __raw_writel(0x0730079c, HW_PXP_CSCCOEFF2_ADDR); ++ */ ++ ++} ++ ++static void pxp_set_s0buf(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_layer_param *s0_params = &pxp_conf->s0_param; ++ struct pxp_proc_data *proc_data = &pxp_conf->proc_data; ++ dma_addr_t Y, U, V; ++ dma_addr_t Y1, U1, V1; ++ u32 offset, bpp = 1; ++ u32 pitch = s0_params->stride ? s0_params->stride : ++ s0_params->width; ++ ++ Y = s0_params->paddr; ++ ++ if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB565) ++ bpp = 2; ++ else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB32) ++ bpp = 4; ++ offset = (proc_data->srect.top * s0_params->width + ++ proc_data->srect.left) * bpp; ++ /* clipping or cropping */ ++ Y1 = Y + offset; ++ __raw_writel(Y1, pxp->base + HW_PXP_PS_BUF); ++ if ((s0_params->pixel_fmt == PXP_PIX_FMT_YUV420P) || ++ (s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P) || ++ (s0_params->pixel_fmt == PXP_PIX_FMT_GREY) || ++ (s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P)) { ++ /* Set to 1 if YUV format is 4:2:2 rather than 4:2:0 */ ++ int s = 2; ++ if (s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P) ++ s = 1; ++ ++ offset = proc_data->srect.top * s0_params->width / 4 + ++ proc_data->srect.left / 2; ++ U = Y + (s0_params->width * s0_params->height); ++ U1 = U + offset; ++ V = U + ((s0_params->width * s0_params->height) >> s); ++ V1 = V + offset; ++ if (s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P) { ++ __raw_writel(V1, pxp->base + HW_PXP_PS_UBUF); ++ __raw_writel(U1, pxp->base + HW_PXP_PS_VBUF); ++ } else { ++ __raw_writel(U1, pxp->base + HW_PXP_PS_UBUF); ++ __raw_writel(V1, pxp->base + HW_PXP_PS_VBUF); ++ } ++ } else if ((s0_params->pixel_fmt == PXP_PIX_FMT_NV12) || ++ (s0_params->pixel_fmt == PXP_PIX_FMT_NV21) || ++ (s0_params->pixel_fmt == PXP_PIX_FMT_NV16) || ++ (s0_params->pixel_fmt == PXP_PIX_FMT_NV61)) { ++ int s = 2; ++ if ((s0_params->pixel_fmt == PXP_PIX_FMT_NV16) || ++ (s0_params->pixel_fmt == PXP_PIX_FMT_NV61)) ++ s = 1; ++ ++ offset = (proc_data->srect.top * s0_params->width + ++ proc_data->srect.left) / s; ++ U = Y + (s0_params->width * s0_params->height); ++ U1 = U + offset; ++ ++ __raw_writel(U1, pxp->base + HW_PXP_PS_UBUF); ++ } ++ ++ /* TODO: only support RGB565, Y8, Y4, YUV420 */ ++ if (s0_params->pixel_fmt == PXP_PIX_FMT_GREY || ++ s0_params->pixel_fmt == PXP_PIX_FMT_YUV420P || ++ s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P || ++ s0_params->pixel_fmt == PXP_PIX_FMT_NV12 || ++ s0_params->pixel_fmt == PXP_PIX_FMT_NV21 || ++ s0_params->pixel_fmt == PXP_PIX_FMT_NV16 || ++ s0_params->pixel_fmt == PXP_PIX_FMT_NV61 || ++ s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P) { ++ __raw_writel(pitch, pxp->base + HW_PXP_PS_PITCH); ++ } ++ else if (s0_params->pixel_fmt == PXP_PIX_FMT_GY04) ++ __raw_writel(pitch >> 1, ++ pxp->base + HW_PXP_PS_PITCH); ++ else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB32) ++ __raw_writel(pitch << 2, ++ pxp->base + HW_PXP_PS_PITCH); ++ else if (s0_params->pixel_fmt == PXP_PIX_FMT_UYVY || ++ s0_params->pixel_fmt == PXP_PIX_FMT_YUYV || ++ s0_params->pixel_fmt == PXP_PIX_FMT_VYUY || ++ s0_params->pixel_fmt == PXP_PIX_FMT_YVYU) ++ __raw_writel(pitch << 1, ++ pxp->base + HW_PXP_PS_PITCH); ++ else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB565) ++ __raw_writel(pitch << 1, ++ pxp->base + HW_PXP_PS_PITCH); ++ else ++ __raw_writel(0, pxp->base + HW_PXP_PS_PITCH); ++} ++ ++/** ++ * pxp_config() - configure PxP for a processing task ++ * @pxps: PXP context. ++ * @pxp_chan: PXP channel. ++ * @return: 0 on success or negative error code on failure. ++ */ ++static int pxp_config(struct pxps *pxp, struct pxp_channel *pxp_chan) ++{ ++ struct pxp_config_data *pxp_conf_data = &pxp->pxp_conf_state; ++ int ol_nr; ++ int i; ++ ++ /* Configure PxP regs */ ++ pxp_set_ctrl(pxp); ++ pxp_set_s0param(pxp); ++ pxp_set_s0crop(pxp); ++ pxp_set_scaling(pxp); ++ ol_nr = pxp_conf_data->layer_nr - 2; ++ while (ol_nr > 0) { ++ i = pxp_conf_data->layer_nr - 2 - ol_nr; ++ pxp_set_oln(i, pxp); ++ pxp_set_olparam(i, pxp); ++ /* only the color key in higher overlay will take effect. */ ++ pxp_set_olcolorkey(i, pxp); ++ ol_nr--; ++ } ++ pxp_set_s0colorkey(pxp); ++ pxp_set_csc(pxp); ++ pxp_set_bg(pxp); ++ pxp_set_lut(pxp); ++ ++ pxp_set_s0buf(pxp); ++ pxp_set_outbuf(pxp); ++ ++ return 0; ++} ++ ++static void pxp_clk_enable(struct pxps *pxp) ++{ ++ mutex_lock(&pxp->clk_mutex); ++ ++ if (pxp->clk_stat == CLK_STAT_ON) { ++ mutex_unlock(&pxp->clk_mutex); ++ return; ++ } ++ ++ clk_prepare_enable(pxp->clk); ++ pxp->clk_stat = CLK_STAT_ON; ++ ++ mutex_unlock(&pxp->clk_mutex); ++} ++ ++static void pxp_clk_disable(struct pxps *pxp) ++{ ++ unsigned long flags; ++ ++ mutex_lock(&pxp->clk_mutex); ++ ++ if (pxp->clk_stat == CLK_STAT_OFF) { ++ mutex_unlock(&pxp->clk_mutex); ++ return; ++ } ++ ++ spin_lock_irqsave(&pxp->lock, flags); ++ if ((pxp->pxp_ongoing == 0) && list_empty(&head)) { ++ spin_unlock_irqrestore(&pxp->lock, flags); ++ clk_disable_unprepare(pxp->clk); ++ pxp->clk_stat = CLK_STAT_OFF; ++ } else ++ spin_unlock_irqrestore(&pxp->lock, flags); ++ ++ mutex_unlock(&pxp->clk_mutex); ++} ++ ++static inline void clkoff_callback(struct work_struct *w) ++{ ++ struct pxps *pxp = container_of(w, struct pxps, work); ++ ++ pxp_clk_disable(pxp); ++} ++ ++static void pxp_clkoff_timer(unsigned long arg) ++{ ++ struct pxps *pxp = (struct pxps *)arg; ++ ++ if ((pxp->pxp_ongoing == 0) && list_empty(&head)) ++ schedule_work(&pxp->work); ++ else ++ mod_timer(&pxp->clk_timer, ++ jiffies + msecs_to_jiffies(timeout_in_ms)); ++} ++ ++static struct pxp_tx_desc *pxpdma_first_queued(struct pxp_channel *pxp_chan) ++{ ++ return list_entry(pxp_chan->queue.next, struct pxp_tx_desc, list); ++} ++ ++/* called with pxp_chan->lock held */ ++static void __pxpdma_dostart(struct pxp_channel *pxp_chan) ++{ ++ struct pxp_dma *pxp_dma = to_pxp_dma(pxp_chan->dma_chan.device); ++ struct pxps *pxp = to_pxp(pxp_dma); ++ struct pxp_tx_desc *desc; ++ struct pxp_tx_desc *child; ++ int i = 0; ++ ++ /* S0 */ ++ desc = list_first_entry(&head, struct pxp_tx_desc, list); ++ memcpy(&pxp->pxp_conf_state.s0_param, ++ &desc->layer_param.s0_param, sizeof(struct pxp_layer_param)); ++ memcpy(&pxp->pxp_conf_state.proc_data, ++ &desc->proc_data, sizeof(struct pxp_proc_data)); ++ ++ /* Save PxP configuration */ ++ list_for_each_entry(child, &desc->tx_list, list) { ++ if (i == 0) { /* Output */ ++ memcpy(&pxp->pxp_conf_state.out_param, ++ &child->layer_param.out_param, ++ sizeof(struct pxp_layer_param)); ++ } else { /* Overlay */ ++ memcpy(&pxp->pxp_conf_state.ol_param[i - 1], ++ &child->layer_param.ol_param, ++ sizeof(struct pxp_layer_param)); ++ } ++ ++ i++; ++ } ++ pr_debug("%s:%d S0 w/h %d/%d paddr %08x\n", __func__, __LINE__, ++ pxp->pxp_conf_state.s0_param.width, ++ pxp->pxp_conf_state.s0_param.height, ++ pxp->pxp_conf_state.s0_param.paddr); ++ pr_debug("%s:%d OUT w/h %d/%d paddr %08x\n", __func__, __LINE__, ++ pxp->pxp_conf_state.out_param.width, ++ pxp->pxp_conf_state.out_param.height, ++ pxp->pxp_conf_state.out_param.paddr); ++} ++ ++static void pxpdma_dostart_work(struct pxps *pxp) ++{ ++ struct pxp_channel *pxp_chan = NULL; ++ unsigned long flags; ++ struct pxp_tx_desc *desc = NULL; ++ ++ spin_lock_irqsave(&pxp->lock, flags); ++ ++ desc = list_entry(head.next, struct pxp_tx_desc, list); ++ pxp_chan = to_pxp_channel(desc->txd.chan); ++ ++ __pxpdma_dostart(pxp_chan); ++ ++ /* Configure PxP */ ++ pxp_config(pxp, pxp_chan); ++ ++ pxp_start(pxp); ++ ++ spin_unlock_irqrestore(&pxp->lock, flags); ++} ++ ++static void pxpdma_dequeue(struct pxp_channel *pxp_chan, struct pxps *pxp) ++{ ++ unsigned long flags; ++ struct pxp_tx_desc *desc = NULL; ++ ++ do { ++ desc = pxpdma_first_queued(pxp_chan); ++ spin_lock_irqsave(&pxp->lock, flags); ++ list_move_tail(&desc->list, &head); ++ spin_unlock_irqrestore(&pxp->lock, flags); ++ } while (!list_empty(&pxp_chan->queue)); ++} ++ ++static dma_cookie_t pxp_tx_submit(struct dma_async_tx_descriptor *tx) ++{ ++ struct pxp_tx_desc *desc = to_tx_desc(tx); ++ struct pxp_channel *pxp_chan = to_pxp_channel(tx->chan); ++ dma_cookie_t cookie; ++ ++ dev_dbg(&pxp_chan->dma_chan.dev->device, "received TX\n"); ++ ++ /* pxp_chan->lock can be taken under ichan->lock, but not v.v. */ ++ spin_lock(&pxp_chan->lock); ++ ++ cookie = pxp_chan->dma_chan.cookie; ++ ++ if (++cookie < 0) ++ cookie = 1; ++ ++ /* from dmaengine.h: "last cookie value returned to client" */ ++ pxp_chan->dma_chan.cookie = cookie; ++ tx->cookie = cookie; ++ ++ /* Here we add the tx descriptor to our PxP task queue. */ ++ list_add_tail(&desc->list, &pxp_chan->queue); ++ ++ spin_unlock(&pxp_chan->lock); ++ ++ dev_dbg(&pxp_chan->dma_chan.dev->device, "done TX\n"); ++ ++ return cookie; ++} ++ ++/** ++ * pxp_init_channel() - initialize a PXP channel. ++ * @pxp_dma: PXP DMA context. ++ * @pchan: pointer to the channel object. ++ * @return 0 on success or negative error code on failure. ++ */ ++static int pxp_init_channel(struct pxp_dma *pxp_dma, ++ struct pxp_channel *pxp_chan) ++{ ++ int ret = 0; ++ ++ /* ++ * We are using _virtual_ channel here. ++ * Each channel contains all parameters of corresponding layers ++ * for one transaction; each layer is represented as one descriptor ++ * (i.e., pxp_tx_desc) here. ++ */ ++ ++ INIT_LIST_HEAD(&pxp_chan->queue); ++ ++ return ret; ++} ++ ++static irqreturn_t pxp_irq(int irq, void *dev_id) ++{ ++ struct pxps *pxp = dev_id; ++ struct pxp_channel *pxp_chan; ++ struct pxp_tx_desc *desc; ++ struct pxp_tx_desc *child, *_child; ++ dma_async_tx_callback callback; ++ void *callback_param; ++ unsigned long flags; ++ u32 hist_status; ++ ++ dump_pxp_reg(pxp); ++ ++ hist_status = ++ __raw_readl(pxp->base + HW_PXP_HIST_CTRL) & BM_PXP_HIST_CTRL_STATUS; ++ ++ __raw_writel(BM_PXP_STAT_IRQ, pxp->base + HW_PXP_STAT_CLR); ++ ++ spin_lock_irqsave(&pxp->lock, flags); ++ ++ if (list_empty(&head)) { ++ pxp->pxp_ongoing = 0; ++ spin_unlock_irqrestore(&pxp->lock, flags); ++ return IRQ_NONE; ++ } ++ ++ /* Get descriptor and call callback */ ++ desc = list_entry(head.next, struct pxp_tx_desc, list); ++ pxp_chan = to_pxp_channel(desc->txd.chan); ++ ++ pxp_chan->completed = desc->txd.cookie; ++ ++ callback = desc->txd.callback; ++ callback_param = desc->txd.callback_param; ++ ++ /* Send histogram status back to caller */ ++ desc->hist_status = hist_status; ++ ++ if ((desc->txd.flags & DMA_PREP_INTERRUPT) && callback) ++ callback(callback_param); ++ ++ pxp_chan->status = PXP_CHANNEL_INITIALIZED; ++ ++ list_for_each_entry_safe(child, _child, &desc->tx_list, list) { ++ list_del_init(&child->list); ++ kmem_cache_free(tx_desc_cache, (void *)child); ++ } ++ list_del_init(&desc->list); ++ kmem_cache_free(tx_desc_cache, (void *)desc); ++ ++ complete(&pxp->complete); ++ pxp->pxp_ongoing = 0; ++ mod_timer(&pxp->clk_timer, jiffies + msecs_to_jiffies(timeout_in_ms)); ++ ++ spin_unlock_irqrestore(&pxp->lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++/* allocate/free dma tx descriptor dynamically*/ ++static struct pxp_tx_desc *pxpdma_desc_alloc(struct pxp_channel *pxp_chan) ++{ ++ struct pxp_tx_desc *desc = NULL; ++ struct dma_async_tx_descriptor *txd = NULL; ++ ++ desc = kmem_cache_alloc(tx_desc_cache, GFP_KERNEL | __GFP_ZERO); ++ if (desc == NULL) ++ return NULL; ++ ++ INIT_LIST_HEAD(&desc->list); ++ INIT_LIST_HEAD(&desc->tx_list); ++ txd = &desc->txd; ++ dma_async_tx_descriptor_init(txd, &pxp_chan->dma_chan); ++ txd->tx_submit = pxp_tx_submit; ++ ++ return desc; ++} ++ ++/* Allocate and initialise a transfer descriptor. */ ++static struct dma_async_tx_descriptor *pxp_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist ++ *sgl, ++ unsigned int sg_len, ++ enum ++ dma_transfer_direction ++ direction, ++ unsigned long tx_flags, ++ void *context) ++{ ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); ++ struct pxps *pxp = to_pxp(pxp_dma); ++ struct pxp_tx_desc *desc = NULL; ++ struct pxp_tx_desc *first = NULL, *prev = NULL; ++ struct scatterlist *sg; ++ dma_addr_t phys_addr; ++ int i; ++ ++ if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) { ++ dev_err(chan->device->dev, "Invalid DMA direction %d!\n", ++ direction); ++ return NULL; ++ } ++ ++ if (unlikely(sg_len < 2)) ++ return NULL; ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ desc = pxpdma_desc_alloc(pxp_chan); ++ if (!desc) { ++ dev_err(chan->device->dev, "no enough memory to allocate tx descriptor\n"); ++ return NULL; ++ } ++ ++ phys_addr = sg_dma_address(sg); ++ ++ if (!first) { ++ first = desc; ++ ++ desc->layer_param.s0_param.paddr = phys_addr; ++ } else { ++ list_add_tail(&desc->list, &first->tx_list); ++ prev->next = desc; ++ desc->next = NULL; ++ ++ if (i == 1) ++ desc->layer_param.out_param.paddr = phys_addr; ++ else ++ desc->layer_param.ol_param.paddr = phys_addr; ++ } ++ ++ prev = desc; ++ } ++ ++ pxp->pxp_conf_state.layer_nr = sg_len; ++ first->txd.flags = tx_flags; ++ first->len = sg_len; ++ pr_debug("%s:%d first %p, first->len %d, flags %08x\n", ++ __func__, __LINE__, first, first->len, first->txd.flags); ++ ++ return &first->txd; ++} ++ ++static void pxp_issue_pending(struct dma_chan *chan) ++{ ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); ++ struct pxps *pxp = to_pxp(pxp_dma); ++ ++ spin_lock(&pxp_chan->lock); ++ ++ if (list_empty(&pxp_chan->queue)) { ++ spin_unlock(&pxp_chan->lock); ++ return; ++ } ++ ++ pxpdma_dequeue(pxp_chan, pxp); ++ pxp_chan->status = PXP_CHANNEL_READY; ++ ++ spin_unlock(&pxp_chan->lock); ++ ++ pxp_clk_enable(pxp); ++ wake_up_interruptible(&pxp->thread_waitq); ++} ++ ++static void __pxp_terminate_all(struct dma_chan *chan) ++{ ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ ++ pxp_chan->status = PXP_CHANNEL_INITIALIZED; ++} ++ ++static int pxp_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ ++ /* Only supports DMA_TERMINATE_ALL */ ++ if (cmd != DMA_TERMINATE_ALL) ++ return -ENXIO; ++ ++ spin_lock(&pxp_chan->lock); ++ __pxp_terminate_all(chan); ++ spin_unlock(&pxp_chan->lock); ++ ++ return 0; ++} ++ ++static int pxp_alloc_chan_resources(struct dma_chan *chan) ++{ ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); ++ int ret; ++ ++ /* dmaengine.c now guarantees to only offer free channels */ ++ BUG_ON(chan->client_count > 1); ++ WARN_ON(pxp_chan->status != PXP_CHANNEL_FREE); ++ ++ chan->cookie = 1; ++ pxp_chan->completed = -ENXIO; ++ ++ pr_debug("%s dma_chan.chan_id %d\n", __func__, chan->chan_id); ++ ret = pxp_init_channel(pxp_dma, pxp_chan); ++ if (ret < 0) ++ goto err_chan; ++ ++ pxp_chan->status = PXP_CHANNEL_INITIALIZED; ++ ++ dev_dbg(&chan->dev->device, "Found channel 0x%x, irq %d\n", ++ chan->chan_id, pxp_chan->eof_irq); ++ ++ return ret; ++ ++err_chan: ++ return ret; ++} ++ ++static void pxp_free_chan_resources(struct dma_chan *chan) ++{ ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ ++ spin_lock(&pxp_chan->lock); ++ ++ __pxp_terminate_all(chan); ++ ++ pxp_chan->status = PXP_CHANNEL_FREE; ++ ++ spin_unlock(&pxp_chan->lock); ++} ++ ++static enum dma_status pxp_tx_status(struct dma_chan *chan, ++ dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ ++ if (cookie != chan->cookie) ++ return DMA_ERROR; ++ ++ if (txstate) { ++ txstate->last = pxp_chan->completed; ++ txstate->used = chan->cookie; ++ txstate->residue = 0; ++ } ++ return DMA_COMPLETE; ++} ++ ++static int pxp_hw_init(struct pxps *pxp) ++{ ++ struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; ++ struct pxp_proc_data *proc_data = &pxp_conf->proc_data; ++ u32 reg_val; ++ ++ /* Pull PxP out of reset */ ++ __raw_writel(0, pxp->base + HW_PXP_CTRL); ++ ++ /* Config defaults */ ++ ++ /* Initialize non-channel-specific PxP parameters */ ++ proc_data->drect.left = proc_data->srect.left = 0; ++ proc_data->drect.top = proc_data->srect.top = 0; ++ proc_data->drect.width = proc_data->srect.width = 0; ++ proc_data->drect.height = proc_data->srect.height = 0; ++ proc_data->scaling = 0; ++ proc_data->hflip = 0; ++ proc_data->vflip = 0; ++ proc_data->rotate = 0; ++ proc_data->bgcolor = 0; ++ ++ /* Initialize S0 channel parameters */ ++ pxp_conf->s0_param.pixel_fmt = pxp_s0_formats[0]; ++ pxp_conf->s0_param.width = 0; ++ pxp_conf->s0_param.height = 0; ++ pxp_conf->s0_param.color_key = -1; ++ pxp_conf->s0_param.color_key_enable = false; ++ ++ /* Initialize OL channel parameters */ ++ pxp_conf->ol_param[0].combine_enable = false; ++ pxp_conf->ol_param[0].width = 0; ++ pxp_conf->ol_param[0].height = 0; ++ pxp_conf->ol_param[0].pixel_fmt = PXP_PIX_FMT_RGB565; ++ pxp_conf->ol_param[0].color_key_enable = false; ++ pxp_conf->ol_param[0].color_key = -1; ++ pxp_conf->ol_param[0].global_alpha_enable = false; ++ pxp_conf->ol_param[0].global_alpha = 0; ++ pxp_conf->ol_param[0].local_alpha_enable = false; ++ ++ /* Initialize Output channel parameters */ ++ pxp_conf->out_param.width = 0; ++ pxp_conf->out_param.height = 0; ++ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_RGB565; ++ ++ proc_data->overlay_state = 0; ++ ++ /* Write default h/w config */ ++ pxp_set_ctrl(pxp); ++ pxp_set_s0param(pxp); ++ pxp_set_s0crop(pxp); ++ /* ++ * simply program the ULC to a higher value than the LRC ++ * to avoid any AS pixels to show up in the output buffer. ++ */ ++ __raw_writel(0xFFFFFFFF, pxp->base + HW_PXP_OUT_AS_ULC); ++ pxp_set_olparam(0, pxp); ++ pxp_set_olcolorkey(0, pxp); ++ ++ pxp_set_s0colorkey(pxp); ++ pxp_set_csc(pxp); ++ pxp_set_bg(pxp); ++ pxp_set_lut(pxp); ++ ++ /* One-time histogram configuration */ ++ reg_val = ++ BF_PXP_HIST_CTRL_PANEL_MODE(BV_PXP_HIST_CTRL_PANEL_MODE__GRAY16); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST_CTRL); ++ ++ reg_val = BF_PXP_HIST2_PARAM_VALUE0(0x00) | ++ BF_PXP_HIST2_PARAM_VALUE1(0x00F); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST2_PARAM); ++ ++ reg_val = BF_PXP_HIST4_PARAM_VALUE0(0x00) | ++ BF_PXP_HIST4_PARAM_VALUE1(0x05) | ++ BF_PXP_HIST4_PARAM_VALUE2(0x0A) | BF_PXP_HIST4_PARAM_VALUE3(0x0F); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST4_PARAM); ++ ++ reg_val = BF_PXP_HIST8_PARAM0_VALUE0(0x00) | ++ BF_PXP_HIST8_PARAM0_VALUE1(0x02) | ++ BF_PXP_HIST8_PARAM0_VALUE2(0x04) | BF_PXP_HIST8_PARAM0_VALUE3(0x06); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST8_PARAM0); ++ reg_val = BF_PXP_HIST8_PARAM1_VALUE4(0x09) | ++ BF_PXP_HIST8_PARAM1_VALUE5(0x0B) | ++ BF_PXP_HIST8_PARAM1_VALUE6(0x0D) | BF_PXP_HIST8_PARAM1_VALUE7(0x0F); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST8_PARAM1); ++ ++ reg_val = BF_PXP_HIST16_PARAM0_VALUE0(0x00) | ++ BF_PXP_HIST16_PARAM0_VALUE1(0x01) | ++ BF_PXP_HIST16_PARAM0_VALUE2(0x02) | ++ BF_PXP_HIST16_PARAM0_VALUE3(0x03); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM0); ++ reg_val = BF_PXP_HIST16_PARAM1_VALUE4(0x04) | ++ BF_PXP_HIST16_PARAM1_VALUE5(0x05) | ++ BF_PXP_HIST16_PARAM1_VALUE6(0x06) | ++ BF_PXP_HIST16_PARAM1_VALUE7(0x07); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM1); ++ reg_val = BF_PXP_HIST16_PARAM2_VALUE8(0x08) | ++ BF_PXP_HIST16_PARAM2_VALUE9(0x09) | ++ BF_PXP_HIST16_PARAM2_VALUE10(0x0A) | ++ BF_PXP_HIST16_PARAM2_VALUE11(0x0B); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM2); ++ reg_val = BF_PXP_HIST16_PARAM3_VALUE12(0x0C) | ++ BF_PXP_HIST16_PARAM3_VALUE13(0x0D) | ++ BF_PXP_HIST16_PARAM3_VALUE14(0x0E) | ++ BF_PXP_HIST16_PARAM3_VALUE15(0x0F); ++ __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM3); ++ ++ return 0; ++} ++ ++static int pxp_dma_init(struct pxps *pxp) ++{ ++ struct pxp_dma *pxp_dma = &pxp->pxp_dma; ++ struct dma_device *dma = &pxp_dma->dma; ++ int i; ++ ++ dma_cap_set(DMA_SLAVE, dma->cap_mask); ++ dma_cap_set(DMA_PRIVATE, dma->cap_mask); ++ ++ /* Compulsory common fields */ ++ dma->dev = pxp->dev; ++ dma->device_alloc_chan_resources = pxp_alloc_chan_resources; ++ dma->device_free_chan_resources = pxp_free_chan_resources; ++ dma->device_tx_status = pxp_tx_status; ++ dma->device_issue_pending = pxp_issue_pending; ++ ++ /* Compulsory for DMA_SLAVE fields */ ++ dma->device_prep_slave_sg = pxp_prep_slave_sg; ++ dma->device_control = pxp_control; ++ ++ /* Initialize PxP Channels */ ++ INIT_LIST_HEAD(&dma->channels); ++ for (i = 0; i < NR_PXP_VIRT_CHANNEL; i++) { ++ struct pxp_channel *pxp_chan = pxp->channel + i; ++ struct dma_chan *dma_chan = &pxp_chan->dma_chan; ++ ++ spin_lock_init(&pxp_chan->lock); ++ ++ /* Only one EOF IRQ for PxP, shared by all channels */ ++ pxp_chan->eof_irq = pxp->irq; ++ pxp_chan->status = PXP_CHANNEL_FREE; ++ pxp_chan->completed = -ENXIO; ++ snprintf(pxp_chan->eof_name, sizeof(pxp_chan->eof_name), ++ "PXP EOF %d", i); ++ ++ dma_chan->device = &pxp_dma->dma; ++ dma_chan->cookie = 1; ++ dma_chan->chan_id = i; ++ list_add_tail(&dma_chan->device_node, &dma->channels); ++ } ++ ++ return dma_async_device_register(&pxp_dma->dma); ++} ++ ++static ssize_t clk_off_timeout_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", timeout_in_ms); ++} ++ ++static ssize_t clk_off_timeout_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int val; ++ if (sscanf(buf, "%d", &val) > 0) { ++ timeout_in_ms = val; ++ return count; ++ } ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(clk_off_timeout, 0644, clk_off_timeout_show, ++ clk_off_timeout_store); ++ ++static ssize_t block_size_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", block_size); ++} ++ ++static ssize_t block_size_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ char **last = NULL; ++ ++ block_size = simple_strtoul(buf, last, 0); ++ if (block_size > 1) ++ block_size = 1; ++ ++ return count; ++} ++static DEVICE_ATTR(block_size, S_IWUSR | S_IRUGO, ++ block_size_show, block_size_store); ++ ++static const struct of_device_id imx_pxpdma_dt_ids[] = { ++ { .compatible = "fsl,imx6dl-pxp-dma", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, imx_pxpdma_dt_ids); ++ ++static int has_pending_task(struct pxps *pxp, struct pxp_channel *task) ++{ ++ int found; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pxp->lock, flags); ++ found = !list_empty(&head); ++ spin_unlock_irqrestore(&pxp->lock, flags); ++ ++ return found; ++} ++ ++static int pxp_dispatch_thread(void *argv) ++{ ++ struct pxps *pxp = (struct pxps *)argv; ++ struct pxp_channel *pending = NULL; ++ unsigned long flags; ++ ++ while (!kthread_should_stop()) { ++ int ret; ++ ret = wait_event_interruptible(pxp->thread_waitq, ++ has_pending_task(pxp, pending)); ++ if (signal_pending(current)) ++ continue; ++ ++ if (kthread_should_stop()) ++ break; ++ ++ spin_lock_irqsave(&pxp->lock, flags); ++ pxp->pxp_ongoing = 1; ++ spin_unlock_irqrestore(&pxp->lock, flags); ++ init_completion(&pxp->complete); ++ pxpdma_dostart_work(pxp); ++ ret = wait_for_completion_timeout(&pxp->complete, 2 * HZ); ++ if (ret == 0) { ++ printk(KERN_EMERG "%s: task is timeout\n\n", __func__); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static int pxp_probe(struct platform_device *pdev) ++{ ++ struct pxps *pxp; ++ struct resource *res; ++ int irq; ++ int err = 0; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ if (!res || irq < 0) { ++ err = -ENODEV; ++ goto exit; ++ } ++ ++ pxp = devm_kzalloc(&pdev->dev, sizeof(*pxp), GFP_KERNEL); ++ if (!pxp) { ++ dev_err(&pdev->dev, "failed to allocate control object\n"); ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ pxp->dev = &pdev->dev; ++ ++ platform_set_drvdata(pdev, pxp); ++ pxp->irq = irq; ++ ++ pxp->pxp_ongoing = 0; ++ pxp->lut_state = 0; ++ ++ spin_lock_init(&pxp->lock); ++ mutex_init(&pxp->clk_mutex); ++ ++ pxp->base = devm_request_and_ioremap(&pdev->dev, res); ++ if (pxp->base == NULL) { ++ dev_err(&pdev->dev, "Couldn't ioremap regs\n"); ++ err = -ENODEV; ++ goto exit; ++ } ++ ++ pxp->pdev = pdev; ++ ++ pxp->clk = devm_clk_get(&pdev->dev, "pxp-axi"); ++ clk_prepare_enable(pxp->clk); ++ ++ err = pxp_hw_init(pxp); ++ clk_disable_unprepare(pxp->clk); ++ if (err) { ++ dev_err(&pdev->dev, "failed to initialize hardware\n"); ++ goto exit; ++ } ++ ++ err = devm_request_irq(&pdev->dev, pxp->irq, pxp_irq, 0, ++ "pxp-dmaengine", pxp); ++ if (err) ++ goto exit; ++ /* Initialize DMA engine */ ++ err = pxp_dma_init(pxp); ++ if (err < 0) ++ goto exit; ++ ++ if (device_create_file(&pdev->dev, &dev_attr_clk_off_timeout)) { ++ dev_err(&pdev->dev, ++ "Unable to create file from clk_off_timeout\n"); ++ goto exit; ++ } ++ ++ device_create_file(&pdev->dev, &dev_attr_block_size); ++ dump_pxp_reg(pxp); ++ ++ INIT_WORK(&pxp->work, clkoff_callback); ++ init_timer(&pxp->clk_timer); ++ pxp->clk_timer.function = pxp_clkoff_timer; ++ pxp->clk_timer.data = (unsigned long)pxp; ++ ++ /* allocate a kernel thread to dispatch pxp conf */ ++ pxp->dispatch = kthread_run(pxp_dispatch_thread, pxp, "pxp_dispatch"); ++ if (IS_ERR(pxp->dispatch)) { ++ err = PTR_ERR(pxp->dispatch); ++ goto exit; ++ } ++ init_waitqueue_head(&pxp->thread_waitq); ++ tx_desc_cache = kmem_cache_create("tx_desc", sizeof(struct pxp_tx_desc), ++ 0, SLAB_HWCACHE_ALIGN, NULL); ++ if (!tx_desc_cache) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ register_pxp_device(); ++ ++exit: ++ if (err) ++ dev_err(&pdev->dev, "Exiting (unsuccessfully) pxp_probe()\n"); ++ return err; ++} ++ ++static int pxp_remove(struct platform_device *pdev) ++{ ++ struct pxps *pxp = platform_get_drvdata(pdev); ++ ++ unregister_pxp_device(); ++ kmem_cache_destroy(tx_desc_cache); ++ kthread_stop(pxp->dispatch); ++ cancel_work_sync(&pxp->work); ++ del_timer_sync(&pxp->clk_timer); ++ clk_disable_unprepare(pxp->clk); ++ device_remove_file(&pdev->dev, &dev_attr_clk_off_timeout); ++ device_remove_file(&pdev->dev, &dev_attr_block_size); ++ dma_async_device_unregister(&(pxp->pxp_dma.dma)); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int pxp_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct pxps *pxp = platform_get_drvdata(pdev); ++ ++ pxp_clk_enable(pxp); ++ while (__raw_readl(pxp->base + HW_PXP_CTRL) & BM_PXP_CTRL_ENABLE) ++ ; ++ ++ __raw_writel(BM_PXP_CTRL_SFTRST, pxp->base + HW_PXP_CTRL); ++ pxp_clk_disable(pxp); ++ ++ return 0; ++} ++ ++static int pxp_resume(struct platform_device *pdev) ++{ ++ struct pxps *pxp = platform_get_drvdata(pdev); ++ ++ pxp_clk_enable(pxp); ++ /* Pull PxP out of reset */ ++ __raw_writel(0, pxp->base + HW_PXP_CTRL); ++ pxp_clk_disable(pxp); ++ ++ return 0; ++} ++#else ++#define pxp_suspend NULL ++#define pxp_resume NULL ++#endif ++ ++static struct platform_driver pxp_driver = { ++ .driver = { ++ .name = "imx-pxp", ++ .of_match_table = of_match_ptr(imx_pxpdma_dt_ids), ++ }, ++ .probe = pxp_probe, ++ .remove = pxp_remove, ++ .suspend = pxp_suspend, ++ .resume = pxp_resume, ++}; ++ ++module_platform_driver(pxp_driver); ++ ++ ++MODULE_DESCRIPTION("i.MX PxP driver"); ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/dma/pxp/regs-pxp_v2.h linux-openelec/drivers/dma/pxp/regs-pxp_v2.h +--- linux-3.14.36/drivers/dma/pxp/regs-pxp_v2.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/dma/pxp/regs-pxp_v2.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1152 @@ ++/* ++ * Freescale PXP Register Definitions ++ * ++ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * This file is created by xml file. Don't Edit it. ++ * ++ * Xml Revision: 1.29 ++ * Template revision: 1.3 ++ */ ++ ++#ifndef __ARCH_ARM___PXP_H ++#define __ARCH_ARM___PXP_H ++ ++#define HW_PXP_CTRL (0x00000000) ++#define HW_PXP_CTRL_SET (0x00000004) ++#define HW_PXP_CTRL_CLR (0x00000008) ++#define HW_PXP_CTRL_TOG (0x0000000c) ++ ++#define BM_PXP_CTRL_SFTRST 0x80000000 ++#define BM_PXP_CTRL_CLKGATE 0x40000000 ++#define BM_PXP_CTRL_RSVD4 0x20000000 ++#define BM_PXP_CTRL_EN_REPEAT 0x10000000 ++#define BP_PXP_CTRL_RSVD3 26 ++#define BM_PXP_CTRL_RSVD3 0x0C000000 ++#define BF_PXP_CTRL_RSVD3(v) \ ++ (((v) << 26) & BM_PXP_CTRL_RSVD3) ++#define BP_PXP_CTRL_INTERLACED_INPUT 24 ++#define BM_PXP_CTRL_INTERLACED_INPUT 0x03000000 ++#define BF_PXP_CTRL_INTERLACED_INPUT(v) \ ++ (((v) << 24) & BM_PXP_CTRL_INTERLACED_INPUT) ++#define BV_PXP_CTRL_INTERLACED_INPUT__PROGRESSIVE 0x0 ++#define BV_PXP_CTRL_INTERLACED_INPUT__FIELD0 0x2 ++#define BV_PXP_CTRL_INTERLACED_INPUT__FIELD1 0x3 ++#define BM_PXP_CTRL_BLOCK_SIZE 0x00800000 ++#define BV_PXP_CTRL_BLOCK_SIZE__8X8 0x0 ++#define BV_PXP_CTRL_BLOCK_SIZE__16X16 0x1 ++#define BM_PXP_CTRL_ROT_POS 0x00400000 ++#define BM_PXP_CTRL_IN_PLACE 0x00200000 ++#define BP_PXP_CTRL_RSVD1 12 ++#define BM_PXP_CTRL_RSVD1 0x001FF000 ++#define BF_PXP_CTRL_RSVD1(v) \ ++ (((v) << 12) & BM_PXP_CTRL_RSVD1) ++#define BM_PXP_CTRL_VFLIP 0x00000800 ++#define BM_PXP_CTRL_HFLIP 0x00000400 ++#define BP_PXP_CTRL_ROTATE 8 ++#define BM_PXP_CTRL_ROTATE 0x00000300 ++#define BF_PXP_CTRL_ROTATE(v) \ ++ (((v) << 8) & BM_PXP_CTRL_ROTATE) ++#define BV_PXP_CTRL_ROTATE__ROT_0 0x0 ++#define BV_PXP_CTRL_ROTATE__ROT_90 0x1 ++#define BV_PXP_CTRL_ROTATE__ROT_180 0x2 ++#define BV_PXP_CTRL_ROTATE__ROT_270 0x3 ++#define BP_PXP_CTRL_RSVD0 5 ++#define BM_PXP_CTRL_RSVD0 0x000000E0 ++#define BF_PXP_CTRL_RSVD0(v) \ ++ (((v) << 5) & BM_PXP_CTRL_RSVD0) ++#define BM_PXP_CTRL_ENABLE_LCD_HANDSHAKE 0x00000010 ++#define BM_PXP_CTRL_LUT_DMA_IRQ_ENABLE 0x00000008 ++#define BM_PXP_CTRL_NEXT_IRQ_ENABLE 0x00000004 ++#define BM_PXP_CTRL_IRQ_ENABLE 0x00000002 ++#define BM_PXP_CTRL_ENABLE 0x00000001 ++ ++#define HW_PXP_STAT (0x00000010) ++#define HW_PXP_STAT_SET (0x00000014) ++#define HW_PXP_STAT_CLR (0x00000018) ++#define HW_PXP_STAT_TOG (0x0000001c) ++ ++#define BP_PXP_STAT_BLOCKX 24 ++#define BM_PXP_STAT_BLOCKX 0xFF000000 ++#define BF_PXP_STAT_BLOCKX(v) \ ++ (((v) << 24) & BM_PXP_STAT_BLOCKX) ++#define BP_PXP_STAT_BLOCKY 16 ++#define BM_PXP_STAT_BLOCKY 0x00FF0000 ++#define BF_PXP_STAT_BLOCKY(v) \ ++ (((v) << 16) & BM_PXP_STAT_BLOCKY) ++#define BP_PXP_STAT_RSVD2 9 ++#define BM_PXP_STAT_RSVD2 0x0000FE00 ++#define BF_PXP_STAT_RSVD2(v) \ ++ (((v) << 9) & BM_PXP_STAT_RSVD2) ++#define BM_PXP_STAT_LUT_DMA_LOAD_DONE_IRQ 0x00000100 ++#define BP_PXP_STAT_AXI_ERROR_ID 4 ++#define BM_PXP_STAT_AXI_ERROR_ID 0x000000F0 ++#define BF_PXP_STAT_AXI_ERROR_ID(v) \ ++ (((v) << 4) & BM_PXP_STAT_AXI_ERROR_ID) ++#define BM_PXP_STAT_NEXT_IRQ 0x00000008 ++#define BM_PXP_STAT_AXI_READ_ERROR 0x00000004 ++#define BM_PXP_STAT_AXI_WRITE_ERROR 0x00000002 ++#define BM_PXP_STAT_IRQ 0x00000001 ++ ++#define HW_PXP_OUT_CTRL (0x00000020) ++#define HW_PXP_OUT_CTRL_SET (0x00000024) ++#define HW_PXP_OUT_CTRL_CLR (0x00000028) ++#define HW_PXP_OUT_CTRL_TOG (0x0000002c) ++ ++#define BP_PXP_OUT_CTRL_ALPHA 24 ++#define BM_PXP_OUT_CTRL_ALPHA 0xFF000000 ++#define BF_PXP_OUT_CTRL_ALPHA(v) \ ++ (((v) << 24) & BM_PXP_OUT_CTRL_ALPHA) ++#define BM_PXP_OUT_CTRL_ALPHA_OUTPUT 0x00800000 ++#define BP_PXP_OUT_CTRL_RSVD1 10 ++#define BM_PXP_OUT_CTRL_RSVD1 0x007FFC00 ++#define BF_PXP_OUT_CTRL_RSVD1(v) \ ++ (((v) << 10) & BM_PXP_OUT_CTRL_RSVD1) ++#define BP_PXP_OUT_CTRL_INTERLACED_OUTPUT 8 ++#define BM_PXP_OUT_CTRL_INTERLACED_OUTPUT 0x00000300 ++#define BF_PXP_OUT_CTRL_INTERLACED_OUTPUT(v) \ ++ (((v) << 8) & BM_PXP_OUT_CTRL_INTERLACED_OUTPUT) ++#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__PROGRESSIVE 0x0 ++#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__FIELD0 0x1 ++#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__FIELD1 0x2 ++#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__INTERLACED 0x3 ++#define BP_PXP_OUT_CTRL_RSVD0 5 ++#define BM_PXP_OUT_CTRL_RSVD0 0x000000E0 ++#define BF_PXP_OUT_CTRL_RSVD0(v) \ ++ (((v) << 5) & BM_PXP_OUT_CTRL_RSVD0) ++#define BP_PXP_OUT_CTRL_FORMAT 0 ++#define BM_PXP_OUT_CTRL_FORMAT 0x0000001F ++#define BF_PXP_OUT_CTRL_FORMAT(v) \ ++ (((v) << 0) & BM_PXP_OUT_CTRL_FORMAT) ++#define BV_PXP_OUT_CTRL_FORMAT__ARGB8888 0x0 ++#define BV_PXP_OUT_CTRL_FORMAT__RGB888 0x4 ++#define BV_PXP_OUT_CTRL_FORMAT__RGB888P 0x5 ++#define BV_PXP_OUT_CTRL_FORMAT__ARGB1555 0x8 ++#define BV_PXP_OUT_CTRL_FORMAT__ARGB4444 0x9 ++#define BV_PXP_OUT_CTRL_FORMAT__RGB555 0xC ++#define BV_PXP_OUT_CTRL_FORMAT__RGB444 0xD ++#define BV_PXP_OUT_CTRL_FORMAT__RGB565 0xE ++#define BV_PXP_OUT_CTRL_FORMAT__YUV1P444 0x10 ++#define BV_PXP_OUT_CTRL_FORMAT__UYVY1P422 0x12 ++#define BV_PXP_OUT_CTRL_FORMAT__VYUY1P422 0x13 ++#define BV_PXP_OUT_CTRL_FORMAT__Y8 0x14 ++#define BV_PXP_OUT_CTRL_FORMAT__Y4 0x15 ++#define BV_PXP_OUT_CTRL_FORMAT__YUV2P422 0x18 ++#define BV_PXP_OUT_CTRL_FORMAT__YUV2P420 0x19 ++#define BV_PXP_OUT_CTRL_FORMAT__YVU2P422 0x1A ++#define BV_PXP_OUT_CTRL_FORMAT__YVU2P420 0x1B ++ ++#define HW_PXP_OUT_BUF (0x00000030) ++ ++#define BP_PXP_OUT_BUF_ADDR 0 ++#define BM_PXP_OUT_BUF_ADDR 0xFFFFFFFF ++#define BF_PXP_OUT_BUF_ADDR(v) (v) ++ ++#define HW_PXP_OUT_BUF2 (0x00000040) ++ ++#define BP_PXP_OUT_BUF2_ADDR 0 ++#define BM_PXP_OUT_BUF2_ADDR 0xFFFFFFFF ++#define BF_PXP_OUT_BUF2_ADDR(v) (v) ++ ++#define HW_PXP_OUT_PITCH (0x00000050) ++ ++#define BP_PXP_OUT_PITCH_RSVD 16 ++#define BM_PXP_OUT_PITCH_RSVD 0xFFFF0000 ++#define BF_PXP_OUT_PITCH_RSVD(v) \ ++ (((v) << 16) & BM_PXP_OUT_PITCH_RSVD) ++#define BP_PXP_OUT_PITCH_PITCH 0 ++#define BM_PXP_OUT_PITCH_PITCH 0x0000FFFF ++#define BF_PXP_OUT_PITCH_PITCH(v) \ ++ (((v) << 0) & BM_PXP_OUT_PITCH_PITCH) ++ ++#define HW_PXP_OUT_LRC (0x00000060) ++ ++#define BP_PXP_OUT_LRC_RSVD1 30 ++#define BM_PXP_OUT_LRC_RSVD1 0xC0000000 ++#define BF_PXP_OUT_LRC_RSVD1(v) \ ++ (((v) << 30) & BM_PXP_OUT_LRC_RSVD1) ++#define BP_PXP_OUT_LRC_X 16 ++#define BM_PXP_OUT_LRC_X 0x3FFF0000 ++#define BF_PXP_OUT_LRC_X(v) \ ++ (((v) << 16) & BM_PXP_OUT_LRC_X) ++#define BP_PXP_OUT_LRC_RSVD0 14 ++#define BM_PXP_OUT_LRC_RSVD0 0x0000C000 ++#define BF_PXP_OUT_LRC_RSVD0(v) \ ++ (((v) << 14) & BM_PXP_OUT_LRC_RSVD0) ++#define BP_PXP_OUT_LRC_Y 0 ++#define BM_PXP_OUT_LRC_Y 0x00003FFF ++#define BF_PXP_OUT_LRC_Y(v) \ ++ (((v) << 0) & BM_PXP_OUT_LRC_Y) ++ ++#define HW_PXP_OUT_PS_ULC (0x00000070) ++ ++#define BP_PXP_OUT_PS_ULC_RSVD1 30 ++#define BM_PXP_OUT_PS_ULC_RSVD1 0xC0000000 ++#define BF_PXP_OUT_PS_ULC_RSVD1(v) \ ++ (((v) << 30) & BM_PXP_OUT_PS_ULC_RSVD1) ++#define BP_PXP_OUT_PS_ULC_X 16 ++#define BM_PXP_OUT_PS_ULC_X 0x3FFF0000 ++#define BF_PXP_OUT_PS_ULC_X(v) \ ++ (((v) << 16) & BM_PXP_OUT_PS_ULC_X) ++#define BP_PXP_OUT_PS_ULC_RSVD0 14 ++#define BM_PXP_OUT_PS_ULC_RSVD0 0x0000C000 ++#define BF_PXP_OUT_PS_ULC_RSVD0(v) \ ++ (((v) << 14) & BM_PXP_OUT_PS_ULC_RSVD0) ++#define BP_PXP_OUT_PS_ULC_Y 0 ++#define BM_PXP_OUT_PS_ULC_Y 0x00003FFF ++#define BF_PXP_OUT_PS_ULC_Y(v) \ ++ (((v) << 0) & BM_PXP_OUT_PS_ULC_Y) ++ ++#define HW_PXP_OUT_PS_LRC (0x00000080) ++ ++#define BP_PXP_OUT_PS_LRC_RSVD1 30 ++#define BM_PXP_OUT_PS_LRC_RSVD1 0xC0000000 ++#define BF_PXP_OUT_PS_LRC_RSVD1(v) \ ++ (((v) << 30) & BM_PXP_OUT_PS_LRC_RSVD1) ++#define BP_PXP_OUT_PS_LRC_X 16 ++#define BM_PXP_OUT_PS_LRC_X 0x3FFF0000 ++#define BF_PXP_OUT_PS_LRC_X(v) \ ++ (((v) << 16) & BM_PXP_OUT_PS_LRC_X) ++#define BP_PXP_OUT_PS_LRC_RSVD0 14 ++#define BM_PXP_OUT_PS_LRC_RSVD0 0x0000C000 ++#define BF_PXP_OUT_PS_LRC_RSVD0(v) \ ++ (((v) << 14) & BM_PXP_OUT_PS_LRC_RSVD0) ++#define BP_PXP_OUT_PS_LRC_Y 0 ++#define BM_PXP_OUT_PS_LRC_Y 0x00003FFF ++#define BF_PXP_OUT_PS_LRC_Y(v) \ ++ (((v) << 0) & BM_PXP_OUT_PS_LRC_Y) ++ ++#define HW_PXP_OUT_AS_ULC (0x00000090) ++ ++#define BP_PXP_OUT_AS_ULC_RSVD1 30 ++#define BM_PXP_OUT_AS_ULC_RSVD1 0xC0000000 ++#define BF_PXP_OUT_AS_ULC_RSVD1(v) \ ++ (((v) << 30) & BM_PXP_OUT_AS_ULC_RSVD1) ++#define BP_PXP_OUT_AS_ULC_X 16 ++#define BM_PXP_OUT_AS_ULC_X 0x3FFF0000 ++#define BF_PXP_OUT_AS_ULC_X(v) \ ++ (((v) << 16) & BM_PXP_OUT_AS_ULC_X) ++#define BP_PXP_OUT_AS_ULC_RSVD0 14 ++#define BM_PXP_OUT_AS_ULC_RSVD0 0x0000C000 ++#define BF_PXP_OUT_AS_ULC_RSVD0(v) \ ++ (((v) << 14) & BM_PXP_OUT_AS_ULC_RSVD0) ++#define BP_PXP_OUT_AS_ULC_Y 0 ++#define BM_PXP_OUT_AS_ULC_Y 0x00003FFF ++#define BF_PXP_OUT_AS_ULC_Y(v) \ ++ (((v) << 0) & BM_PXP_OUT_AS_ULC_Y) ++ ++#define HW_PXP_OUT_AS_LRC (0x000000a0) ++ ++#define BP_PXP_OUT_AS_LRC_RSVD1 30 ++#define BM_PXP_OUT_AS_LRC_RSVD1 0xC0000000 ++#define BF_PXP_OUT_AS_LRC_RSVD1(v) \ ++ (((v) << 30) & BM_PXP_OUT_AS_LRC_RSVD1) ++#define BP_PXP_OUT_AS_LRC_X 16 ++#define BM_PXP_OUT_AS_LRC_X 0x3FFF0000 ++#define BF_PXP_OUT_AS_LRC_X(v) \ ++ (((v) << 16) & BM_PXP_OUT_AS_LRC_X) ++#define BP_PXP_OUT_AS_LRC_RSVD0 14 ++#define BM_PXP_OUT_AS_LRC_RSVD0 0x0000C000 ++#define BF_PXP_OUT_AS_LRC_RSVD0(v) \ ++ (((v) << 14) & BM_PXP_OUT_AS_LRC_RSVD0) ++#define BP_PXP_OUT_AS_LRC_Y 0 ++#define BM_PXP_OUT_AS_LRC_Y 0x00003FFF ++#define BF_PXP_OUT_AS_LRC_Y(v) \ ++ (((v) << 0) & BM_PXP_OUT_AS_LRC_Y) ++ ++#define HW_PXP_PS_CTRL (0x000000b0) ++#define HW_PXP_PS_CTRL_SET (0x000000b4) ++#define HW_PXP_PS_CTRL_CLR (0x000000b8) ++#define HW_PXP_PS_CTRL_TOG (0x000000bc) ++ ++#define BP_PXP_PS_CTRL_RSVD1 12 ++#define BM_PXP_PS_CTRL_RSVD1 0xFFFFF000 ++#define BF_PXP_PS_CTRL_RSVD1(v) \ ++ (((v) << 12) & BM_PXP_PS_CTRL_RSVD1) ++#define BP_PXP_PS_CTRL_DECX 10 ++#define BM_PXP_PS_CTRL_DECX 0x00000C00 ++#define BF_PXP_PS_CTRL_DECX(v) \ ++ (((v) << 10) & BM_PXP_PS_CTRL_DECX) ++#define BV_PXP_PS_CTRL_DECX__DISABLE 0x0 ++#define BV_PXP_PS_CTRL_DECX__DECX2 0x1 ++#define BV_PXP_PS_CTRL_DECX__DECX4 0x2 ++#define BV_PXP_PS_CTRL_DECX__DECX8 0x3 ++#define BP_PXP_PS_CTRL_DECY 8 ++#define BM_PXP_PS_CTRL_DECY 0x00000300 ++#define BF_PXP_PS_CTRL_DECY(v) \ ++ (((v) << 8) & BM_PXP_PS_CTRL_DECY) ++#define BV_PXP_PS_CTRL_DECY__DISABLE 0x0 ++#define BV_PXP_PS_CTRL_DECY__DECY2 0x1 ++#define BV_PXP_PS_CTRL_DECY__DECY4 0x2 ++#define BV_PXP_PS_CTRL_DECY__DECY8 0x3 ++#define BP_PXP_PS_CTRL_SWAP 5 ++#define BM_PXP_PS_CTRL_SWAP 0x000000E0 ++#define BF_PXP_PS_CTRL_SWAP(v) \ ++ (((v) << 5) & BM_PXP_PS_CTRL_SWAP) ++#define BP_PXP_PS_CTRL_FORMAT 0 ++#define BM_PXP_PS_CTRL_FORMAT 0x0000001F ++#define BF_PXP_PS_CTRL_FORMAT(v) \ ++ (((v) << 0) & BM_PXP_PS_CTRL_FORMAT) ++#define BV_PXP_PS_CTRL_FORMAT__RGB888 0x4 ++#define BV_PXP_PS_CTRL_FORMAT__RGB555 0xC ++#define BV_PXP_PS_CTRL_FORMAT__RGB444 0xD ++#define BV_PXP_PS_CTRL_FORMAT__RGB565 0xE ++#define BV_PXP_PS_CTRL_FORMAT__YUV1P444 0x10 ++#define BV_PXP_PS_CTRL_FORMAT__UYVY1P422 0x12 ++#define BV_PXP_PS_CTRL_FORMAT__VYUY1P422 0x13 ++#define BV_PXP_PS_CTRL_FORMAT__Y8 0x14 ++#define BV_PXP_PS_CTRL_FORMAT__Y4 0x15 ++#define BV_PXP_PS_CTRL_FORMAT__YUV2P422 0x18 ++#define BV_PXP_PS_CTRL_FORMAT__YUV2P420 0x19 ++#define BV_PXP_PS_CTRL_FORMAT__YVU2P422 0x1A ++#define BV_PXP_PS_CTRL_FORMAT__YVU2P420 0x1B ++#define BV_PXP_PS_CTRL_FORMAT__YUV422 0x1E ++#define BV_PXP_PS_CTRL_FORMAT__YUV420 0x1F ++ ++#define HW_PXP_PS_BUF (0x000000c0) ++ ++#define BP_PXP_PS_BUF_ADDR 0 ++#define BM_PXP_PS_BUF_ADDR 0xFFFFFFFF ++#define BF_PXP_PS_BUF_ADDR(v) (v) ++ ++#define HW_PXP_PS_UBUF (0x000000d0) ++ ++#define BP_PXP_PS_UBUF_ADDR 0 ++#define BM_PXP_PS_UBUF_ADDR 0xFFFFFFFF ++#define BF_PXP_PS_UBUF_ADDR(v) (v) ++ ++#define HW_PXP_PS_VBUF (0x000000e0) ++ ++#define BP_PXP_PS_VBUF_ADDR 0 ++#define BM_PXP_PS_VBUF_ADDR 0xFFFFFFFF ++#define BF_PXP_PS_VBUF_ADDR(v) (v) ++ ++#define HW_PXP_PS_PITCH (0x000000f0) ++ ++#define BP_PXP_PS_PITCH_RSVD 16 ++#define BM_PXP_PS_PITCH_RSVD 0xFFFF0000 ++#define BF_PXP_PS_PITCH_RSVD(v) \ ++ (((v) << 16) & BM_PXP_PS_PITCH_RSVD) ++#define BP_PXP_PS_PITCH_PITCH 0 ++#define BM_PXP_PS_PITCH_PITCH 0x0000FFFF ++#define BF_PXP_PS_PITCH_PITCH(v) \ ++ (((v) << 0) & BM_PXP_PS_PITCH_PITCH) ++ ++#define HW_PXP_PS_BACKGROUND (0x00000100) ++ ++#define BP_PXP_PS_BACKGROUND_RSVD 24 ++#define BM_PXP_PS_BACKGROUND_RSVD 0xFF000000 ++#define BF_PXP_PS_BACKGROUND_RSVD(v) \ ++ (((v) << 24) & BM_PXP_PS_BACKGROUND_RSVD) ++#define BP_PXP_PS_BACKGROUND_COLOR 0 ++#define BM_PXP_PS_BACKGROUND_COLOR 0x00FFFFFF ++#define BF_PXP_PS_BACKGROUND_COLOR(v) \ ++ (((v) << 0) & BM_PXP_PS_BACKGROUND_COLOR) ++ ++#define HW_PXP_PS_SCALE (0x00000110) ++ ++#define BM_PXP_PS_SCALE_RSVD2 0x80000000 ++#define BP_PXP_PS_SCALE_YSCALE 16 ++#define BM_PXP_PS_SCALE_YSCALE 0x7FFF0000 ++#define BF_PXP_PS_SCALE_YSCALE(v) \ ++ (((v) << 16) & BM_PXP_PS_SCALE_YSCALE) ++#define BM_PXP_PS_SCALE_RSVD1 0x00008000 ++#define BP_PXP_PS_SCALE_XSCALE 0 ++#define BM_PXP_PS_SCALE_XSCALE 0x00007FFF ++#define BF_PXP_PS_SCALE_XSCALE(v) \ ++ (((v) << 0) & BM_PXP_PS_SCALE_XSCALE) ++ ++#define HW_PXP_PS_OFFSET (0x00000120) ++ ++#define BP_PXP_PS_OFFSET_RSVD2 28 ++#define BM_PXP_PS_OFFSET_RSVD2 0xF0000000 ++#define BF_PXP_PS_OFFSET_RSVD2(v) \ ++ (((v) << 28) & BM_PXP_PS_OFFSET_RSVD2) ++#define BP_PXP_PS_OFFSET_YOFFSET 16 ++#define BM_PXP_PS_OFFSET_YOFFSET 0x0FFF0000 ++#define BF_PXP_PS_OFFSET_YOFFSET(v) \ ++ (((v) << 16) & BM_PXP_PS_OFFSET_YOFFSET) ++#define BP_PXP_PS_OFFSET_RSVD1 12 ++#define BM_PXP_PS_OFFSET_RSVD1 0x0000F000 ++#define BF_PXP_PS_OFFSET_RSVD1(v) \ ++ (((v) << 12) & BM_PXP_PS_OFFSET_RSVD1) ++#define BP_PXP_PS_OFFSET_XOFFSET 0 ++#define BM_PXP_PS_OFFSET_XOFFSET 0x00000FFF ++#define BF_PXP_PS_OFFSET_XOFFSET(v) \ ++ (((v) << 0) & BM_PXP_PS_OFFSET_XOFFSET) ++ ++#define HW_PXP_PS_CLRKEYLOW (0x00000130) ++ ++#define BP_PXP_PS_CLRKEYLOW_RSVD1 24 ++#define BM_PXP_PS_CLRKEYLOW_RSVD1 0xFF000000 ++#define BF_PXP_PS_CLRKEYLOW_RSVD1(v) \ ++ (((v) << 24) & BM_PXP_PS_CLRKEYLOW_RSVD1) ++#define BP_PXP_PS_CLRKEYLOW_PIXEL 0 ++#define BM_PXP_PS_CLRKEYLOW_PIXEL 0x00FFFFFF ++#define BF_PXP_PS_CLRKEYLOW_PIXEL(v) \ ++ (((v) << 0) & BM_PXP_PS_CLRKEYLOW_PIXEL) ++ ++#define HW_PXP_PS_CLRKEYHIGH (0x00000140) ++ ++#define BP_PXP_PS_CLRKEYHIGH_RSVD1 24 ++#define BM_PXP_PS_CLRKEYHIGH_RSVD1 0xFF000000 ++#define BF_PXP_PS_CLRKEYHIGH_RSVD1(v) \ ++ (((v) << 24) & BM_PXP_PS_CLRKEYHIGH_RSVD1) ++#define BP_PXP_PS_CLRKEYHIGH_PIXEL 0 ++#define BM_PXP_PS_CLRKEYHIGH_PIXEL 0x00FFFFFF ++#define BF_PXP_PS_CLRKEYHIGH_PIXEL(v) \ ++ (((v) << 0) & BM_PXP_PS_CLRKEYHIGH_PIXEL) ++ ++#define HW_PXP_AS_CTRL (0x00000150) ++ ++#define BP_PXP_AS_CTRL_RSVD1 21 ++#define BM_PXP_AS_CTRL_RSVD1 0xFFE00000 ++#define BF_PXP_AS_CTRL_RSVD1(v) \ ++ (((v) << 21) & BM_PXP_AS_CTRL_RSVD1) ++#define BM_PXP_AS_CTRL_ALPHA_INVERT 0x00100000 ++#define BP_PXP_AS_CTRL_ROP 16 ++#define BM_PXP_AS_CTRL_ROP 0x000F0000 ++#define BF_PXP_AS_CTRL_ROP(v) \ ++ (((v) << 16) & BM_PXP_AS_CTRL_ROP) ++#define BV_PXP_AS_CTRL_ROP__MASKAS 0x0 ++#define BV_PXP_AS_CTRL_ROP__MASKNOTAS 0x1 ++#define BV_PXP_AS_CTRL_ROP__MASKASNOT 0x2 ++#define BV_PXP_AS_CTRL_ROP__MERGEAS 0x3 ++#define BV_PXP_AS_CTRL_ROP__MERGENOTAS 0x4 ++#define BV_PXP_AS_CTRL_ROP__MERGEASNOT 0x5 ++#define BV_PXP_AS_CTRL_ROP__NOTCOPYAS 0x6 ++#define BV_PXP_AS_CTRL_ROP__NOT 0x7 ++#define BV_PXP_AS_CTRL_ROP__NOTMASKAS 0x8 ++#define BV_PXP_AS_CTRL_ROP__NOTMERGEAS 0x9 ++#define BV_PXP_AS_CTRL_ROP__XORAS 0xA ++#define BV_PXP_AS_CTRL_ROP__NOTXORAS 0xB ++#define BP_PXP_AS_CTRL_ALPHA 8 ++#define BM_PXP_AS_CTRL_ALPHA 0x0000FF00 ++#define BF_PXP_AS_CTRL_ALPHA(v) \ ++ (((v) << 8) & BM_PXP_AS_CTRL_ALPHA) ++#define BP_PXP_AS_CTRL_FORMAT 4 ++#define BM_PXP_AS_CTRL_FORMAT 0x000000F0 ++#define BF_PXP_AS_CTRL_FORMAT(v) \ ++ (((v) << 4) & BM_PXP_AS_CTRL_FORMAT) ++#define BV_PXP_AS_CTRL_FORMAT__ARGB8888 0x0 ++#define BV_PXP_AS_CTRL_FORMAT__RGB888 0x4 ++#define BV_PXP_AS_CTRL_FORMAT__ARGB1555 0x8 ++#define BV_PXP_AS_CTRL_FORMAT__ARGB4444 0x9 ++#define BV_PXP_AS_CTRL_FORMAT__RGB555 0xC ++#define BV_PXP_AS_CTRL_FORMAT__RGB444 0xD ++#define BV_PXP_AS_CTRL_FORMAT__RGB565 0xE ++#define BM_PXP_AS_CTRL_ENABLE_COLORKEY 0x00000008 ++#define BP_PXP_AS_CTRL_ALPHA_CTRL 1 ++#define BM_PXP_AS_CTRL_ALPHA_CTRL 0x00000006 ++#define BF_PXP_AS_CTRL_ALPHA_CTRL(v) \ ++ (((v) << 1) & BM_PXP_AS_CTRL_ALPHA_CTRL) ++#define BV_PXP_AS_CTRL_ALPHA_CTRL__Embedded 0x0 ++#define BV_PXP_AS_CTRL_ALPHA_CTRL__Override 0x1 ++#define BV_PXP_AS_CTRL_ALPHA_CTRL__Multiply 0x2 ++#define BV_PXP_AS_CTRL_ALPHA_CTRL__ROPs 0x3 ++#define BM_PXP_AS_CTRL_RSVD0 0x00000001 ++ ++#define HW_PXP_AS_BUF (0x00000160) ++ ++#define BP_PXP_AS_BUF_ADDR 0 ++#define BM_PXP_AS_BUF_ADDR 0xFFFFFFFF ++#define BF_PXP_AS_BUF_ADDR(v) (v) ++ ++#define HW_PXP_AS_PITCH (0x00000170) ++ ++#define BP_PXP_AS_PITCH_RSVD 16 ++#define BM_PXP_AS_PITCH_RSVD 0xFFFF0000 ++#define BF_PXP_AS_PITCH_RSVD(v) \ ++ (((v) << 16) & BM_PXP_AS_PITCH_RSVD) ++#define BP_PXP_AS_PITCH_PITCH 0 ++#define BM_PXP_AS_PITCH_PITCH 0x0000FFFF ++#define BF_PXP_AS_PITCH_PITCH(v) \ ++ (((v) << 0) & BM_PXP_AS_PITCH_PITCH) ++ ++#define HW_PXP_AS_CLRKEYLOW (0x00000180) ++ ++#define BP_PXP_AS_CLRKEYLOW_RSVD1 24 ++#define BM_PXP_AS_CLRKEYLOW_RSVD1 0xFF000000 ++#define BF_PXP_AS_CLRKEYLOW_RSVD1(v) \ ++ (((v) << 24) & BM_PXP_AS_CLRKEYLOW_RSVD1) ++#define BP_PXP_AS_CLRKEYLOW_PIXEL 0 ++#define BM_PXP_AS_CLRKEYLOW_PIXEL 0x00FFFFFF ++#define BF_PXP_AS_CLRKEYLOW_PIXEL(v) \ ++ (((v) << 0) & BM_PXP_AS_CLRKEYLOW_PIXEL) ++ ++#define HW_PXP_AS_CLRKEYHIGH (0x00000190) ++ ++#define BP_PXP_AS_CLRKEYHIGH_RSVD1 24 ++#define BM_PXP_AS_CLRKEYHIGH_RSVD1 0xFF000000 ++#define BF_PXP_AS_CLRKEYHIGH_RSVD1(v) \ ++ (((v) << 24) & BM_PXP_AS_CLRKEYHIGH_RSVD1) ++#define BP_PXP_AS_CLRKEYHIGH_PIXEL 0 ++#define BM_PXP_AS_CLRKEYHIGH_PIXEL 0x00FFFFFF ++#define BF_PXP_AS_CLRKEYHIGH_PIXEL(v) \ ++ (((v) << 0) & BM_PXP_AS_CLRKEYHIGH_PIXEL) ++ ++#define HW_PXP_CSC1_COEF0 (0x000001a0) ++ ++#define BM_PXP_CSC1_COEF0_YCBCR_MODE 0x80000000 ++#define BM_PXP_CSC1_COEF0_BYPASS 0x40000000 ++#define BM_PXP_CSC1_COEF0_RSVD1 0x20000000 ++#define BP_PXP_CSC1_COEF0_C0 18 ++#define BM_PXP_CSC1_COEF0_C0 0x1FFC0000 ++#define BF_PXP_CSC1_COEF0_C0(v) \ ++ (((v) << 18) & BM_PXP_CSC1_COEF0_C0) ++#define BP_PXP_CSC1_COEF0_UV_OFFSET 9 ++#define BM_PXP_CSC1_COEF0_UV_OFFSET 0x0003FE00 ++#define BF_PXP_CSC1_COEF0_UV_OFFSET(v) \ ++ (((v) << 9) & BM_PXP_CSC1_COEF0_UV_OFFSET) ++#define BP_PXP_CSC1_COEF0_Y_OFFSET 0 ++#define BM_PXP_CSC1_COEF0_Y_OFFSET 0x000001FF ++#define BF_PXP_CSC1_COEF0_Y_OFFSET(v) \ ++ (((v) << 0) & BM_PXP_CSC1_COEF0_Y_OFFSET) ++ ++#define HW_PXP_CSC1_COEF1 (0x000001b0) ++ ++#define BP_PXP_CSC1_COEF1_RSVD1 27 ++#define BM_PXP_CSC1_COEF1_RSVD1 0xF8000000 ++#define BF_PXP_CSC1_COEF1_RSVD1(v) \ ++ (((v) << 27) & BM_PXP_CSC1_COEF1_RSVD1) ++#define BP_PXP_CSC1_COEF1_C1 16 ++#define BM_PXP_CSC1_COEF1_C1 0x07FF0000 ++#define BF_PXP_CSC1_COEF1_C1(v) \ ++ (((v) << 16) & BM_PXP_CSC1_COEF1_C1) ++#define BP_PXP_CSC1_COEF1_RSVD0 11 ++#define BM_PXP_CSC1_COEF1_RSVD0 0x0000F800 ++#define BF_PXP_CSC1_COEF1_RSVD0(v) \ ++ (((v) << 11) & BM_PXP_CSC1_COEF1_RSVD0) ++#define BP_PXP_CSC1_COEF1_C4 0 ++#define BM_PXP_CSC1_COEF1_C4 0x000007FF ++#define BF_PXP_CSC1_COEF1_C4(v) \ ++ (((v) << 0) & BM_PXP_CSC1_COEF1_C4) ++ ++#define HW_PXP_CSC1_COEF2 (0x000001c0) ++ ++#define BP_PXP_CSC1_COEF2_RSVD1 27 ++#define BM_PXP_CSC1_COEF2_RSVD1 0xF8000000 ++#define BF_PXP_CSC1_COEF2_RSVD1(v) \ ++ (((v) << 27) & BM_PXP_CSC1_COEF2_RSVD1) ++#define BP_PXP_CSC1_COEF2_C2 16 ++#define BM_PXP_CSC1_COEF2_C2 0x07FF0000 ++#define BF_PXP_CSC1_COEF2_C2(v) \ ++ (((v) << 16) & BM_PXP_CSC1_COEF2_C2) ++#define BP_PXP_CSC1_COEF2_RSVD0 11 ++#define BM_PXP_CSC1_COEF2_RSVD0 0x0000F800 ++#define BF_PXP_CSC1_COEF2_RSVD0(v) \ ++ (((v) << 11) & BM_PXP_CSC1_COEF2_RSVD0) ++#define BP_PXP_CSC1_COEF2_C3 0 ++#define BM_PXP_CSC1_COEF2_C3 0x000007FF ++#define BF_PXP_CSC1_COEF2_C3(v) \ ++ (((v) << 0) & BM_PXP_CSC1_COEF2_C3) ++ ++#define HW_PXP_CSC2_CTRL (0x000001d0) ++ ++#define BP_PXP_CSC2_CTRL_RSVD 3 ++#define BM_PXP_CSC2_CTRL_RSVD 0xFFFFFFF8 ++#define BF_PXP_CSC2_CTRL_RSVD(v) \ ++ (((v) << 3) & BM_PXP_CSC2_CTRL_RSVD) ++#define BP_PXP_CSC2_CTRL_CSC_MODE 1 ++#define BM_PXP_CSC2_CTRL_CSC_MODE 0x00000006 ++#define BF_PXP_CSC2_CTRL_CSC_MODE(v) \ ++ (((v) << 1) & BM_PXP_CSC2_CTRL_CSC_MODE) ++#define BV_PXP_CSC2_CTRL_CSC_MODE__YUV2RGB 0x0 ++#define BV_PXP_CSC2_CTRL_CSC_MODE__YCbCr2RGB 0x1 ++#define BV_PXP_CSC2_CTRL_CSC_MODE__RGB2YUV 0x2 ++#define BV_PXP_CSC2_CTRL_CSC_MODE__RGB2YCbCr 0x3 ++#define BM_PXP_CSC2_CTRL_BYPASS 0x00000001 ++ ++#define HW_PXP_CSC2_COEF0 (0x000001e0) ++ ++#define BP_PXP_CSC2_COEF0_RSVD1 27 ++#define BM_PXP_CSC2_COEF0_RSVD1 0xF8000000 ++#define BF_PXP_CSC2_COEF0_RSVD1(v) \ ++ (((v) << 27) & BM_PXP_CSC2_COEF0_RSVD1) ++#define BP_PXP_CSC2_COEF0_A2 16 ++#define BM_PXP_CSC2_COEF0_A2 0x07FF0000 ++#define BF_PXP_CSC2_COEF0_A2(v) \ ++ (((v) << 16) & BM_PXP_CSC2_COEF0_A2) ++#define BP_PXP_CSC2_COEF0_RSVD0 11 ++#define BM_PXP_CSC2_COEF0_RSVD0 0x0000F800 ++#define BF_PXP_CSC2_COEF0_RSVD0(v) \ ++ (((v) << 11) & BM_PXP_CSC2_COEF0_RSVD0) ++#define BP_PXP_CSC2_COEF0_A1 0 ++#define BM_PXP_CSC2_COEF0_A1 0x000007FF ++#define BF_PXP_CSC2_COEF0_A1(v) \ ++ (((v) << 0) & BM_PXP_CSC2_COEF0_A1) ++ ++#define HW_PXP_CSC2_COEF1 (0x000001f0) ++ ++#define BP_PXP_CSC2_COEF1_RSVD1 27 ++#define BM_PXP_CSC2_COEF1_RSVD1 0xF8000000 ++#define BF_PXP_CSC2_COEF1_RSVD1(v) \ ++ (((v) << 27) & BM_PXP_CSC2_COEF1_RSVD1) ++#define BP_PXP_CSC2_COEF1_B1 16 ++#define BM_PXP_CSC2_COEF1_B1 0x07FF0000 ++#define BF_PXP_CSC2_COEF1_B1(v) \ ++ (((v) << 16) & BM_PXP_CSC2_COEF1_B1) ++#define BP_PXP_CSC2_COEF1_RSVD0 11 ++#define BM_PXP_CSC2_COEF1_RSVD0 0x0000F800 ++#define BF_PXP_CSC2_COEF1_RSVD0(v) \ ++ (((v) << 11) & BM_PXP_CSC2_COEF1_RSVD0) ++#define BP_PXP_CSC2_COEF1_A3 0 ++#define BM_PXP_CSC2_COEF1_A3 0x000007FF ++#define BF_PXP_CSC2_COEF1_A3(v) \ ++ (((v) << 0) & BM_PXP_CSC2_COEF1_A3) ++ ++#define HW_PXP_CSC2_COEF2 (0x00000200) ++ ++#define BP_PXP_CSC2_COEF2_RSVD1 27 ++#define BM_PXP_CSC2_COEF2_RSVD1 0xF8000000 ++#define BF_PXP_CSC2_COEF2_RSVD1(v) \ ++ (((v) << 27) & BM_PXP_CSC2_COEF2_RSVD1) ++#define BP_PXP_CSC2_COEF2_B3 16 ++#define BM_PXP_CSC2_COEF2_B3 0x07FF0000 ++#define BF_PXP_CSC2_COEF2_B3(v) \ ++ (((v) << 16) & BM_PXP_CSC2_COEF2_B3) ++#define BP_PXP_CSC2_COEF2_RSVD0 11 ++#define BM_PXP_CSC2_COEF2_RSVD0 0x0000F800 ++#define BF_PXP_CSC2_COEF2_RSVD0(v) \ ++ (((v) << 11) & BM_PXP_CSC2_COEF2_RSVD0) ++#define BP_PXP_CSC2_COEF2_B2 0 ++#define BM_PXP_CSC2_COEF2_B2 0x000007FF ++#define BF_PXP_CSC2_COEF2_B2(v) \ ++ (((v) << 0) & BM_PXP_CSC2_COEF2_B2) ++ ++#define HW_PXP_CSC2_COEF3 (0x00000210) ++ ++#define BP_PXP_CSC2_COEF3_RSVD1 27 ++#define BM_PXP_CSC2_COEF3_RSVD1 0xF8000000 ++#define BF_PXP_CSC2_COEF3_RSVD1(v) \ ++ (((v) << 27) & BM_PXP_CSC2_COEF3_RSVD1) ++#define BP_PXP_CSC2_COEF3_C2 16 ++#define BM_PXP_CSC2_COEF3_C2 0x07FF0000 ++#define BF_PXP_CSC2_COEF3_C2(v) \ ++ (((v) << 16) & BM_PXP_CSC2_COEF3_C2) ++#define BP_PXP_CSC2_COEF3_RSVD0 11 ++#define BM_PXP_CSC2_COEF3_RSVD0 0x0000F800 ++#define BF_PXP_CSC2_COEF3_RSVD0(v) \ ++ (((v) << 11) & BM_PXP_CSC2_COEF3_RSVD0) ++#define BP_PXP_CSC2_COEF3_C1 0 ++#define BM_PXP_CSC2_COEF3_C1 0x000007FF ++#define BF_PXP_CSC2_COEF3_C1(v) \ ++ (((v) << 0) & BM_PXP_CSC2_COEF3_C1) ++ ++#define HW_PXP_CSC2_COEF4 (0x00000220) ++ ++#define BP_PXP_CSC2_COEF4_RSVD1 25 ++#define BM_PXP_CSC2_COEF4_RSVD1 0xFE000000 ++#define BF_PXP_CSC2_COEF4_RSVD1(v) \ ++ (((v) << 25) & BM_PXP_CSC2_COEF4_RSVD1) ++#define BP_PXP_CSC2_COEF4_D1 16 ++#define BM_PXP_CSC2_COEF4_D1 0x01FF0000 ++#define BF_PXP_CSC2_COEF4_D1(v) \ ++ (((v) << 16) & BM_PXP_CSC2_COEF4_D1) ++#define BP_PXP_CSC2_COEF4_RSVD0 11 ++#define BM_PXP_CSC2_COEF4_RSVD0 0x0000F800 ++#define BF_PXP_CSC2_COEF4_RSVD0(v) \ ++ (((v) << 11) & BM_PXP_CSC2_COEF4_RSVD0) ++#define BP_PXP_CSC2_COEF4_C3 0 ++#define BM_PXP_CSC2_COEF4_C3 0x000007FF ++#define BF_PXP_CSC2_COEF4_C3(v) \ ++ (((v) << 0) & BM_PXP_CSC2_COEF4_C3) ++ ++#define HW_PXP_CSC2_COEF5 (0x00000230) ++ ++#define BP_PXP_CSC2_COEF5_RSVD1 25 ++#define BM_PXP_CSC2_COEF5_RSVD1 0xFE000000 ++#define BF_PXP_CSC2_COEF5_RSVD1(v) \ ++ (((v) << 25) & BM_PXP_CSC2_COEF5_RSVD1) ++#define BP_PXP_CSC2_COEF5_D3 16 ++#define BM_PXP_CSC2_COEF5_D3 0x01FF0000 ++#define BF_PXP_CSC2_COEF5_D3(v) \ ++ (((v) << 16) & BM_PXP_CSC2_COEF5_D3) ++#define BP_PXP_CSC2_COEF5_RSVD0 9 ++#define BM_PXP_CSC2_COEF5_RSVD0 0x0000FE00 ++#define BF_PXP_CSC2_COEF5_RSVD0(v) \ ++ (((v) << 9) & BM_PXP_CSC2_COEF5_RSVD0) ++#define BP_PXP_CSC2_COEF5_D2 0 ++#define BM_PXP_CSC2_COEF5_D2 0x000001FF ++#define BF_PXP_CSC2_COEF5_D2(v) \ ++ (((v) << 0) & BM_PXP_CSC2_COEF5_D2) ++ ++#define HW_PXP_LUT_CTRL (0x00000240) ++ ++#define BM_PXP_LUT_CTRL_BYPASS 0x80000000 ++#define BP_PXP_LUT_CTRL_RSVD3 26 ++#define BM_PXP_LUT_CTRL_RSVD3 0x7C000000 ++#define BF_PXP_LUT_CTRL_RSVD3(v) \ ++ (((v) << 26) & BM_PXP_LUT_CTRL_RSVD3) ++#define BP_PXP_LUT_CTRL_LOOKUP_MODE 24 ++#define BM_PXP_LUT_CTRL_LOOKUP_MODE 0x03000000 ++#define BF_PXP_LUT_CTRL_LOOKUP_MODE(v) \ ++ (((v) << 24) & BM_PXP_LUT_CTRL_LOOKUP_MODE) ++#define BV_PXP_LUT_CTRL_LOOKUP_MODE__CACHE_RGB565 0x0 ++#define BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8 0x1 ++#define BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_RGB444 0x2 ++#define BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_RGB454 0x3 ++#define BP_PXP_LUT_CTRL_RSVD2 18 ++#define BM_PXP_LUT_CTRL_RSVD2 0x00FC0000 ++#define BF_PXP_LUT_CTRL_RSVD2(v) \ ++ (((v) << 18) & BM_PXP_LUT_CTRL_RSVD2) ++#define BP_PXP_LUT_CTRL_OUT_MODE 16 ++#define BM_PXP_LUT_CTRL_OUT_MODE 0x00030000 ++#define BF_PXP_LUT_CTRL_OUT_MODE(v) \ ++ (((v) << 16) & BM_PXP_LUT_CTRL_OUT_MODE) ++#define BV_PXP_LUT_CTRL_OUT_MODE__RESERVED 0x0 ++#define BV_PXP_LUT_CTRL_OUT_MODE__Y8 0x1 ++#define BV_PXP_LUT_CTRL_OUT_MODE__RGBW4444CFA 0x2 ++#define BV_PXP_LUT_CTRL_OUT_MODE__RGB888 0x3 ++#define BP_PXP_LUT_CTRL_RSVD1 11 ++#define BM_PXP_LUT_CTRL_RSVD1 0x0000F800 ++#define BF_PXP_LUT_CTRL_RSVD1(v) \ ++ (((v) << 11) & BM_PXP_LUT_CTRL_RSVD1) ++#define BM_PXP_LUT_CTRL_SEL_8KB 0x00000400 ++#define BM_PXP_LUT_CTRL_LRU_UPD 0x00000200 ++#define BM_PXP_LUT_CTRL_INVALID 0x00000100 ++#define BP_PXP_LUT_CTRL_RSVD0 1 ++#define BM_PXP_LUT_CTRL_RSVD0 0x000000FE ++#define BF_PXP_LUT_CTRL_RSVD0(v) \ ++ (((v) << 1) & BM_PXP_LUT_CTRL_RSVD0) ++#define BM_PXP_LUT_CTRL_DMA_START 0x00000001 ++ ++#define HW_PXP_LUT_ADDR (0x00000250) ++ ++#define BM_PXP_LUT_ADDR_RSVD2 0x80000000 ++#define BP_PXP_LUT_ADDR_NUM_BYTES 16 ++#define BM_PXP_LUT_ADDR_NUM_BYTES 0x7FFF0000 ++#define BF_PXP_LUT_ADDR_NUM_BYTES(v) \ ++ (((v) << 16) & BM_PXP_LUT_ADDR_NUM_BYTES) ++#define BP_PXP_LUT_ADDR_RSVD1 14 ++#define BM_PXP_LUT_ADDR_RSVD1 0x0000C000 ++#define BF_PXP_LUT_ADDR_RSVD1(v) \ ++ (((v) << 14) & BM_PXP_LUT_ADDR_RSVD1) ++#define BP_PXP_LUT_ADDR_ADDR 0 ++#define BM_PXP_LUT_ADDR_ADDR 0x00003FFF ++#define BF_PXP_LUT_ADDR_ADDR(v) \ ++ (((v) << 0) & BM_PXP_LUT_ADDR_ADDR) ++ ++#define HW_PXP_LUT_DATA (0x00000260) ++ ++#define BP_PXP_LUT_DATA_DATA 0 ++#define BM_PXP_LUT_DATA_DATA 0xFFFFFFFF ++#define BF_PXP_LUT_DATA_DATA(v) (v) ++ ++#define HW_PXP_LUT_EXTMEM (0x00000270) ++ ++#define BP_PXP_LUT_EXTMEM_ADDR 0 ++#define BM_PXP_LUT_EXTMEM_ADDR 0xFFFFFFFF ++#define BF_PXP_LUT_EXTMEM_ADDR(v) (v) ++ ++#define HW_PXP_CFA (0x00000280) ++ ++#define BP_PXP_CFA_DATA 0 ++#define BM_PXP_CFA_DATA 0xFFFFFFFF ++#define BF_PXP_CFA_DATA(v) (v) ++ ++#define HW_PXP_HIST_CTRL (0x00000290) ++ ++#define BP_PXP_HIST_CTRL_RSVD 6 ++#define BM_PXP_HIST_CTRL_RSVD 0xFFFFFFC0 ++#define BF_PXP_HIST_CTRL_RSVD(v) \ ++ (((v) << 6) & BM_PXP_HIST_CTRL_RSVD) ++#define BP_PXP_HIST_CTRL_PANEL_MODE 4 ++#define BM_PXP_HIST_CTRL_PANEL_MODE 0x00000030 ++#define BF_PXP_HIST_CTRL_PANEL_MODE(v) \ ++ (((v) << 4) & BM_PXP_HIST_CTRL_PANEL_MODE) ++#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY4 0x0 ++#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY8 0x1 ++#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY16 0x2 ++#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY32 0x3 ++#define BP_PXP_HIST_CTRL_STATUS 0 ++#define BM_PXP_HIST_CTRL_STATUS 0x0000000F ++#define BF_PXP_HIST_CTRL_STATUS(v) \ ++ (((v) << 0) & BM_PXP_HIST_CTRL_STATUS) ++ ++#define HW_PXP_HIST2_PARAM (0x000002a0) ++ ++#define BP_PXP_HIST2_PARAM_RSVD 16 ++#define BM_PXP_HIST2_PARAM_RSVD 0xFFFF0000 ++#define BF_PXP_HIST2_PARAM_RSVD(v) \ ++ (((v) << 16) & BM_PXP_HIST2_PARAM_RSVD) ++#define BP_PXP_HIST2_PARAM_RSVD1 13 ++#define BM_PXP_HIST2_PARAM_RSVD1 0x0000E000 ++#define BF_PXP_HIST2_PARAM_RSVD1(v) \ ++ (((v) << 13) & BM_PXP_HIST2_PARAM_RSVD1) ++#define BP_PXP_HIST2_PARAM_VALUE1 8 ++#define BM_PXP_HIST2_PARAM_VALUE1 0x00001F00 ++#define BF_PXP_HIST2_PARAM_VALUE1(v) \ ++ (((v) << 8) & BM_PXP_HIST2_PARAM_VALUE1) ++#define BP_PXP_HIST2_PARAM_RSVD0 5 ++#define BM_PXP_HIST2_PARAM_RSVD0 0x000000E0 ++#define BF_PXP_HIST2_PARAM_RSVD0(v) \ ++ (((v) << 5) & BM_PXP_HIST2_PARAM_RSVD0) ++#define BP_PXP_HIST2_PARAM_VALUE0 0 ++#define BM_PXP_HIST2_PARAM_VALUE0 0x0000001F ++#define BF_PXP_HIST2_PARAM_VALUE0(v) \ ++ (((v) << 0) & BM_PXP_HIST2_PARAM_VALUE0) ++ ++#define HW_PXP_HIST4_PARAM (0x000002b0) ++ ++#define BP_PXP_HIST4_PARAM_RSVD3 29 ++#define BM_PXP_HIST4_PARAM_RSVD3 0xE0000000 ++#define BF_PXP_HIST4_PARAM_RSVD3(v) \ ++ (((v) << 29) & BM_PXP_HIST4_PARAM_RSVD3) ++#define BP_PXP_HIST4_PARAM_VALUE3 24 ++#define BM_PXP_HIST4_PARAM_VALUE3 0x1F000000 ++#define BF_PXP_HIST4_PARAM_VALUE3(v) \ ++ (((v) << 24) & BM_PXP_HIST4_PARAM_VALUE3) ++#define BP_PXP_HIST4_PARAM_RSVD2 21 ++#define BM_PXP_HIST4_PARAM_RSVD2 0x00E00000 ++#define BF_PXP_HIST4_PARAM_RSVD2(v) \ ++ (((v) << 21) & BM_PXP_HIST4_PARAM_RSVD2) ++#define BP_PXP_HIST4_PARAM_VALUE2 16 ++#define BM_PXP_HIST4_PARAM_VALUE2 0x001F0000 ++#define BF_PXP_HIST4_PARAM_VALUE2(v) \ ++ (((v) << 16) & BM_PXP_HIST4_PARAM_VALUE2) ++#define BP_PXP_HIST4_PARAM_RSVD1 13 ++#define BM_PXP_HIST4_PARAM_RSVD1 0x0000E000 ++#define BF_PXP_HIST4_PARAM_RSVD1(v) \ ++ (((v) << 13) & BM_PXP_HIST4_PARAM_RSVD1) ++#define BP_PXP_HIST4_PARAM_VALUE1 8 ++#define BM_PXP_HIST4_PARAM_VALUE1 0x00001F00 ++#define BF_PXP_HIST4_PARAM_VALUE1(v) \ ++ (((v) << 8) & BM_PXP_HIST4_PARAM_VALUE1) ++#define BP_PXP_HIST4_PARAM_RSVD0 5 ++#define BM_PXP_HIST4_PARAM_RSVD0 0x000000E0 ++#define BF_PXP_HIST4_PARAM_RSVD0(v) \ ++ (((v) << 5) & BM_PXP_HIST4_PARAM_RSVD0) ++#define BP_PXP_HIST4_PARAM_VALUE0 0 ++#define BM_PXP_HIST4_PARAM_VALUE0 0x0000001F ++#define BF_PXP_HIST4_PARAM_VALUE0(v) \ ++ (((v) << 0) & BM_PXP_HIST4_PARAM_VALUE0) ++ ++#define HW_PXP_HIST8_PARAM0 (0x000002c0) ++ ++#define BP_PXP_HIST8_PARAM0_RSVD3 29 ++#define BM_PXP_HIST8_PARAM0_RSVD3 0xE0000000 ++#define BF_PXP_HIST8_PARAM0_RSVD3(v) \ ++ (((v) << 29) & BM_PXP_HIST8_PARAM0_RSVD3) ++#define BP_PXP_HIST8_PARAM0_VALUE3 24 ++#define BM_PXP_HIST8_PARAM0_VALUE3 0x1F000000 ++#define BF_PXP_HIST8_PARAM0_VALUE3(v) \ ++ (((v) << 24) & BM_PXP_HIST8_PARAM0_VALUE3) ++#define BP_PXP_HIST8_PARAM0_RSVD2 21 ++#define BM_PXP_HIST8_PARAM0_RSVD2 0x00E00000 ++#define BF_PXP_HIST8_PARAM0_RSVD2(v) \ ++ (((v) << 21) & BM_PXP_HIST8_PARAM0_RSVD2) ++#define BP_PXP_HIST8_PARAM0_VALUE2 16 ++#define BM_PXP_HIST8_PARAM0_VALUE2 0x001F0000 ++#define BF_PXP_HIST8_PARAM0_VALUE2(v) \ ++ (((v) << 16) & BM_PXP_HIST8_PARAM0_VALUE2) ++#define BP_PXP_HIST8_PARAM0_RSVD1 13 ++#define BM_PXP_HIST8_PARAM0_RSVD1 0x0000E000 ++#define BF_PXP_HIST8_PARAM0_RSVD1(v) \ ++ (((v) << 13) & BM_PXP_HIST8_PARAM0_RSVD1) ++#define BP_PXP_HIST8_PARAM0_VALUE1 8 ++#define BM_PXP_HIST8_PARAM0_VALUE1 0x00001F00 ++#define BF_PXP_HIST8_PARAM0_VALUE1(v) \ ++ (((v) << 8) & BM_PXP_HIST8_PARAM0_VALUE1) ++#define BP_PXP_HIST8_PARAM0_RSVD0 5 ++#define BM_PXP_HIST8_PARAM0_RSVD0 0x000000E0 ++#define BF_PXP_HIST8_PARAM0_RSVD0(v) \ ++ (((v) << 5) & BM_PXP_HIST8_PARAM0_RSVD0) ++#define BP_PXP_HIST8_PARAM0_VALUE0 0 ++#define BM_PXP_HIST8_PARAM0_VALUE0 0x0000001F ++#define BF_PXP_HIST8_PARAM0_VALUE0(v) \ ++ (((v) << 0) & BM_PXP_HIST8_PARAM0_VALUE0) ++ ++#define HW_PXP_HIST8_PARAM1 (0x000002d0) ++ ++#define BP_PXP_HIST8_PARAM1_RSVD7 29 ++#define BM_PXP_HIST8_PARAM1_RSVD7 0xE0000000 ++#define BF_PXP_HIST8_PARAM1_RSVD7(v) \ ++ (((v) << 29) & BM_PXP_HIST8_PARAM1_RSVD7) ++#define BP_PXP_HIST8_PARAM1_VALUE7 24 ++#define BM_PXP_HIST8_PARAM1_VALUE7 0x1F000000 ++#define BF_PXP_HIST8_PARAM1_VALUE7(v) \ ++ (((v) << 24) & BM_PXP_HIST8_PARAM1_VALUE7) ++#define BP_PXP_HIST8_PARAM1_RSVD6 21 ++#define BM_PXP_HIST8_PARAM1_RSVD6 0x00E00000 ++#define BF_PXP_HIST8_PARAM1_RSVD6(v) \ ++ (((v) << 21) & BM_PXP_HIST8_PARAM1_RSVD6) ++#define BP_PXP_HIST8_PARAM1_VALUE6 16 ++#define BM_PXP_HIST8_PARAM1_VALUE6 0x001F0000 ++#define BF_PXP_HIST8_PARAM1_VALUE6(v) \ ++ (((v) << 16) & BM_PXP_HIST8_PARAM1_VALUE6) ++#define BP_PXP_HIST8_PARAM1_RSVD5 13 ++#define BM_PXP_HIST8_PARAM1_RSVD5 0x0000E000 ++#define BF_PXP_HIST8_PARAM1_RSVD5(v) \ ++ (((v) << 13) & BM_PXP_HIST8_PARAM1_RSVD5) ++#define BP_PXP_HIST8_PARAM1_VALUE5 8 ++#define BM_PXP_HIST8_PARAM1_VALUE5 0x00001F00 ++#define BF_PXP_HIST8_PARAM1_VALUE5(v) \ ++ (((v) << 8) & BM_PXP_HIST8_PARAM1_VALUE5) ++#define BP_PXP_HIST8_PARAM1_RSVD4 5 ++#define BM_PXP_HIST8_PARAM1_RSVD4 0x000000E0 ++#define BF_PXP_HIST8_PARAM1_RSVD4(v) \ ++ (((v) << 5) & BM_PXP_HIST8_PARAM1_RSVD4) ++#define BP_PXP_HIST8_PARAM1_VALUE4 0 ++#define BM_PXP_HIST8_PARAM1_VALUE4 0x0000001F ++#define BF_PXP_HIST8_PARAM1_VALUE4(v) \ ++ (((v) << 0) & BM_PXP_HIST8_PARAM1_VALUE4) ++ ++#define HW_PXP_HIST16_PARAM0 (0x000002e0) ++ ++#define BP_PXP_HIST16_PARAM0_RSVD3 29 ++#define BM_PXP_HIST16_PARAM0_RSVD3 0xE0000000 ++#define BF_PXP_HIST16_PARAM0_RSVD3(v) \ ++ (((v) << 29) & BM_PXP_HIST16_PARAM0_RSVD3) ++#define BP_PXP_HIST16_PARAM0_VALUE3 24 ++#define BM_PXP_HIST16_PARAM0_VALUE3 0x1F000000 ++#define BF_PXP_HIST16_PARAM0_VALUE3(v) \ ++ (((v) << 24) & BM_PXP_HIST16_PARAM0_VALUE3) ++#define BP_PXP_HIST16_PARAM0_RSVD2 21 ++#define BM_PXP_HIST16_PARAM0_RSVD2 0x00E00000 ++#define BF_PXP_HIST16_PARAM0_RSVD2(v) \ ++ (((v) << 21) & BM_PXP_HIST16_PARAM0_RSVD2) ++#define BP_PXP_HIST16_PARAM0_VALUE2 16 ++#define BM_PXP_HIST16_PARAM0_VALUE2 0x001F0000 ++#define BF_PXP_HIST16_PARAM0_VALUE2(v) \ ++ (((v) << 16) & BM_PXP_HIST16_PARAM0_VALUE2) ++#define BP_PXP_HIST16_PARAM0_RSVD1 13 ++#define BM_PXP_HIST16_PARAM0_RSVD1 0x0000E000 ++#define BF_PXP_HIST16_PARAM0_RSVD1(v) \ ++ (((v) << 13) & BM_PXP_HIST16_PARAM0_RSVD1) ++#define BP_PXP_HIST16_PARAM0_VALUE1 8 ++#define BM_PXP_HIST16_PARAM0_VALUE1 0x00001F00 ++#define BF_PXP_HIST16_PARAM0_VALUE1(v) \ ++ (((v) << 8) & BM_PXP_HIST16_PARAM0_VALUE1) ++#define BP_PXP_HIST16_PARAM0_RSVD0 5 ++#define BM_PXP_HIST16_PARAM0_RSVD0 0x000000E0 ++#define BF_PXP_HIST16_PARAM0_RSVD0(v) \ ++ (((v) << 5) & BM_PXP_HIST16_PARAM0_RSVD0) ++#define BP_PXP_HIST16_PARAM0_VALUE0 0 ++#define BM_PXP_HIST16_PARAM0_VALUE0 0x0000001F ++#define BF_PXP_HIST16_PARAM0_VALUE0(v) \ ++ (((v) << 0) & BM_PXP_HIST16_PARAM0_VALUE0) ++ ++#define HW_PXP_HIST16_PARAM1 (0x000002f0) ++ ++#define BP_PXP_HIST16_PARAM1_RSVD7 29 ++#define BM_PXP_HIST16_PARAM1_RSVD7 0xE0000000 ++#define BF_PXP_HIST16_PARAM1_RSVD7(v) \ ++ (((v) << 29) & BM_PXP_HIST16_PARAM1_RSVD7) ++#define BP_PXP_HIST16_PARAM1_VALUE7 24 ++#define BM_PXP_HIST16_PARAM1_VALUE7 0x1F000000 ++#define BF_PXP_HIST16_PARAM1_VALUE7(v) \ ++ (((v) << 24) & BM_PXP_HIST16_PARAM1_VALUE7) ++#define BP_PXP_HIST16_PARAM1_RSVD6 21 ++#define BM_PXP_HIST16_PARAM1_RSVD6 0x00E00000 ++#define BF_PXP_HIST16_PARAM1_RSVD6(v) \ ++ (((v) << 21) & BM_PXP_HIST16_PARAM1_RSVD6) ++#define BP_PXP_HIST16_PARAM1_VALUE6 16 ++#define BM_PXP_HIST16_PARAM1_VALUE6 0x001F0000 ++#define BF_PXP_HIST16_PARAM1_VALUE6(v) \ ++ (((v) << 16) & BM_PXP_HIST16_PARAM1_VALUE6) ++#define BP_PXP_HIST16_PARAM1_RSVD5 13 ++#define BM_PXP_HIST16_PARAM1_RSVD5 0x0000E000 ++#define BF_PXP_HIST16_PARAM1_RSVD5(v) \ ++ (((v) << 13) & BM_PXP_HIST16_PARAM1_RSVD5) ++#define BP_PXP_HIST16_PARAM1_VALUE5 8 ++#define BM_PXP_HIST16_PARAM1_VALUE5 0x00001F00 ++#define BF_PXP_HIST16_PARAM1_VALUE5(v) \ ++ (((v) << 8) & BM_PXP_HIST16_PARAM1_VALUE5) ++#define BP_PXP_HIST16_PARAM1_RSVD4 5 ++#define BM_PXP_HIST16_PARAM1_RSVD4 0x000000E0 ++#define BF_PXP_HIST16_PARAM1_RSVD4(v) \ ++ (((v) << 5) & BM_PXP_HIST16_PARAM1_RSVD4) ++#define BP_PXP_HIST16_PARAM1_VALUE4 0 ++#define BM_PXP_HIST16_PARAM1_VALUE4 0x0000001F ++#define BF_PXP_HIST16_PARAM1_VALUE4(v) \ ++ (((v) << 0) & BM_PXP_HIST16_PARAM1_VALUE4) ++ ++#define HW_PXP_HIST16_PARAM2 (0x00000300) ++ ++#define BP_PXP_HIST16_PARAM2_RSVD11 29 ++#define BM_PXP_HIST16_PARAM2_RSVD11 0xE0000000 ++#define BF_PXP_HIST16_PARAM2_RSVD11(v) \ ++ (((v) << 29) & BM_PXP_HIST16_PARAM2_RSVD11) ++#define BP_PXP_HIST16_PARAM2_VALUE11 24 ++#define BM_PXP_HIST16_PARAM2_VALUE11 0x1F000000 ++#define BF_PXP_HIST16_PARAM2_VALUE11(v) \ ++ (((v) << 24) & BM_PXP_HIST16_PARAM2_VALUE11) ++#define BP_PXP_HIST16_PARAM2_RSVD10 21 ++#define BM_PXP_HIST16_PARAM2_RSVD10 0x00E00000 ++#define BF_PXP_HIST16_PARAM2_RSVD10(v) \ ++ (((v) << 21) & BM_PXP_HIST16_PARAM2_RSVD10) ++#define BP_PXP_HIST16_PARAM2_VALUE10 16 ++#define BM_PXP_HIST16_PARAM2_VALUE10 0x001F0000 ++#define BF_PXP_HIST16_PARAM2_VALUE10(v) \ ++ (((v) << 16) & BM_PXP_HIST16_PARAM2_VALUE10) ++#define BP_PXP_HIST16_PARAM2_RSVD9 13 ++#define BM_PXP_HIST16_PARAM2_RSVD9 0x0000E000 ++#define BF_PXP_HIST16_PARAM2_RSVD9(v) \ ++ (((v) << 13) & BM_PXP_HIST16_PARAM2_RSVD9) ++#define BP_PXP_HIST16_PARAM2_VALUE9 8 ++#define BM_PXP_HIST16_PARAM2_VALUE9 0x00001F00 ++#define BF_PXP_HIST16_PARAM2_VALUE9(v) \ ++ (((v) << 8) & BM_PXP_HIST16_PARAM2_VALUE9) ++#define BP_PXP_HIST16_PARAM2_RSVD8 5 ++#define BM_PXP_HIST16_PARAM2_RSVD8 0x000000E0 ++#define BF_PXP_HIST16_PARAM2_RSVD8(v) \ ++ (((v) << 5) & BM_PXP_HIST16_PARAM2_RSVD8) ++#define BP_PXP_HIST16_PARAM2_VALUE8 0 ++#define BM_PXP_HIST16_PARAM2_VALUE8 0x0000001F ++#define BF_PXP_HIST16_PARAM2_VALUE8(v) \ ++ (((v) << 0) & BM_PXP_HIST16_PARAM2_VALUE8) ++ ++#define HW_PXP_HIST16_PARAM3 (0x00000310) ++ ++#define BP_PXP_HIST16_PARAM3_RSVD15 29 ++#define BM_PXP_HIST16_PARAM3_RSVD15 0xE0000000 ++#define BF_PXP_HIST16_PARAM3_RSVD15(v) \ ++ (((v) << 29) & BM_PXP_HIST16_PARAM3_RSVD15) ++#define BP_PXP_HIST16_PARAM3_VALUE15 24 ++#define BM_PXP_HIST16_PARAM3_VALUE15 0x1F000000 ++#define BF_PXP_HIST16_PARAM3_VALUE15(v) \ ++ (((v) << 24) & BM_PXP_HIST16_PARAM3_VALUE15) ++#define BP_PXP_HIST16_PARAM3_RSVD14 21 ++#define BM_PXP_HIST16_PARAM3_RSVD14 0x00E00000 ++#define BF_PXP_HIST16_PARAM3_RSVD14(v) \ ++ (((v) << 21) & BM_PXP_HIST16_PARAM3_RSVD14) ++#define BP_PXP_HIST16_PARAM3_VALUE14 16 ++#define BM_PXP_HIST16_PARAM3_VALUE14 0x001F0000 ++#define BF_PXP_HIST16_PARAM3_VALUE14(v) \ ++ (((v) << 16) & BM_PXP_HIST16_PARAM3_VALUE14) ++#define BP_PXP_HIST16_PARAM3_RSVD13 13 ++#define BM_PXP_HIST16_PARAM3_RSVD13 0x0000E000 ++#define BF_PXP_HIST16_PARAM3_RSVD13(v) \ ++ (((v) << 13) & BM_PXP_HIST16_PARAM3_RSVD13) ++#define BP_PXP_HIST16_PARAM3_VALUE13 8 ++#define BM_PXP_HIST16_PARAM3_VALUE13 0x00001F00 ++#define BF_PXP_HIST16_PARAM3_VALUE13(v) \ ++ (((v) << 8) & BM_PXP_HIST16_PARAM3_VALUE13) ++#define BP_PXP_HIST16_PARAM3_RSVD12 5 ++#define BM_PXP_HIST16_PARAM3_RSVD12 0x000000E0 ++#define BF_PXP_HIST16_PARAM3_RSVD12(v) \ ++ (((v) << 5) & BM_PXP_HIST16_PARAM3_RSVD12) ++#define BP_PXP_HIST16_PARAM3_VALUE12 0 ++#define BM_PXP_HIST16_PARAM3_VALUE12 0x0000001F ++#define BF_PXP_HIST16_PARAM3_VALUE12(v) \ ++ (((v) << 0) & BM_PXP_HIST16_PARAM3_VALUE12) ++ ++#define HW_PXP_POWER (0x00000320) ++ ++#define BP_PXP_POWER_CTRL 12 ++#define BM_PXP_POWER_CTRL 0xFFFFF000 ++#define BF_PXP_POWER_CTRL(v) \ ++ (((v) << 12) & BM_PXP_POWER_CTRL) ++#define BP_PXP_POWER_ROT_MEM_LP_STATE 9 ++#define BM_PXP_POWER_ROT_MEM_LP_STATE 0x00000E00 ++#define BF_PXP_POWER_ROT_MEM_LP_STATE(v) \ ++ (((v) << 9) & BM_PXP_POWER_ROT_MEM_LP_STATE) ++#define BV_PXP_POWER_ROT_MEM_LP_STATE__NONE 0x0 ++#define BV_PXP_POWER_ROT_MEM_LP_STATE__LS 0x1 ++#define BV_PXP_POWER_ROT_MEM_LP_STATE__DS 0x2 ++#define BV_PXP_POWER_ROT_MEM_LP_STATE__SD 0x4 ++#define BP_PXP_POWER_LUT_LP_STATE_WAY1_BANKN 6 ++#define BM_PXP_POWER_LUT_LP_STATE_WAY1_BANKN 0x000001C0 ++#define BF_PXP_POWER_LUT_LP_STATE_WAY1_BANKN(v) \ ++ (((v) << 6) & BM_PXP_POWER_LUT_LP_STATE_WAY1_BANKN) ++#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__NONE 0x0 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__LS 0x1 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__DS 0x2 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__SD 0x4 ++#define BP_PXP_POWER_LUT_LP_STATE_WAY0_BANKN 3 ++#define BM_PXP_POWER_LUT_LP_STATE_WAY0_BANKN 0x00000038 ++#define BF_PXP_POWER_LUT_LP_STATE_WAY0_BANKN(v) \ ++ (((v) << 3) & BM_PXP_POWER_LUT_LP_STATE_WAY0_BANKN) ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__NONE 0x0 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__LS 0x1 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__DS 0x2 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__SD 0x4 ++#define BP_PXP_POWER_LUT_LP_STATE_WAY0_BANK0 0 ++#define BM_PXP_POWER_LUT_LP_STATE_WAY0_BANK0 0x00000007 ++#define BF_PXP_POWER_LUT_LP_STATE_WAY0_BANK0(v) \ ++ (((v) << 0) & BM_PXP_POWER_LUT_LP_STATE_WAY0_BANK0) ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__NONE 0x0 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__LS 0x1 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__DS 0x2 ++#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__SD 0x4 ++ ++#define HW_PXP_NEXT (0x00000400) ++ ++#define BP_PXP_NEXT_POINTER 2 ++#define BM_PXP_NEXT_POINTER 0xFFFFFFFC ++#define BF_PXP_NEXT_POINTER(v) \ ++ (((v) << 2) & BM_PXP_NEXT_POINTER) ++#define BM_PXP_NEXT_RSVD 0x00000002 ++#define BM_PXP_NEXT_ENABLED 0x00000001 ++ ++#define HW_PXP_DEBUGCTRL (0x00000410) ++ ++#define BP_PXP_DEBUGCTRL_RSVD 12 ++#define BM_PXP_DEBUGCTRL_RSVD 0xFFFFF000 ++#define BF_PXP_DEBUGCTRL_RSVD(v) \ ++ (((v) << 12) & BM_PXP_DEBUGCTRL_RSVD) ++#define BP_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT 8 ++#define BM_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT 0x00000F00 ++#define BF_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT(v) \ ++ (((v) << 8) & BM_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT) ++#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__NONE 0x0 ++#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__MISS_CNT 0x1 ++#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__HIT_CNT 0x2 ++#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__LAT_CNT 0x4 ++#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__MAX_LAT 0x8 ++#define BP_PXP_DEBUGCTRL_SELECT 0 ++#define BM_PXP_DEBUGCTRL_SELECT 0x000000FF ++#define BF_PXP_DEBUGCTRL_SELECT(v) \ ++ (((v) << 0) & BM_PXP_DEBUGCTRL_SELECT) ++#define BV_PXP_DEBUGCTRL_SELECT__NONE 0x0 ++#define BV_PXP_DEBUGCTRL_SELECT__CTRL 0x1 ++#define BV_PXP_DEBUGCTRL_SELECT__PSBUF 0x2 ++#define BV_PXP_DEBUGCTRL_SELECT__PSBAX 0x3 ++#define BV_PXP_DEBUGCTRL_SELECT__PSBAY 0x4 ++#define BV_PXP_DEBUGCTRL_SELECT__ASBUF 0x5 ++#define BV_PXP_DEBUGCTRL_SELECT__ROTATION 0x6 ++#define BV_PXP_DEBUGCTRL_SELECT__OUTBUF0 0x7 ++#define BV_PXP_DEBUGCTRL_SELECT__OUTBUF1 0x8 ++#define BV_PXP_DEBUGCTRL_SELECT__OUTBUF2 0x9 ++#define BV_PXP_DEBUGCTRL_SELECT__LUT_STAT 0x10 ++#define BV_PXP_DEBUGCTRL_SELECT__LUT_MISS 0x11 ++#define BV_PXP_DEBUGCTRL_SELECT__LUT_HIT 0x12 ++#define BV_PXP_DEBUGCTRL_SELECT__LUT_LAT 0x13 ++#define BV_PXP_DEBUGCTRL_SELECT__LUT_MAX_LAT 0x14 ++ ++#define HW_PXP_DEBUG (0x00000420) ++ ++#define BP_PXP_DEBUG_DATA 0 ++#define BM_PXP_DEBUG_DATA 0xFFFFFFFF ++#define BF_PXP_DEBUG_DATA(v) (v) ++ ++#define HW_PXP_VERSION (0x00000430) ++ ++#define BP_PXP_VERSION_MAJOR 24 ++#define BM_PXP_VERSION_MAJOR 0xFF000000 ++#define BF_PXP_VERSION_MAJOR(v) \ ++ (((v) << 24) & BM_PXP_VERSION_MAJOR) ++#define BP_PXP_VERSION_MINOR 16 ++#define BM_PXP_VERSION_MINOR 0x00FF0000 ++#define BF_PXP_VERSION_MINOR(v) \ ++ (((v) << 16) & BM_PXP_VERSION_MINOR) ++#define BP_PXP_VERSION_STEP 0 ++#define BM_PXP_VERSION_STEP 0x0000FFFF ++#define BF_PXP_VERSION_STEP(v) \ ++ (((v) << 0) & BM_PXP_VERSION_STEP) ++#endif /* __ARCH_ARM___PXP_H */ +diff -Nur linux-3.14.36/drivers/gpio/gpio-pca953x.c linux-openelec/drivers/gpio/gpio-pca953x.c +--- linux-3.14.36/drivers/gpio/gpio-pca953x.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/gpio/gpio-pca953x.c 2015-05-06 12:05:42.000000000 -0500 +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #ifdef CONFIG_OF_GPIO + #include +@@ -741,6 +742,10 @@ + + mutex_init(&chip->i2c_lock); + ++ ret = device_reset(&client->dev); ++ if (ret == -ENODEV) ++ return -EPROBE_DEFER; ++ + /* initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ +diff -Nur linux-3.14.36/drivers/gpu/drm/drm_crtc_helper.c linux-openelec/drivers/gpu/drm/drm_crtc_helper.c +--- linux-3.14.36/drivers/gpu/drm/drm_crtc_helper.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/gpu/drm/drm_crtc_helper.c 2015-05-06 12:05:42.000000000 -0500 +@@ -564,7 +564,7 @@ + * Caller must hold mode config lock. + * + * Setup a new configuration, provided by the upper layers (either an ioctl call +- * from userspace or internally e.g. from the fbdev suppport code) in @set, and ++ * from userspace or internally e.g. from the fbdev support code) in @set, and + * enable it. This is the main helper functions for drivers that implement + * kernel mode setting with the crtc helper functions and the assorted + * ->prepare(), ->modeset() and ->commit() helper callbacks. +diff -Nur linux-3.14.36/drivers/gpu/drm/drm_prime.c linux-openelec/drivers/gpu/drm/drm_prime.c +--- linux-3.14.36/drivers/gpu/drm/drm_prime.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/gpu/drm/drm_prime.c 2015-05-06 12:05:42.000000000 -0500 +@@ -471,7 +471,7 @@ + get_dma_buf(dma_buf); + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); +- if (IS_ERR_OR_NULL(sgt)) { ++ if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto fail_detach; + } +diff -Nur linux-3.14.36/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c linux-openelec/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +--- linux-3.14.36/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c 2015-05-06 12:05:42.000000000 -0500 +@@ -224,7 +224,7 @@ + get_dma_buf(dma_buf); + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); +- if (IS_ERR_OR_NULL(sgt)) { ++ if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto err_buf_detach; + } +diff -Nur linux-3.14.36/drivers/gpu/drm/Kconfig linux-openelec/drivers/gpu/drm/Kconfig +--- linux-3.14.36/drivers/gpu/drm/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/gpu/drm/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -166,6 +166,13 @@ + Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister + chipset. If M is selected the module will be called savage. + ++config DRM_VIVANTE ++ tristate "Vivante GCCore" ++ depends on DRM ++ help ++ Choose this option if you have a Vivante graphics card. ++ If M is selected, the module will be called vivante. ++ + source "drivers/gpu/drm/exynos/Kconfig" + + source "drivers/gpu/drm/vmwgfx/Kconfig" +diff -Nur linux-3.14.36/drivers/gpu/drm/Makefile linux-openelec/drivers/gpu/drm/Makefile +--- linux-3.14.36/drivers/gpu/drm/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/gpu/drm/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -1,3 +1,24 @@ ++############################################################################## ++# ++# Copyright (C) 2005 - 2013 by Vivante Corp. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the license, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not write to the Free Software ++# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++# ++############################################################################## ++ ++ + # + # Makefile for the drm device driver. This driver provides support for the + # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. +@@ -35,6 +56,7 @@ + obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o + obj-$(CONFIG_DRM_USB) += drm_usb.o + obj-$(CONFIG_DRM_TTM) += ttm/ ++obj-$(CONFIG_DRM_VIVANTE) += vivante/ + obj-$(CONFIG_DRM_TDFX) += tdfx/ + obj-$(CONFIG_DRM_R128) += r128/ + obj-$(CONFIG_DRM_RADEON)+= radeon/ +diff -Nur linux-3.14.36/drivers/gpu/drm/vivante/Makefile linux-openelec/drivers/gpu/drm/vivante/Makefile +--- linux-3.14.36/drivers/gpu/drm/vivante/Makefile 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/gpu/drm/vivante/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,29 @@ ++############################################################################## ++# ++# Copyright (C) 2005 - 2013 by Vivante Corp. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the license, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not write to the Free Software ++# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++# ++############################################################################## ++ ++ ++# ++# Makefile for the drm device driver. This driver provides support for the ++# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. ++ ++ccflags-y := -Iinclude/drm ++vivante-y := vivante_drv.o ++ ++obj-$(CONFIG_DRM_VIVANTE) += vivante.o +diff -Nur linux-3.14.36/drivers/gpu/drm/vivante/vivante_drv.c linux-openelec/drivers/gpu/drm/vivante/vivante_drv.c +--- linux-3.14.36/drivers/gpu/drm/vivante/vivante_drv.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/gpu/drm/vivante/vivante_drv.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,108 @@ ++/**************************************************************************** ++* ++* Copyright (C) 2005 - 2013 by Vivante Corp. ++* ++* This program is free software; you can redistribute it and/or modify ++* it under the terms of the GNU General Public License as published by ++* the Free Software Foundation; either version 2 of the license, or ++* (at your option) any later version. ++* ++* This program is distributed in the hope that it will be useful, ++* but WITHOUT ANY WARRANTY; without even the implied warranty of ++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++* GNU General Public License for more details. ++* ++* You should have received a copy of the GNU General Public License ++* along with this program; if not write to the Free Software ++* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++* ++*****************************************************************************/ ++ ++ ++/* vivante_drv.c -- vivante driver -*- linux-c -*- ++ * ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: ++ * Rickard E. (Rik) Faith ++ * Daryll Strauss ++ * Gareth Hughes ++ */ ++ ++#include ++#include ++ ++#include "drmP.h" ++#include "vivante_drv.h" ++ ++#include "drm_pciids.h" ++ ++static char platformdevicename[] = "Vivante GCCore"; ++static struct platform_device *pplatformdev; ++ ++static const struct file_operations viv_driver_fops = { ++ .owner = THIS_MODULE, ++ .open = drm_open, ++ .release = drm_release, ++ .unlocked_ioctl = drm_ioctl, ++ .mmap = drm_mmap, ++ .poll = drm_poll, ++ .llseek = noop_llseek, ++}; ++ ++static struct drm_driver driver = { ++ .fops = &viv_driver_fops, ++ .name = DRIVER_NAME, ++ .desc = DRIVER_DESC, ++ .date = DRIVER_DATE, ++ .major = DRIVER_MAJOR, ++ .minor = DRIVER_MINOR, ++ .patchlevel = DRIVER_PATCHLEVEL, ++}; ++ ++static int __init vivante_init(void) ++{ ++ int retcode; ++ ++ pplatformdev = platform_device_register_simple(platformdevicename, ++ -1, NULL, 0); ++ if (pplatformdev == NULL) ++ printk(KERN_ERR"Platform device is null\n"); ++ ++ retcode = drm_platform_init(&driver, pplatformdev); ++ ++ return retcode; ++} ++ ++static void __exit vivante_exit(void) ++{ ++ if (pplatformdev) { ++ platform_device_unregister(pplatformdev); ++ pplatformdev = NULL; ++ } ++} ++ ++module_init(vivante_init); ++module_exit(vivante_exit); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL and additional rights"); +diff -Nur linux-3.14.36/drivers/gpu/drm/vivante/vivante_drv.h linux-openelec/drivers/gpu/drm/vivante/vivante_drv.h +--- linux-3.14.36/drivers/gpu/drm/vivante/vivante_drv.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/gpu/drm/vivante/vivante_drv.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,66 @@ ++/**************************************************************************** ++* ++* Copyright (C) 2005 - 2013 by Vivante Corp. ++* ++* This program is free software; you can redistribute it and/or modify ++* it under the terms of the GNU General Public License as published by ++* the Free Software Foundation; either version 2 of the license, or ++* (at your option) any later version. ++* ++* This program is distributed in the hope that it will be useful, ++* but WITHOUT ANY WARRANTY; without even the implied warranty of ++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++* GNU General Public License for more details. ++* ++* You should have received a copy of the GNU General Public License ++* along with this program; if not write to the Free Software ++* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++* ++*****************************************************************************/ ++ ++ ++/* vivante_drv.h -- Vivante DRM template customization -*- linux-c -*- ++ * Created: Wed Feb 14 12:32:32 2012 by John Zhao ++ */ ++/* ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: ++ * Gareth Hughes ++ */ ++ ++#ifndef __VIVANTE_DRV_H__ ++#define __VIVANTE_DRV_H__ ++ ++/* General customization: ++ */ ++ ++#define DRIVER_AUTHOR "Vivante Inc." ++ ++#define DRIVER_NAME "vivante" ++#define DRIVER_DESC "Vivante GCCore" ++#define DRIVER_DATE "20120216" ++ ++#define DRIVER_MAJOR 1 ++#define DRIVER_MINOR 0 ++#define DRIVER_PATCHLEVEL 0 ++ ++#endif +diff -Nur linux-3.14.36/drivers/hid/hid-core.c linux-openelec/drivers/hid/hid-core.c +--- linux-3.14.36/drivers/hid/hid-core.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hid/hid-core.c 2015-07-24 18:03:30.048842002 -0500 +@@ -1816,7 +1816,11 @@ + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_OUYA, USB_DEVICE_ID_OUYA_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, + #if IS_ENABLED(CONFIG_HID_ROCCAT) + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, +@@ -1840,6 +1844,7 @@ + { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, +@@ -1863,6 +1868,7 @@ + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, +diff -Nur linux-3.14.36/drivers/hid/hid-core.c.orig linux-openelec/drivers/hid/hid-core.c.orig +--- linux-3.14.36/drivers/hid/hid-core.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hid/hid-core.c.orig 2015-07-24 18:03:29.976842002 -0500 +@@ -0,0 +1,2642 @@ ++/* ++ * HID support for Linux ++ * ++ * Copyright (c) 1999 Andreas Gal ++ * Copyright (c) 2000-2005 Vojtech Pavlik ++ * Copyright (c) 2005 Michael Haboustak for Concept2, Inc ++ * Copyright (c) 2006-2012 Jiri Kosina ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "hid-ids.h" ++ ++/* ++ * Version Information ++ */ ++ ++#define DRIVER_DESC "HID core driver" ++#define DRIVER_LICENSE "GPL" ++ ++int hid_debug = 0; ++module_param_named(debug, hid_debug, int, 0600); ++MODULE_PARM_DESC(debug, "toggle HID debugging messages"); ++EXPORT_SYMBOL_GPL(hid_debug); ++ ++static int hid_ignore_special_drivers = 0; ++module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600); ++MODULE_PARM_DESC(debug, "Ignore any special drivers and handle all devices by generic driver"); ++ ++/* ++ * Register a new report for a device. ++ */ ++ ++struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id) ++{ ++ struct hid_report_enum *report_enum = device->report_enum + type; ++ struct hid_report *report; ++ ++ if (id >= HID_MAX_IDS) ++ return NULL; ++ if (report_enum->report_id_hash[id]) ++ return report_enum->report_id_hash[id]; ++ ++ report = kzalloc(sizeof(struct hid_report), GFP_KERNEL); ++ if (!report) ++ return NULL; ++ ++ if (id != 0) ++ report_enum->numbered = 1; ++ ++ report->id = id; ++ report->type = type; ++ report->size = 0; ++ report->device = device; ++ report_enum->report_id_hash[id] = report; ++ ++ list_add_tail(&report->list, &report_enum->report_list); ++ ++ return report; ++} ++EXPORT_SYMBOL_GPL(hid_register_report); ++ ++/* ++ * Register a new field for this report. ++ */ ++ ++static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) ++{ ++ struct hid_field *field; ++ ++ if (report->maxfield == HID_MAX_FIELDS) { ++ hid_err(report->device, "too many fields in report\n"); ++ return NULL; ++ } ++ ++ field = kzalloc((sizeof(struct hid_field) + ++ usages * sizeof(struct hid_usage) + ++ values * sizeof(unsigned)), GFP_KERNEL); ++ if (!field) ++ return NULL; ++ ++ field->index = report->maxfield++; ++ report->field[field->index] = field; ++ field->usage = (struct hid_usage *)(field + 1); ++ field->value = (s32 *)(field->usage + usages); ++ field->report = report; ++ ++ return field; ++} ++ ++/* ++ * Open a collection. The type/usage is pushed on the stack. ++ */ ++ ++static int open_collection(struct hid_parser *parser, unsigned type) ++{ ++ struct hid_collection *collection; ++ unsigned usage; ++ ++ usage = parser->local.usage[0]; ++ ++ if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { ++ hid_err(parser->device, "collection stack overflow\n"); ++ return -EINVAL; ++ } ++ ++ if (parser->device->maxcollection == parser->device->collection_size) { ++ collection = kmalloc(sizeof(struct hid_collection) * ++ parser->device->collection_size * 2, GFP_KERNEL); ++ if (collection == NULL) { ++ hid_err(parser->device, "failed to reallocate collection array\n"); ++ return -ENOMEM; ++ } ++ memcpy(collection, parser->device->collection, ++ sizeof(struct hid_collection) * ++ parser->device->collection_size); ++ memset(collection + parser->device->collection_size, 0, ++ sizeof(struct hid_collection) * ++ parser->device->collection_size); ++ kfree(parser->device->collection); ++ parser->device->collection = collection; ++ parser->device->collection_size *= 2; ++ } ++ ++ parser->collection_stack[parser->collection_stack_ptr++] = ++ parser->device->maxcollection; ++ ++ collection = parser->device->collection + ++ parser->device->maxcollection++; ++ collection->type = type; ++ collection->usage = usage; ++ collection->level = parser->collection_stack_ptr - 1; ++ ++ if (type == HID_COLLECTION_APPLICATION) ++ parser->device->maxapplication++; ++ ++ return 0; ++} ++ ++/* ++ * Close a collection. ++ */ ++ ++static int close_collection(struct hid_parser *parser) ++{ ++ if (!parser->collection_stack_ptr) { ++ hid_err(parser->device, "collection stack underflow\n"); ++ return -EINVAL; ++ } ++ parser->collection_stack_ptr--; ++ return 0; ++} ++ ++/* ++ * Climb up the stack, search for the specified collection type ++ * and return the usage. ++ */ ++ ++static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) ++{ ++ struct hid_collection *collection = parser->device->collection; ++ int n; ++ ++ for (n = parser->collection_stack_ptr - 1; n >= 0; n--) { ++ unsigned index = parser->collection_stack[n]; ++ if (collection[index].type == type) ++ return collection[index].usage; ++ } ++ return 0; /* we know nothing about this usage type */ ++} ++ ++/* ++ * Add a usage to the temporary parser table. ++ */ ++ ++static int hid_add_usage(struct hid_parser *parser, unsigned usage) ++{ ++ if (parser->local.usage_index >= HID_MAX_USAGES) { ++ hid_err(parser->device, "usage index exceeded\n"); ++ return -1; ++ } ++ parser->local.usage[parser->local.usage_index] = usage; ++ parser->local.collection_index[parser->local.usage_index] = ++ parser->collection_stack_ptr ? ++ parser->collection_stack[parser->collection_stack_ptr - 1] : 0; ++ parser->local.usage_index++; ++ return 0; ++} ++ ++/* ++ * Register a new field for this report. ++ */ ++ ++static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags) ++{ ++ struct hid_report *report; ++ struct hid_field *field; ++ unsigned usages; ++ unsigned offset; ++ unsigned i; ++ ++ report = hid_register_report(parser->device, report_type, parser->global.report_id); ++ if (!report) { ++ hid_err(parser->device, "hid_register_report failed\n"); ++ return -1; ++ } ++ ++ /* Handle both signed and unsigned cases properly */ ++ if ((parser->global.logical_minimum < 0 && ++ parser->global.logical_maximum < ++ parser->global.logical_minimum) || ++ (parser->global.logical_minimum >= 0 && ++ (__u32)parser->global.logical_maximum < ++ (__u32)parser->global.logical_minimum)) { ++ dbg_hid("logical range invalid 0x%x 0x%x\n", ++ parser->global.logical_minimum, ++ parser->global.logical_maximum); ++ return -1; ++ } ++ ++ offset = report->size; ++ report->size += parser->global.report_size * parser->global.report_count; ++ ++ if (!parser->local.usage_index) /* Ignore padding fields */ ++ return 0; ++ ++ usages = max_t(unsigned, parser->local.usage_index, ++ parser->global.report_count); ++ ++ field = hid_register_field(report, usages, parser->global.report_count); ++ if (!field) ++ return 0; ++ ++ field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); ++ field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); ++ field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); ++ ++ for (i = 0; i < usages; i++) { ++ unsigned j = i; ++ /* Duplicate the last usage we parsed if we have excess values */ ++ if (i >= parser->local.usage_index) ++ j = parser->local.usage_index - 1; ++ field->usage[i].hid = parser->local.usage[j]; ++ field->usage[i].collection_index = ++ parser->local.collection_index[j]; ++ field->usage[i].usage_index = i; ++ } ++ ++ field->maxusage = usages; ++ field->flags = flags; ++ field->report_offset = offset; ++ field->report_type = report_type; ++ field->report_size = parser->global.report_size; ++ field->report_count = parser->global.report_count; ++ field->logical_minimum = parser->global.logical_minimum; ++ field->logical_maximum = parser->global.logical_maximum; ++ field->physical_minimum = parser->global.physical_minimum; ++ field->physical_maximum = parser->global.physical_maximum; ++ field->unit_exponent = parser->global.unit_exponent; ++ field->unit = parser->global.unit; ++ ++ return 0; ++} ++ ++/* ++ * Read data value from item. ++ */ ++ ++static u32 item_udata(struct hid_item *item) ++{ ++ switch (item->size) { ++ case 1: return item->data.u8; ++ case 2: return item->data.u16; ++ case 4: return item->data.u32; ++ } ++ return 0; ++} ++ ++static s32 item_sdata(struct hid_item *item) ++{ ++ switch (item->size) { ++ case 1: return item->data.s8; ++ case 2: return item->data.s16; ++ case 4: return item->data.s32; ++ } ++ return 0; ++} ++ ++/* ++ * Process a global item. ++ */ ++ ++static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) ++{ ++ __s32 raw_value; ++ switch (item->tag) { ++ case HID_GLOBAL_ITEM_TAG_PUSH: ++ ++ if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { ++ hid_err(parser->device, "global environment stack overflow\n"); ++ return -1; ++ } ++ ++ memcpy(parser->global_stack + parser->global_stack_ptr++, ++ &parser->global, sizeof(struct hid_global)); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_POP: ++ ++ if (!parser->global_stack_ptr) { ++ hid_err(parser->device, "global environment stack underflow\n"); ++ return -1; ++ } ++ ++ memcpy(&parser->global, parser->global_stack + ++ --parser->global_stack_ptr, sizeof(struct hid_global)); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: ++ parser->global.usage_page = item_udata(item); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: ++ parser->global.logical_minimum = item_sdata(item); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: ++ if (parser->global.logical_minimum < 0) ++ parser->global.logical_maximum = item_sdata(item); ++ else ++ parser->global.logical_maximum = item_udata(item); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: ++ parser->global.physical_minimum = item_sdata(item); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: ++ if (parser->global.physical_minimum < 0) ++ parser->global.physical_maximum = item_sdata(item); ++ else ++ parser->global.physical_maximum = item_udata(item); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: ++ /* Many devices provide unit exponent as a two's complement ++ * nibble due to the common misunderstanding of HID ++ * specification 1.11, 6.2.2.7 Global Items. Attempt to handle ++ * both this and the standard encoding. */ ++ raw_value = item_sdata(item); ++ if (!(raw_value & 0xfffffff0)) ++ parser->global.unit_exponent = hid_snto32(raw_value, 4); ++ else ++ parser->global.unit_exponent = raw_value; ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_UNIT: ++ parser->global.unit = item_udata(item); ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: ++ parser->global.report_size = item_udata(item); ++ if (parser->global.report_size > 128) { ++ hid_err(parser->device, "invalid report_size %d\n", ++ parser->global.report_size); ++ return -1; ++ } ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: ++ parser->global.report_count = item_udata(item); ++ if (parser->global.report_count > HID_MAX_USAGES) { ++ hid_err(parser->device, "invalid report_count %d\n", ++ parser->global.report_count); ++ return -1; ++ } ++ return 0; ++ ++ case HID_GLOBAL_ITEM_TAG_REPORT_ID: ++ parser->global.report_id = item_udata(item); ++ if (parser->global.report_id == 0 || ++ parser->global.report_id >= HID_MAX_IDS) { ++ hid_err(parser->device, "report_id %u is invalid\n", ++ parser->global.report_id); ++ return -1; ++ } ++ return 0; ++ ++ default: ++ hid_err(parser->device, "unknown global tag 0x%x\n", item->tag); ++ return -1; ++ } ++} ++ ++/* ++ * Process a local item. ++ */ ++ ++static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) ++{ ++ __u32 data; ++ unsigned n; ++ ++ data = item_udata(item); ++ ++ switch (item->tag) { ++ case HID_LOCAL_ITEM_TAG_DELIMITER: ++ ++ if (data) { ++ /* ++ * We treat items before the first delimiter ++ * as global to all usage sets (branch 0). ++ * In the moment we process only these global ++ * items and the first delimiter set. ++ */ ++ if (parser->local.delimiter_depth != 0) { ++ hid_err(parser->device, "nested delimiters\n"); ++ return -1; ++ } ++ parser->local.delimiter_depth++; ++ parser->local.delimiter_branch++; ++ } else { ++ if (parser->local.delimiter_depth < 1) { ++ hid_err(parser->device, "bogus close delimiter\n"); ++ return -1; ++ } ++ parser->local.delimiter_depth--; ++ } ++ return 0; ++ ++ case HID_LOCAL_ITEM_TAG_USAGE: ++ ++ if (parser->local.delimiter_branch > 1) { ++ dbg_hid("alternative usage ignored\n"); ++ return 0; ++ } ++ ++ if (item->size <= 2) ++ data = (parser->global.usage_page << 16) + data; ++ ++ return hid_add_usage(parser, data); ++ ++ case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: ++ ++ if (parser->local.delimiter_branch > 1) { ++ dbg_hid("alternative usage ignored\n"); ++ return 0; ++ } ++ ++ if (item->size <= 2) ++ data = (parser->global.usage_page << 16) + data; ++ ++ parser->local.usage_minimum = data; ++ return 0; ++ ++ case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: ++ ++ if (parser->local.delimiter_branch > 1) { ++ dbg_hid("alternative usage ignored\n"); ++ return 0; ++ } ++ ++ if (item->size <= 2) ++ data = (parser->global.usage_page << 16) + data; ++ ++ for (n = parser->local.usage_minimum; n <= data; n++) ++ if (hid_add_usage(parser, n)) { ++ dbg_hid("hid_add_usage failed\n"); ++ return -1; ++ } ++ return 0; ++ ++ default: ++ ++ dbg_hid("unknown local item tag 0x%x\n", item->tag); ++ return 0; ++ } ++ return 0; ++} ++ ++/* ++ * Process a main item. ++ */ ++ ++static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) ++{ ++ __u32 data; ++ int ret; ++ ++ data = item_udata(item); ++ ++ switch (item->tag) { ++ case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: ++ ret = open_collection(parser, data & 0xff); ++ break; ++ case HID_MAIN_ITEM_TAG_END_COLLECTION: ++ ret = close_collection(parser); ++ break; ++ case HID_MAIN_ITEM_TAG_INPUT: ++ ret = hid_add_field(parser, HID_INPUT_REPORT, data); ++ break; ++ case HID_MAIN_ITEM_TAG_OUTPUT: ++ ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); ++ break; ++ case HID_MAIN_ITEM_TAG_FEATURE: ++ ret = hid_add_field(parser, HID_FEATURE_REPORT, data); ++ break; ++ default: ++ hid_err(parser->device, "unknown main item tag 0x%x\n", item->tag); ++ ret = 0; ++ } ++ ++ memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */ ++ ++ return ret; ++} ++ ++/* ++ * Process a reserved item. ++ */ ++ ++static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item) ++{ ++ dbg_hid("reserved item type, tag 0x%x\n", item->tag); ++ return 0; ++} ++ ++/* ++ * Free a report and all registered fields. The field->usage and ++ * field->value table's are allocated behind the field, so we need ++ * only to free(field) itself. ++ */ ++ ++static void hid_free_report(struct hid_report *report) ++{ ++ unsigned n; ++ ++ for (n = 0; n < report->maxfield; n++) ++ kfree(report->field[n]); ++ kfree(report); ++} ++ ++/* ++ * Close report. This function returns the device ++ * state to the point prior to hid_open_report(). ++ */ ++static void hid_close_report(struct hid_device *device) ++{ ++ unsigned i, j; ++ ++ for (i = 0; i < HID_REPORT_TYPES; i++) { ++ struct hid_report_enum *report_enum = device->report_enum + i; ++ ++ for (j = 0; j < HID_MAX_IDS; j++) { ++ struct hid_report *report = report_enum->report_id_hash[j]; ++ if (report) ++ hid_free_report(report); ++ } ++ memset(report_enum, 0, sizeof(*report_enum)); ++ INIT_LIST_HEAD(&report_enum->report_list); ++ } ++ ++ kfree(device->rdesc); ++ device->rdesc = NULL; ++ device->rsize = 0; ++ ++ kfree(device->collection); ++ device->collection = NULL; ++ device->collection_size = 0; ++ device->maxcollection = 0; ++ device->maxapplication = 0; ++ ++ device->status &= ~HID_STAT_PARSED; ++} ++ ++/* ++ * Free a device structure, all reports, and all fields. ++ */ ++ ++static void hid_device_release(struct device *dev) ++{ ++ struct hid_device *hid = container_of(dev, struct hid_device, dev); ++ ++ hid_close_report(hid); ++ kfree(hid->dev_rdesc); ++ kfree(hid); ++} ++ ++/* ++ * Fetch a report description item from the data stream. We support long ++ * items, though they are not used yet. ++ */ ++ ++static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) ++{ ++ u8 b; ++ ++ if ((end - start) <= 0) ++ return NULL; ++ ++ b = *start++; ++ ++ item->type = (b >> 2) & 3; ++ item->tag = (b >> 4) & 15; ++ ++ if (item->tag == HID_ITEM_TAG_LONG) { ++ ++ item->format = HID_ITEM_FORMAT_LONG; ++ ++ if ((end - start) < 2) ++ return NULL; ++ ++ item->size = *start++; ++ item->tag = *start++; ++ ++ if ((end - start) < item->size) ++ return NULL; ++ ++ item->data.longdata = start; ++ start += item->size; ++ return start; ++ } ++ ++ item->format = HID_ITEM_FORMAT_SHORT; ++ item->size = b & 3; ++ ++ switch (item->size) { ++ case 0: ++ return start; ++ ++ case 1: ++ if ((end - start) < 1) ++ return NULL; ++ item->data.u8 = *start++; ++ return start; ++ ++ case 2: ++ if ((end - start) < 2) ++ return NULL; ++ item->data.u16 = get_unaligned_le16(start); ++ start = (__u8 *)((__le16 *)start + 1); ++ return start; ++ ++ case 3: ++ item->size++; ++ if ((end - start) < 4) ++ return NULL; ++ item->data.u32 = get_unaligned_le32(start); ++ start = (__u8 *)((__le32 *)start + 1); ++ return start; ++ } ++ ++ return NULL; ++} ++ ++static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) ++{ ++ struct hid_device *hid = parser->device; ++ ++ if (usage == HID_DG_CONTACTID) ++ hid->group = HID_GROUP_MULTITOUCH; ++} ++ ++static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) ++{ ++ if (usage == 0xff0000c5 && parser->global.report_count == 256 && ++ parser->global.report_size == 8) ++ parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; ++} ++ ++static void hid_scan_collection(struct hid_parser *parser, unsigned type) ++{ ++ struct hid_device *hid = parser->device; ++ ++ if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && ++ type == HID_COLLECTION_PHYSICAL) ++ hid->group = HID_GROUP_SENSOR_HUB; ++} ++ ++static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) ++{ ++ __u32 data; ++ int i; ++ ++ data = item_udata(item); ++ ++ switch (item->tag) { ++ case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: ++ hid_scan_collection(parser, data & 0xff); ++ break; ++ case HID_MAIN_ITEM_TAG_END_COLLECTION: ++ break; ++ case HID_MAIN_ITEM_TAG_INPUT: ++ /* ignore constant inputs, they will be ignored by hid-input */ ++ if (data & HID_MAIN_ITEM_CONSTANT) ++ break; ++ for (i = 0; i < parser->local.usage_index; i++) ++ hid_scan_input_usage(parser, parser->local.usage[i]); ++ break; ++ case HID_MAIN_ITEM_TAG_OUTPUT: ++ break; ++ case HID_MAIN_ITEM_TAG_FEATURE: ++ for (i = 0; i < parser->local.usage_index; i++) ++ hid_scan_feature_usage(parser, parser->local.usage[i]); ++ break; ++ } ++ ++ /* Reset the local parser environment */ ++ memset(&parser->local, 0, sizeof(parser->local)); ++ ++ return 0; ++} ++ ++/* ++ * Scan a report descriptor before the device is added to the bus. ++ * Sets device groups and other properties that determine what driver ++ * to load. ++ */ ++static int hid_scan_report(struct hid_device *hid) ++{ ++ struct hid_parser *parser; ++ struct hid_item item; ++ __u8 *start = hid->dev_rdesc; ++ __u8 *end = start + hid->dev_rsize; ++ static int (*dispatch_type[])(struct hid_parser *parser, ++ struct hid_item *item) = { ++ hid_scan_main, ++ hid_parser_global, ++ hid_parser_local, ++ hid_parser_reserved ++ }; ++ ++ parser = vzalloc(sizeof(struct hid_parser)); ++ if (!parser) ++ return -ENOMEM; ++ ++ parser->device = hid; ++ hid->group = HID_GROUP_GENERIC; ++ ++ /* ++ * The parsing is simpler than the one in hid_open_report() as we should ++ * be robust against hid errors. Those errors will be raised by ++ * hid_open_report() anyway. ++ */ ++ while ((start = fetch_item(start, end, &item)) != NULL) ++ dispatch_type[item.type](parser, &item); ++ ++ /* ++ * Handle special flags set during scanning. ++ */ ++ if ((parser->scan_flags & HID_SCAN_FLAG_MT_WIN_8) && ++ (hid->group == HID_GROUP_MULTITOUCH)) ++ hid->group = HID_GROUP_MULTITOUCH_WIN_8; ++ ++ vfree(parser); ++ return 0; ++} ++ ++/** ++ * hid_parse_report - parse device report ++ * ++ * @device: hid device ++ * @start: report start ++ * @size: report size ++ * ++ * Allocate the device report as read by the bus driver. This function should ++ * only be called from parse() in ll drivers. ++ */ ++int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) ++{ ++ hid->dev_rdesc = kmemdup(start, size, GFP_KERNEL); ++ if (!hid->dev_rdesc) ++ return -ENOMEM; ++ hid->dev_rsize = size; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(hid_parse_report); ++ ++static const char * const hid_report_names[] = { ++ "HID_INPUT_REPORT", ++ "HID_OUTPUT_REPORT", ++ "HID_FEATURE_REPORT", ++}; ++/** ++ * hid_validate_values - validate existing device report's value indexes ++ * ++ * @device: hid device ++ * @type: which report type to examine ++ * @id: which report ID to examine (0 for first) ++ * @field_index: which report field to examine ++ * @report_counts: expected number of values ++ * ++ * Validate the number of values in a given field of a given report, after ++ * parsing. ++ */ ++struct hid_report *hid_validate_values(struct hid_device *hid, ++ unsigned int type, unsigned int id, ++ unsigned int field_index, ++ unsigned int report_counts) ++{ ++ struct hid_report *report; ++ ++ if (type > HID_FEATURE_REPORT) { ++ hid_err(hid, "invalid HID report type %u\n", type); ++ return NULL; ++ } ++ ++ if (id >= HID_MAX_IDS) { ++ hid_err(hid, "invalid HID report id %u\n", id); ++ return NULL; ++ } ++ ++ /* ++ * Explicitly not using hid_get_report() here since it depends on ++ * ->numbered being checked, which may not always be the case when ++ * drivers go to access report values. ++ */ ++ if (id == 0) { ++ /* ++ * Validating on id 0 means we should examine the first ++ * report in the list. ++ */ ++ report = list_entry( ++ hid->report_enum[type].report_list.next, ++ struct hid_report, list); ++ } else { ++ report = hid->report_enum[type].report_id_hash[id]; ++ } ++ if (!report) { ++ hid_err(hid, "missing %s %u\n", hid_report_names[type], id); ++ return NULL; ++ } ++ if (report->maxfield <= field_index) { ++ hid_err(hid, "not enough fields in %s %u\n", ++ hid_report_names[type], id); ++ return NULL; ++ } ++ if (report->field[field_index]->report_count < report_counts) { ++ hid_err(hid, "not enough values in %s %u field %u\n", ++ hid_report_names[type], id, field_index); ++ return NULL; ++ } ++ return report; ++} ++EXPORT_SYMBOL_GPL(hid_validate_values); ++ ++/** ++ * hid_open_report - open a driver-specific device report ++ * ++ * @device: hid device ++ * ++ * Parse a report description into a hid_device structure. Reports are ++ * enumerated, fields are attached to these reports. ++ * 0 returned on success, otherwise nonzero error value. ++ * ++ * This function (or the equivalent hid_parse() macro) should only be ++ * called from probe() in drivers, before starting the device. ++ */ ++int hid_open_report(struct hid_device *device) ++{ ++ struct hid_parser *parser; ++ struct hid_item item; ++ unsigned int size; ++ __u8 *start; ++ __u8 *buf; ++ __u8 *end; ++ int ret; ++ static int (*dispatch_type[])(struct hid_parser *parser, ++ struct hid_item *item) = { ++ hid_parser_main, ++ hid_parser_global, ++ hid_parser_local, ++ hid_parser_reserved ++ }; ++ ++ if (WARN_ON(device->status & HID_STAT_PARSED)) ++ return -EBUSY; ++ ++ start = device->dev_rdesc; ++ if (WARN_ON(!start)) ++ return -ENODEV; ++ size = device->dev_rsize; ++ ++ buf = kmemdup(start, size, GFP_KERNEL); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ if (device->driver->report_fixup) ++ start = device->driver->report_fixup(device, buf, &size); ++ else ++ start = buf; ++ ++ start = kmemdup(start, size, GFP_KERNEL); ++ kfree(buf); ++ if (start == NULL) ++ return -ENOMEM; ++ ++ device->rdesc = start; ++ device->rsize = size; ++ ++ parser = vzalloc(sizeof(struct hid_parser)); ++ if (!parser) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ parser->device = device; ++ ++ end = start + size; ++ ++ device->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS, ++ sizeof(struct hid_collection), GFP_KERNEL); ++ if (!device->collection) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; ++ ++ ret = -EINVAL; ++ while ((start = fetch_item(start, end, &item)) != NULL) { ++ ++ if (item.format != HID_ITEM_FORMAT_SHORT) { ++ hid_err(device, "unexpected long global item\n"); ++ goto err; ++ } ++ ++ if (dispatch_type[item.type](parser, &item)) { ++ hid_err(device, "item %u %u %u %u parsing failed\n", ++ item.format, (unsigned)item.size, ++ (unsigned)item.type, (unsigned)item.tag); ++ goto err; ++ } ++ ++ if (start == end) { ++ if (parser->collection_stack_ptr) { ++ hid_err(device, "unbalanced collection at end of report description\n"); ++ goto err; ++ } ++ if (parser->local.delimiter_depth) { ++ hid_err(device, "unbalanced delimiter at end of report description\n"); ++ goto err; ++ } ++ vfree(parser); ++ device->status |= HID_STAT_PARSED; ++ return 0; ++ } ++ } ++ ++ hid_err(device, "item fetching failed at offset %d\n", (int)(end - start)); ++err: ++ vfree(parser); ++ hid_close_report(device); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(hid_open_report); ++ ++/* ++ * Convert a signed n-bit integer to signed 32-bit integer. Common ++ * cases are done through the compiler, the screwed things has to be ++ * done by hand. ++ */ ++ ++static s32 snto32(__u32 value, unsigned n) ++{ ++ switch (n) { ++ case 8: return ((__s8)value); ++ case 16: return ((__s16)value); ++ case 32: return ((__s32)value); ++ } ++ return value & (1 << (n - 1)) ? value | (-1 << n) : value; ++} ++ ++s32 hid_snto32(__u32 value, unsigned n) ++{ ++ return snto32(value, n); ++} ++EXPORT_SYMBOL_GPL(hid_snto32); ++ ++/* ++ * Convert a signed 32-bit integer to a signed n-bit integer. ++ */ ++ ++static u32 s32ton(__s32 value, unsigned n) ++{ ++ s32 a = value >> (n - 1); ++ if (a && a != -1) ++ return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; ++ return value & ((1 << n) - 1); ++} ++ ++/* ++ * Extract/implement a data field from/to a little endian report (bit array). ++ * ++ * Code sort-of follows HID spec: ++ * http://www.usb.org/developers/devclass_docs/HID1_11.pdf ++ * ++ * While the USB HID spec allows unlimited length bit fields in "report ++ * descriptors", most devices never use more than 16 bits. ++ * One model of UPS is claimed to report "LINEV" as a 32-bit field. ++ * Search linux-kernel and linux-usb-devel archives for "hid-core extract". ++ */ ++ ++static __u32 extract(const struct hid_device *hid, __u8 *report, ++ unsigned offset, unsigned n) ++{ ++ u64 x; ++ ++ if (n > 32) ++ hid_warn(hid, "extract() called with n (%d) > 32! (%s)\n", ++ n, current->comm); ++ ++ report += offset >> 3; /* adjust byte index */ ++ offset &= 7; /* now only need bit offset into one byte */ ++ x = get_unaligned_le64(report); ++ x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */ ++ return (u32) x; ++} ++ ++/* ++ * "implement" : set bits in a little endian bit stream. ++ * Same concepts as "extract" (see comments above). ++ * The data mangled in the bit stream remains in little endian ++ * order the whole time. It make more sense to talk about ++ * endianness of register values by considering a register ++ * a "cached" copy of the little endiad bit stream. ++ */ ++static void implement(const struct hid_device *hid, __u8 *report, ++ unsigned offset, unsigned n, __u32 value) ++{ ++ u64 x; ++ u64 m = (1ULL << n) - 1; ++ ++ if (n > 32) ++ hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n", ++ __func__, n, current->comm); ++ ++ if (value > m) ++ hid_warn(hid, "%s() called with too large value %d! (%s)\n", ++ __func__, value, current->comm); ++ WARN_ON(value > m); ++ value &= m; ++ ++ report += offset >> 3; ++ offset &= 7; ++ ++ x = get_unaligned_le64(report); ++ x &= ~(m << offset); ++ x |= ((u64)value) << offset; ++ put_unaligned_le64(x, report); ++} ++ ++/* ++ * Search an array for a value. ++ */ ++ ++static int search(__s32 *array, __s32 value, unsigned n) ++{ ++ while (n--) { ++ if (*array++ == value) ++ return 0; ++ } ++ return -1; ++} ++ ++/** ++ * hid_match_report - check if driver's raw_event should be called ++ * ++ * @hid: hid device ++ * @report_type: type to match against ++ * ++ * compare hid->driver->report_table->report_type to report->type ++ */ ++static int hid_match_report(struct hid_device *hid, struct hid_report *report) ++{ ++ const struct hid_report_id *id = hid->driver->report_table; ++ ++ if (!id) /* NULL means all */ ++ return 1; ++ ++ for (; id->report_type != HID_TERMINATOR; id++) ++ if (id->report_type == HID_ANY_ID || ++ id->report_type == report->type) ++ return 1; ++ return 0; ++} ++ ++/** ++ * hid_match_usage - check if driver's event should be called ++ * ++ * @hid: hid device ++ * @usage: usage to match against ++ * ++ * compare hid->driver->usage_table->usage_{type,code} to ++ * usage->usage_{type,code} ++ */ ++static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage) ++{ ++ const struct hid_usage_id *id = hid->driver->usage_table; ++ ++ if (!id) /* NULL means all */ ++ return 1; ++ ++ for (; id->usage_type != HID_ANY_ID - 1; id++) ++ if ((id->usage_hid == HID_ANY_ID || ++ id->usage_hid == usage->hid) && ++ (id->usage_type == HID_ANY_ID || ++ id->usage_type == usage->type) && ++ (id->usage_code == HID_ANY_ID || ++ id->usage_code == usage->code)) ++ return 1; ++ return 0; ++} ++ ++static void hid_process_event(struct hid_device *hid, struct hid_field *field, ++ struct hid_usage *usage, __s32 value, int interrupt) ++{ ++ struct hid_driver *hdrv = hid->driver; ++ int ret; ++ ++ if (!list_empty(&hid->debug_list)) ++ hid_dump_input(hid, usage, value); ++ ++ if (hdrv && hdrv->event && hid_match_usage(hid, usage)) { ++ ret = hdrv->event(hid, field, usage, value); ++ if (ret != 0) { ++ if (ret < 0) ++ hid_err(hid, "%s's event failed with %d\n", ++ hdrv->name, ret); ++ return; ++ } ++ } ++ ++ if (hid->claimed & HID_CLAIMED_INPUT) ++ hidinput_hid_event(hid, field, usage, value); ++ if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event) ++ hid->hiddev_hid_event(hid, field, usage, value); ++} ++ ++/* ++ * Analyse a received field, and fetch the data from it. The field ++ * content is stored for next report processing (we do differential ++ * reporting to the layer). ++ */ ++ ++static void hid_input_field(struct hid_device *hid, struct hid_field *field, ++ __u8 *data, int interrupt) ++{ ++ unsigned n; ++ unsigned count = field->report_count; ++ unsigned offset = field->report_offset; ++ unsigned size = field->report_size; ++ __s32 min = field->logical_minimum; ++ __s32 max = field->logical_maximum; ++ __s32 *value; ++ ++ value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC); ++ if (!value) ++ return; ++ ++ for (n = 0; n < count; n++) { ++ ++ value[n] = min < 0 ? ++ snto32(extract(hid, data, offset + n * size, size), ++ size) : ++ extract(hid, data, offset + n * size, size); ++ ++ /* Ignore report if ErrorRollOver */ ++ if (!(field->flags & HID_MAIN_ITEM_VARIABLE) && ++ value[n] >= min && value[n] <= max && ++ field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) ++ goto exit; ++ } ++ ++ for (n = 0; n < count; n++) { ++ ++ if (HID_MAIN_ITEM_VARIABLE & field->flags) { ++ hid_process_event(hid, field, &field->usage[n], value[n], interrupt); ++ continue; ++ } ++ ++ if (field->value[n] >= min && field->value[n] <= max ++ && field->usage[field->value[n] - min].hid ++ && search(value, field->value[n], count)) ++ hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt); ++ ++ if (value[n] >= min && value[n] <= max ++ && field->usage[value[n] - min].hid ++ && search(field->value, value[n], count)) ++ hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt); ++ } ++ ++ memcpy(field->value, value, count * sizeof(__s32)); ++exit: ++ kfree(value); ++} ++ ++/* ++ * Output the field into the report. ++ */ ++ ++static void hid_output_field(const struct hid_device *hid, ++ struct hid_field *field, __u8 *data) ++{ ++ unsigned count = field->report_count; ++ unsigned offset = field->report_offset; ++ unsigned size = field->report_size; ++ unsigned n; ++ ++ for (n = 0; n < count; n++) { ++ if (field->logical_minimum < 0) /* signed values */ ++ implement(hid, data, offset + n * size, size, ++ s32ton(field->value[n], size)); ++ else /* unsigned values */ ++ implement(hid, data, offset + n * size, size, ++ field->value[n]); ++ } ++} ++ ++/* ++ * Create a report. 'data' has to be allocated using ++ * hid_alloc_report_buf() so that it has proper size. ++ */ ++ ++void hid_output_report(struct hid_report *report, __u8 *data) ++{ ++ unsigned n; ++ ++ if (report->id > 0) ++ *data++ = report->id; ++ ++ memset(data, 0, ((report->size - 1) >> 3) + 1); ++ for (n = 0; n < report->maxfield; n++) ++ hid_output_field(report->device, report->field[n], data); ++} ++EXPORT_SYMBOL_GPL(hid_output_report); ++ ++/* ++ * Allocator for buffer that is going to be passed to hid_output_report() ++ */ ++u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) ++{ ++ /* ++ * 7 extra bytes are necessary to achieve proper functionality ++ * of implement() working on 8 byte chunks ++ */ ++ ++ int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; ++ ++ return kmalloc(len, flags); ++} ++EXPORT_SYMBOL_GPL(hid_alloc_report_buf); ++ ++/* ++ * Set a field value. The report this field belongs to has to be ++ * created and transferred to the device, to set this value in the ++ * device. ++ */ ++ ++int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) ++{ ++ unsigned size; ++ ++ if (!field) ++ return -1; ++ ++ size = field->report_size; ++ ++ hid_dump_input(field->report->device, field->usage + offset, value); ++ ++ if (offset >= field->report_count) { ++ hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n", ++ offset, field->report_count); ++ return -1; ++ } ++ if (field->logical_minimum < 0) { ++ if (value != snto32(s32ton(value, size), size)) { ++ hid_err(field->report->device, "value %d is out of range\n", value); ++ return -1; ++ } ++ } ++ field->value[offset] = value; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(hid_set_field); ++ ++static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, ++ const u8 *data) ++{ ++ struct hid_report *report; ++ unsigned int n = 0; /* Normally report number is 0 */ ++ ++ /* Device uses numbered reports, data[0] is report number */ ++ if (report_enum->numbered) ++ n = *data; ++ ++ report = report_enum->report_id_hash[n]; ++ if (report == NULL) ++ dbg_hid("undefined report_id %u received\n", n); ++ ++ return report; ++} ++ ++int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, ++ int interrupt) ++{ ++ struct hid_report_enum *report_enum = hid->report_enum + type; ++ struct hid_report *report; ++ struct hid_driver *hdrv; ++ unsigned int a; ++ int rsize, csize = size; ++ u8 *cdata = data; ++ int ret = 0; ++ ++ report = hid_get_report(report_enum, data); ++ if (!report) ++ goto out; ++ ++ if (report_enum->numbered) { ++ cdata++; ++ csize--; ++ } ++ ++ rsize = ((report->size - 1) >> 3) + 1; ++ ++ if (rsize > HID_MAX_BUFFER_SIZE) ++ rsize = HID_MAX_BUFFER_SIZE; ++ ++ if (csize < rsize) { ++ dbg_hid("report %d is too short, (%d < %d)\n", report->id, ++ csize, rsize); ++ memset(cdata + csize, 0, rsize - csize); ++ } ++ ++ if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) ++ hid->hiddev_report_event(hid, report); ++ if (hid->claimed & HID_CLAIMED_HIDRAW) { ++ ret = hidraw_report_event(hid, data, size); ++ if (ret) ++ goto out; ++ } ++ ++ if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { ++ for (a = 0; a < report->maxfield; a++) ++ hid_input_field(hid, report->field[a], cdata, interrupt); ++ hdrv = hid->driver; ++ if (hdrv && hdrv->report) ++ hdrv->report(hid, report); ++ } ++ ++ if (hid->claimed & HID_CLAIMED_INPUT) ++ hidinput_report_event(hid, report); ++out: ++ return ret; ++} ++EXPORT_SYMBOL_GPL(hid_report_raw_event); ++ ++/** ++ * hid_input_report - report data from lower layer (usb, bt...) ++ * ++ * @hid: hid device ++ * @type: HID report type (HID_*_REPORT) ++ * @data: report contents ++ * @size: size of data parameter ++ * @interrupt: distinguish between interrupt and control transfers ++ * ++ * This is data entry for lower layers. ++ */ ++int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) ++{ ++ struct hid_report_enum *report_enum; ++ struct hid_driver *hdrv; ++ struct hid_report *report; ++ int ret = 0; ++ ++ if (!hid) ++ return -ENODEV; ++ ++ if (down_trylock(&hid->driver_input_lock)) ++ return -EBUSY; ++ ++ if (!hid->driver) { ++ ret = -ENODEV; ++ goto unlock; ++ } ++ report_enum = hid->report_enum + type; ++ hdrv = hid->driver; ++ ++ if (!size) { ++ dbg_hid("empty report\n"); ++ ret = -1; ++ goto unlock; ++ } ++ ++ /* Avoid unnecessary overhead if debugfs is disabled */ ++ if (!list_empty(&hid->debug_list)) ++ hid_dump_report(hid, type, data, size); ++ ++ report = hid_get_report(report_enum, data); ++ ++ if (!report) { ++ ret = -1; ++ goto unlock; ++ } ++ ++ if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { ++ ret = hdrv->raw_event(hid, report, data, size); ++ if (ret < 0) ++ goto unlock; ++ } ++ ++ ret = hid_report_raw_event(hid, type, data, size, interrupt); ++ ++unlock: ++ up(&hid->driver_input_lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(hid_input_report); ++ ++static bool hid_match_one_id(struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ return (id->bus == HID_BUS_ANY || id->bus == hdev->bus) && ++ (id->group == HID_GROUP_ANY || id->group == hdev->group) && ++ (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) && ++ (id->product == HID_ANY_ID || id->product == hdev->product); ++} ++ ++const struct hid_device_id *hid_match_id(struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ for (; id->bus; id++) ++ if (hid_match_one_id(hdev, id)) ++ return id; ++ ++ return NULL; ++} ++ ++static const struct hid_device_id hid_hiddev_list[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1) }, ++ { } ++}; ++ ++static bool hid_hiddev(struct hid_device *hdev) ++{ ++ return !!hid_match_id(hdev, hid_hiddev_list); ++} ++ ++ ++static ssize_t ++read_report_descriptor(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, ++ char *buf, loff_t off, size_t count) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ ++ if (off >= hdev->rsize) ++ return 0; ++ ++ if (off + count > hdev->rsize) ++ count = hdev->rsize - off; ++ ++ memcpy(buf, hdev->rdesc + off, count); ++ ++ return count; ++} ++ ++static struct bin_attribute dev_bin_attr_report_desc = { ++ .attr = { .name = "report_descriptor", .mode = 0444 }, ++ .read = read_report_descriptor, ++ .size = HID_MAX_DESCRIPTOR_SIZE, ++}; ++ ++int hid_connect(struct hid_device *hdev, unsigned int connect_mask) ++{ ++ static const char *types[] = { "Device", "Pointer", "Mouse", "Device", ++ "Joystick", "Gamepad", "Keyboard", "Keypad", ++ "Multi-Axis Controller" ++ }; ++ const char *type, *bus; ++ char buf[64]; ++ unsigned int i; ++ int len; ++ int ret; ++ ++ if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) ++ connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); ++ if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE) ++ connect_mask |= HID_CONNECT_HIDINPUT_FORCE; ++ if (hdev->bus != BUS_USB) ++ connect_mask &= ~HID_CONNECT_HIDDEV; ++ if (hid_hiddev(hdev)) ++ connect_mask |= HID_CONNECT_HIDDEV_FORCE; ++ ++ if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, ++ connect_mask & HID_CONNECT_HIDINPUT_FORCE)) ++ hdev->claimed |= HID_CLAIMED_INPUT; ++ ++ if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && ++ !hdev->hiddev_connect(hdev, ++ connect_mask & HID_CONNECT_HIDDEV_FORCE)) ++ hdev->claimed |= HID_CLAIMED_HIDDEV; ++ if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev)) ++ hdev->claimed |= HID_CLAIMED_HIDRAW; ++ ++ /* Drivers with the ->raw_event callback set are not required to connect ++ * to any other listener. */ ++ if (!hdev->claimed && !hdev->driver->raw_event) { ++ hid_err(hdev, "device has no listeners, quitting\n"); ++ return -ENODEV; ++ } ++ ++ if ((hdev->claimed & HID_CLAIMED_INPUT) && ++ (connect_mask & HID_CONNECT_FF) && hdev->ff_init) ++ hdev->ff_init(hdev); ++ ++ len = 0; ++ if (hdev->claimed & HID_CLAIMED_INPUT) ++ len += sprintf(buf + len, "input"); ++ if (hdev->claimed & HID_CLAIMED_HIDDEV) ++ len += sprintf(buf + len, "%shiddev%d", len ? "," : "", ++ hdev->minor); ++ if (hdev->claimed & HID_CLAIMED_HIDRAW) ++ len += sprintf(buf + len, "%shidraw%d", len ? "," : "", ++ ((struct hidraw *)hdev->hidraw)->minor); ++ ++ type = "Device"; ++ for (i = 0; i < hdev->maxcollection; i++) { ++ struct hid_collection *col = &hdev->collection[i]; ++ if (col->type == HID_COLLECTION_APPLICATION && ++ (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK && ++ (col->usage & 0xffff) < ARRAY_SIZE(types)) { ++ type = types[col->usage & 0xffff]; ++ break; ++ } ++ } ++ ++ switch (hdev->bus) { ++ case BUS_USB: ++ bus = "USB"; ++ break; ++ case BUS_BLUETOOTH: ++ bus = "BLUETOOTH"; ++ break; ++ default: ++ bus = ""; ++ } ++ ++ ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc); ++ if (ret) ++ hid_warn(hdev, ++ "can't create sysfs report descriptor attribute err: %d\n", ret); ++ ++ hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n", ++ buf, bus, hdev->version >> 8, hdev->version & 0xff, ++ type, hdev->name, hdev->phys); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(hid_connect); ++ ++void hid_disconnect(struct hid_device *hdev) ++{ ++ device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc); ++ if (hdev->claimed & HID_CLAIMED_INPUT) ++ hidinput_disconnect(hdev); ++ if (hdev->claimed & HID_CLAIMED_HIDDEV) ++ hdev->hiddev_disconnect(hdev); ++ if (hdev->claimed & HID_CLAIMED_HIDRAW) ++ hidraw_disconnect(hdev); ++} ++EXPORT_SYMBOL_GPL(hid_disconnect); ++ ++/* ++ * A list of devices for which there is a specialized driver on HID bus. ++ * ++ * Please note that for multitouch devices (driven by hid-multitouch driver), ++ * there is a proper autodetection and autoloading in place (based on presence ++ * of HID_DG_CONTACTID), so those devices don't need to be added to this list, ++ * as we are doing the right thing in hid_scan_usage(). ++ * ++ * Autodetection for (USB) HID sensor hubs exists too. If a collection of type ++ * physical is found inside a usage page of type sensor, hid-sensor-hub will be ++ * used as a driver. See hid_scan_report(). ++ */ ++static const struct hid_device_id hid_have_special_driver[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, ++#if IS_ENABLED(CONFIG_HID_LENOVO_TPKBD) ++ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, ++#endif ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL) }, ++#if IS_ENABLED(CONFIG_HID_LOGITECH_DJ) ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2) }, ++#endif ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_4) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_5) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_6) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_7) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_8) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_9) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_10) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_11) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_12) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_13) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_14) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_15) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_OUYA, USB_DEVICE_ID_OUYA_CONTROLLER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, ++#if IS_ENABLED(CONFIG_HID_ROCCAT) ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, ++#endif ++ { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_PID_0038) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) }, ++ ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, ++ { } ++}; ++ ++struct hid_dynid { ++ struct list_head list; ++ struct hid_device_id id; ++}; ++ ++/** ++ * store_new_id - add a new HID device ID to this driver and re-probe devices ++ * @driver: target device driver ++ * @buf: buffer for scanning device ID data ++ * @count: input size ++ * ++ * Adds a new dynamic hid device ID to this driver, ++ * and causes the driver to probe for all devices again. ++ */ ++static ssize_t store_new_id(struct device_driver *drv, const char *buf, ++ size_t count) ++{ ++ struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); ++ struct hid_dynid *dynid; ++ __u32 bus, vendor, product; ++ unsigned long driver_data = 0; ++ int ret; ++ ++ ret = sscanf(buf, "%x %x %x %lx", ++ &bus, &vendor, &product, &driver_data); ++ if (ret < 3) ++ return -EINVAL; ++ ++ dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); ++ if (!dynid) ++ return -ENOMEM; ++ ++ dynid->id.bus = bus; ++ dynid->id.group = HID_GROUP_ANY; ++ dynid->id.vendor = vendor; ++ dynid->id.product = product; ++ dynid->id.driver_data = driver_data; ++ ++ spin_lock(&hdrv->dyn_lock); ++ list_add_tail(&dynid->list, &hdrv->dyn_list); ++ spin_unlock(&hdrv->dyn_lock); ++ ++ ret = driver_attach(&hdrv->driver); ++ ++ return ret ? : count; ++} ++static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); ++ ++static void hid_free_dynids(struct hid_driver *hdrv) ++{ ++ struct hid_dynid *dynid, *n; ++ ++ spin_lock(&hdrv->dyn_lock); ++ list_for_each_entry_safe(dynid, n, &hdrv->dyn_list, list) { ++ list_del(&dynid->list); ++ kfree(dynid); ++ } ++ spin_unlock(&hdrv->dyn_lock); ++} ++ ++static const struct hid_device_id *hid_match_device(struct hid_device *hdev, ++ struct hid_driver *hdrv) ++{ ++ struct hid_dynid *dynid; ++ ++ spin_lock(&hdrv->dyn_lock); ++ list_for_each_entry(dynid, &hdrv->dyn_list, list) { ++ if (hid_match_one_id(hdev, &dynid->id)) { ++ spin_unlock(&hdrv->dyn_lock); ++ return &dynid->id; ++ } ++ } ++ spin_unlock(&hdrv->dyn_lock); ++ ++ return hid_match_id(hdev, hdrv->id_table); ++} ++ ++static int hid_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ ++ return hid_match_device(hdev, hdrv) != NULL; ++} ++ ++static int hid_device_probe(struct device *dev) ++{ ++ struct hid_driver *hdrv = container_of(dev->driver, ++ struct hid_driver, driver); ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ const struct hid_device_id *id; ++ int ret = 0; ++ ++ if (down_interruptible(&hdev->driver_lock)) ++ return -EINTR; ++ if (down_interruptible(&hdev->driver_input_lock)) { ++ ret = -EINTR; ++ goto unlock_driver_lock; ++ } ++ hdev->io_started = false; ++ ++ if (!hdev->driver) { ++ id = hid_match_device(hdev, hdrv); ++ if (id == NULL) { ++ ret = -ENODEV; ++ goto unlock; ++ } ++ ++ hdev->driver = hdrv; ++ if (hdrv->probe) { ++ ret = hdrv->probe(hdev, id); ++ } else { /* default probe */ ++ ret = hid_open_report(hdev); ++ if (!ret) ++ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); ++ } ++ if (ret) { ++ hid_close_report(hdev); ++ hdev->driver = NULL; ++ } ++ } ++unlock: ++ if (!hdev->io_started) ++ up(&hdev->driver_input_lock); ++unlock_driver_lock: ++ up(&hdev->driver_lock); ++ return ret; ++} ++ ++static int hid_device_remove(struct device *dev) ++{ ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ struct hid_driver *hdrv; ++ int ret = 0; ++ ++ if (down_interruptible(&hdev->driver_lock)) ++ return -EINTR; ++ if (down_interruptible(&hdev->driver_input_lock)) { ++ ret = -EINTR; ++ goto unlock_driver_lock; ++ } ++ hdev->io_started = false; ++ ++ hdrv = hdev->driver; ++ if (hdrv) { ++ if (hdrv->remove) ++ hdrv->remove(hdev); ++ else /* default remove */ ++ hid_hw_stop(hdev); ++ hid_close_report(hdev); ++ hdev->driver = NULL; ++ } ++ ++ if (!hdev->io_started) ++ up(&hdev->driver_input_lock); ++unlock_driver_lock: ++ up(&hdev->driver_lock); ++ return ret; ++} ++ ++static ssize_t modalias_show(struct device *dev, struct device_attribute *a, ++ char *buf) ++{ ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ int len; ++ ++ len = snprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n", ++ hdev->bus, hdev->group, hdev->vendor, hdev->product); ++ ++ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; ++} ++static DEVICE_ATTR_RO(modalias); ++ ++static struct attribute *hid_dev_attrs[] = { ++ &dev_attr_modalias.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(hid_dev); ++ ++static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ ++ if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X", ++ hdev->bus, hdev->vendor, hdev->product)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, "HID_NAME=%s", hdev->name)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X", ++ hdev->bus, hdev->group, hdev->vendor, hdev->product)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static struct bus_type hid_bus_type = { ++ .name = "hid", ++ .dev_groups = hid_dev_groups, ++ .match = hid_bus_match, ++ .probe = hid_device_probe, ++ .remove = hid_device_remove, ++ .uevent = hid_uevent, ++}; ++ ++/* a list of devices that shouldn't be handled by HID core at all */ ++static const struct hid_device_id hid_ignore_list[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)}, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)}, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_AXENTIA, USB_DEVICE_ID_AXENTIA_FM_RADIO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI4713) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_RADIOSHARK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_KYE, 0x0058) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYVOLTAGE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYCURRENT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIC) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIB) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOTOR) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_ABSESP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_AUTODATABUS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_BEATPAD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0001) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, ++#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE) ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_STICK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_COMP_TP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) }, ++#endif ++ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, ++ { } ++}; ++ ++/** ++ * hid_mouse_ignore_list - mouse devices which should not be handled by the hid layer ++ * ++ * There are composite devices for which we want to ignore only a certain ++ * interface. This is a list of devices for which only the mouse interface will ++ * be ignored. This allows a dedicated driver to take care of the interface. ++ */ ++static const struct hid_device_id hid_mouse_ignore_list[] = { ++ /* appletouch driver */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, ++ { } ++}; ++ ++bool hid_ignore(struct hid_device *hdev) ++{ ++ if (hdev->quirks & HID_QUIRK_NO_IGNORE) ++ return false; ++ if (hdev->quirks & HID_QUIRK_IGNORE) ++ return true; ++ ++ switch (hdev->vendor) { ++ case USB_VENDOR_ID_CODEMERCS: ++ /* ignore all Code Mercenaries IOWarrior devices */ ++ if (hdev->product >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && ++ hdev->product <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) ++ return true; ++ break; ++ case USB_VENDOR_ID_LOGITECH: ++ if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST && ++ hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST) ++ return true; ++ /* ++ * The Keene FM transmitter USB device has the same USB ID as ++ * the Logitech AudioHub Speaker, but it should ignore the hid. ++ * Check if the name is that of the Keene device. ++ * For reference: the name of the AudioHub is ++ * "HOLTEK AudioHub Speaker". ++ */ ++ if (hdev->product == USB_DEVICE_ID_LOGITECH_AUDIOHUB && ++ !strcmp(hdev->name, "HOLTEK B-LINK USB Audio ")) ++ return true; ++ break; ++ case USB_VENDOR_ID_SOUNDGRAPH: ++ if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST && ++ hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST) ++ return true; ++ break; ++ case USB_VENDOR_ID_HANWANG: ++ if (hdev->product >= USB_DEVICE_ID_HANWANG_TABLET_FIRST && ++ hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST) ++ return true; ++ break; ++ case USB_VENDOR_ID_JESS: ++ if (hdev->product == USB_DEVICE_ID_JESS_YUREX && ++ hdev->type == HID_TYPE_USBNONE) ++ return true; ++ break; ++ case USB_VENDOR_ID_VELLEMAN: ++ /* These are not HID devices. They are handled by comedi. */ ++ if ((hdev->product >= USB_DEVICE_ID_VELLEMAN_K8055_FIRST && ++ hdev->product <= USB_DEVICE_ID_VELLEMAN_K8055_LAST) || ++ (hdev->product >= USB_DEVICE_ID_VELLEMAN_K8061_FIRST && ++ hdev->product <= USB_DEVICE_ID_VELLEMAN_K8061_LAST)) ++ return true; ++ break; ++ case USB_VENDOR_ID_ATMEL_V_USB: ++ /* Masterkit MA901 usb radio based on Atmel tiny85 chip and ++ * it has the same USB ID as many Atmel V-USB devices. This ++ * usb radio is handled by radio-ma901.c driver so we want ++ * ignore the hid. Check the name, bus, product and ignore ++ * if we have MA901 usb radio. ++ */ ++ if (hdev->product == USB_DEVICE_ID_ATMEL_V_USB && ++ hdev->bus == BUS_USB && ++ strncmp(hdev->name, "www.masterkit.ru MA901", 22) == 0) ++ return true; ++ break; ++ } ++ ++ if (hdev->type == HID_TYPE_USBMOUSE && ++ hid_match_id(hdev, hid_mouse_ignore_list)) ++ return true; ++ ++ return !!hid_match_id(hdev, hid_ignore_list); ++} ++EXPORT_SYMBOL_GPL(hid_ignore); ++ ++int hid_add_device(struct hid_device *hdev) ++{ ++ static atomic_t id = ATOMIC_INIT(0); ++ int ret; ++ ++ if (WARN_ON(hdev->status & HID_STAT_ADDED)) ++ return -EBUSY; ++ ++ /* we need to kill them here, otherwise they will stay allocated to ++ * wait for coming driver */ ++ if (hid_ignore(hdev)) ++ return -ENODEV; ++ ++ /* ++ * Read the device report descriptor once and use as template ++ * for the driver-specific modifications. ++ */ ++ ret = hdev->ll_driver->parse(hdev); ++ if (ret) ++ return ret; ++ if (!hdev->dev_rdesc) ++ return -ENODEV; ++ ++ /* ++ * Scan generic devices for group information ++ */ ++ if (hid_ignore_special_drivers || ++ !hid_match_id(hdev, hid_have_special_driver)) { ++ ret = hid_scan_report(hdev); ++ if (ret) ++ hid_warn(hdev, "bad device descriptor (%d)\n", ret); ++ } ++ ++ /* XXX hack, any other cleaner solution after the driver core ++ * is converted to allow more than 20 bytes as the device name? */ ++ dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, ++ hdev->vendor, hdev->product, atomic_inc_return(&id)); ++ ++ hid_debug_register(hdev, dev_name(&hdev->dev)); ++ ret = device_add(&hdev->dev); ++ if (!ret) ++ hdev->status |= HID_STAT_ADDED; ++ else ++ hid_debug_unregister(hdev); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(hid_add_device); ++ ++/** ++ * hid_allocate_device - allocate new hid device descriptor ++ * ++ * Allocate and initialize hid device, so that hid_destroy_device might be ++ * used to free it. ++ * ++ * New hid_device pointer is returned on success, otherwise ERR_PTR encoded ++ * error value. ++ */ ++struct hid_device *hid_allocate_device(void) ++{ ++ struct hid_device *hdev; ++ int ret = -ENOMEM; ++ ++ hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); ++ if (hdev == NULL) ++ return ERR_PTR(ret); ++ ++ device_initialize(&hdev->dev); ++ hdev->dev.release = hid_device_release; ++ hdev->dev.bus = &hid_bus_type; ++ ++ hid_close_report(hdev); ++ ++ init_waitqueue_head(&hdev->debug_wait); ++ INIT_LIST_HEAD(&hdev->debug_list); ++ spin_lock_init(&hdev->debug_list_lock); ++ sema_init(&hdev->driver_lock, 1); ++ sema_init(&hdev->driver_input_lock, 1); ++ ++ return hdev; ++} ++EXPORT_SYMBOL_GPL(hid_allocate_device); ++ ++static void hid_remove_device(struct hid_device *hdev) ++{ ++ if (hdev->status & HID_STAT_ADDED) { ++ device_del(&hdev->dev); ++ hid_debug_unregister(hdev); ++ hdev->status &= ~HID_STAT_ADDED; ++ } ++ kfree(hdev->dev_rdesc); ++ hdev->dev_rdesc = NULL; ++ hdev->dev_rsize = 0; ++} ++ ++/** ++ * hid_destroy_device - free previously allocated device ++ * ++ * @hdev: hid device ++ * ++ * If you allocate hid_device through hid_allocate_device, you should ever ++ * free by this function. ++ */ ++void hid_destroy_device(struct hid_device *hdev) ++{ ++ hid_remove_device(hdev); ++ put_device(&hdev->dev); ++} ++EXPORT_SYMBOL_GPL(hid_destroy_device); ++ ++int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, ++ const char *mod_name) ++{ ++ int ret; ++ ++ hdrv->driver.name = hdrv->name; ++ hdrv->driver.bus = &hid_bus_type; ++ hdrv->driver.owner = owner; ++ hdrv->driver.mod_name = mod_name; ++ ++ INIT_LIST_HEAD(&hdrv->dyn_list); ++ spin_lock_init(&hdrv->dyn_lock); ++ ++ ret = driver_register(&hdrv->driver); ++ if (ret) ++ return ret; ++ ++ ret = driver_create_file(&hdrv->driver, &driver_attr_new_id); ++ if (ret) ++ driver_unregister(&hdrv->driver); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(__hid_register_driver); ++ ++void hid_unregister_driver(struct hid_driver *hdrv) ++{ ++ driver_remove_file(&hdrv->driver, &driver_attr_new_id); ++ driver_unregister(&hdrv->driver); ++ hid_free_dynids(hdrv); ++} ++EXPORT_SYMBOL_GPL(hid_unregister_driver); ++ ++int hid_check_keys_pressed(struct hid_device *hid) ++{ ++ struct hid_input *hidinput; ++ int i; ++ ++ if (!(hid->claimed & HID_CLAIMED_INPUT)) ++ return 0; ++ ++ list_for_each_entry(hidinput, &hid->inputs, list) { ++ for (i = 0; i < BITS_TO_LONGS(KEY_MAX); i++) ++ if (hidinput->input->key[i]) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(hid_check_keys_pressed); ++ ++static int __init hid_init(void) ++{ ++ int ret; ++ ++ if (hid_debug) ++ pr_warn("hid_debug is now used solely for parser and driver debugging.\n" ++ "debugfs is now used for inspecting the device (report descriptor, reports)\n"); ++ ++ ret = bus_register(&hid_bus_type); ++ if (ret) { ++ pr_err("can't register hid bus\n"); ++ goto err; ++ } ++ ++ ret = hidraw_init(); ++ if (ret) ++ goto err_bus; ++ ++ hid_debug_init(); ++ ++ return 0; ++err_bus: ++ bus_unregister(&hid_bus_type); ++err: ++ return ret; ++} ++ ++static void __exit hid_exit(void) ++{ ++ hid_debug_exit(); ++ hidraw_exit(); ++ bus_unregister(&hid_bus_type); ++} ++ ++module_init(hid_init); ++module_exit(hid_exit); ++ ++MODULE_AUTHOR("Andreas Gal"); ++MODULE_AUTHOR("Vojtech Pavlik"); ++MODULE_AUTHOR("Jiri Kosina"); ++MODULE_LICENSE(DRIVER_LICENSE); ++ +diff -Nur linux-3.14.36/drivers/hid/hid-ids.h linux-openelec/drivers/hid/hid-ids.h +--- linux-3.14.36/drivers/hid/hid-ids.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hid/hid-ids.h 2015-07-24 18:03:30.048842002 -0500 +@@ -697,6 +697,9 @@ + #define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 + #define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 + ++#define USB_VENDOR_ID_OUYA 0x2836 ++#define USB_DEVICE_ID_OUYA_CONTROLLER 0x0001 ++ + #define USB_VENDOR_ID_PANASONIC 0x04da + #define USB_DEVICE_ID_PANABOARD_UBT780 0x1044 + #define USB_DEVICE_ID_PANABOARD_UBT880 0x104d +@@ -714,6 +717,9 @@ + + #define USB_VENDOR_ID_PHILIPS 0x0471 + #define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617 ++#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1 0x206c ++#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2 0x20cc ++#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3 0x0613 + + #define USB_VENDOR_ID_PI_ENGINEERING 0x05f3 + #define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff +@@ -784,6 +790,7 @@ + #define USB_VENDOR_ID_SKYCABLE 0x1223 + #define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 + ++#define USB_VENDOR_ID_SMK 0x0609 + #define USB_VENDOR_ID_SONY 0x054c + #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b + #define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE 0x0374 +@@ -844,6 +851,7 @@ + #define USB_VENDOR_ID_TIVO 0x150a + #define USB_DEVICE_ID_TIVO_SLIDE_BT 0x1200 + #define USB_DEVICE_ID_TIVO_SLIDE 0x1201 ++#define USB_DEVICE_ID_TIVO_SLIDE_PRO 0x1203 + + #define USB_VENDOR_ID_TOPSEED 0x0766 + #define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 +diff -Nur linux-3.14.36/drivers/hid/hid-ids.h.orig linux-openelec/drivers/hid/hid-ids.h.orig +--- linux-3.14.36/drivers/hid/hid-ids.h.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hid/hid-ids.h.orig 2015-07-24 18:03:29.980842002 -0500 +@@ -0,0 +1,976 @@ ++/* ++ * USB HID quirks support for Linux ++ * ++ * Copyright (c) 1999 Andreas Gal ++ * Copyright (c) 2000-2005 Vojtech Pavlik ++ * Copyright (c) 2005 Michael Haboustak for Concept2, Inc ++ * Copyright (c) 2006-2007 Jiri Kosina ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#ifndef HID_IDS_H_FILE ++#define HID_IDS_H_FILE ++ ++#define USB_VENDOR_ID_3M 0x0596 ++#define USB_DEVICE_ID_3M1968 0x0500 ++#define USB_DEVICE_ID_3M2256 0x0502 ++#define USB_DEVICE_ID_3M3266 0x0506 ++ ++#define USB_VENDOR_ID_A4TECH 0x09da ++#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 ++#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a ++#define USB_DEVICE_ID_A4TECH_RP_649 0x001a ++ ++#define USB_VENDOR_ID_AASHIMA 0x06d6 ++#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 ++#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 ++ ++#define USB_VENDOR_ID_ACECAD 0x0460 ++#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 ++#define USB_DEVICE_ID_ACECAD_302 0x0008 ++ ++#define USB_VENDOR_ID_ACRUX 0x1a34 ++ ++#define USB_VENDOR_ID_ACTIONSTAR 0x2101 ++#define USB_DEVICE_ID_ACTIONSTAR_1011 0x1011 ++ ++#define USB_VENDOR_ID_ADS_TECH 0x06e1 ++#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155 ++ ++#define USB_VENDOR_ID_AFATECH 0x15a4 ++#define USB_DEVICE_ID_AFATECH_AF9016 0x9016 ++ ++#define USB_VENDOR_ID_AIPTEK 0x08ca ++#define USB_DEVICE_ID_AIPTEK_01 0x0001 ++#define USB_DEVICE_ID_AIPTEK_10 0x0010 ++#define USB_DEVICE_ID_AIPTEK_20 0x0020 ++#define USB_DEVICE_ID_AIPTEK_21 0x0021 ++#define USB_DEVICE_ID_AIPTEK_22 0x0022 ++#define USB_DEVICE_ID_AIPTEK_23 0x0023 ++#define USB_DEVICE_ID_AIPTEK_24 0x0024 ++ ++#define USB_VENDOR_ID_AIRCABLE 0x16CA ++#define USB_DEVICE_ID_AIRCABLE1 0x1502 ++ ++#define USB_VENDOR_ID_AIREN 0x1a2c ++#define USB_DEVICE_ID_AIREN_SLIMPLUS 0x0002 ++ ++#define USB_VENDOR_ID_ALCOR 0x058f ++#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 ++ ++#define USB_VENDOR_ID_ALPS 0x0433 ++#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 ++ ++#define USB_VENDOR_ID_APPLE 0x05ac ++#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 ++#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d ++#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e ++#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e ++#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f ++#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 ++#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 ++#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 ++#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 ++#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 ++#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 ++#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a ++#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b ++#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c ++#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d ++#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e ++#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f ++#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 ++#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 ++#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 ++#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223 ++#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224 ++#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225 ++#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI 0x0229 ++#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO 0x022a ++#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS 0x022b ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e ++#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 ++#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 ++#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 ++#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236 ++#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237 ++#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238 ++#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f ++#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240 ++#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241 ++#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 ++#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 ++#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 ++#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 ++#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 ++#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 ++#define USB_DEVICE_ID_APPLE_ALU_REVB_ANSI 0x024f ++#define USB_DEVICE_ID_APPLE_ALU_REVB_ISO 0x0250 ++#define USB_DEVICE_ID_APPLE_ALU_REVB_JIS 0x0251 ++#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252 ++#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253 ++#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254 ++#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259 ++#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a ++#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b ++#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249 ++#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a ++#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b ++#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c ++#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d ++#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e ++#define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI 0x0262 ++#define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO 0x0263 ++#define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS 0x0264 ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 ++#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS 0x0257 ++#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 ++#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 ++#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 ++#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a ++#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b ++#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 ++#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 ++#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 ++#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 ++#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 ++ ++#define USB_VENDOR_ID_ASUS 0x0486 ++#define USB_DEVICE_ID_ASUS_T91MT 0x0185 ++#define USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO 0x0186 ++ ++#define USB_VENDOR_ID_ASUSTEK 0x0b05 ++#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726 ++#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b ++ ++#define USB_VENDOR_ID_ATEN 0x0557 ++#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 ++#define USB_DEVICE_ID_ATEN_CS124U 0x2202 ++#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 ++#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 ++#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 ++ ++#define USB_VENDOR_ID_ATMEL 0x03eb ++#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c ++#define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118 ++#define USB_VENDOR_ID_ATMEL_V_USB 0x16c0 ++#define USB_DEVICE_ID_ATMEL_V_USB 0x05df ++ ++#define USB_VENDOR_ID_AUREAL 0x0755 ++#define USB_DEVICE_ID_AUREAL_W01RN 0x2626 ++ ++#define USB_VENDOR_ID_AVERMEDIA 0x07ca ++#define USB_DEVICE_ID_AVER_FM_MR800 0xb800 ++ ++#define USB_VENDOR_ID_AXENTIA 0x12cf ++#define USB_DEVICE_ID_AXENTIA_FM_RADIO 0x7111 ++ ++#define USB_VENDOR_ID_BAANTO 0x2453 ++#define USB_DEVICE_ID_BAANTO_MT_190W2 0x0100 ++ ++#define USB_VENDOR_ID_BELKIN 0x050d ++#define USB_DEVICE_ID_FLIP_KVM 0x3201 ++ ++#define USB_VENDOR_ID_BERKSHIRE 0x0c98 ++#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 ++ ++#define USB_VENDOR_ID_BTC 0x046e ++#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 ++#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 ++ ++#define USB_VENDOR_ID_CANDO 0x2087 ++#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703 ++#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01 ++#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1 0x0a02 ++#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03 ++#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6 0x0f01 ++ ++#define USB_VENDOR_ID_CH 0x068e ++#define USB_DEVICE_ID_CH_PRO_THROTTLE 0x00f1 ++#define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2 ++#define USB_DEVICE_ID_CH_FIGHTERSTICK 0x00f3 ++#define USB_DEVICE_ID_CH_COMBATSTICK 0x00f4 ++#define USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE 0x0051 ++#define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff ++#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3 ++#define USB_DEVICE_ID_CH_AXIS_295 0x001c ++ ++#define USB_VENDOR_ID_CHERRY 0x046a ++#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 ++#define USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR 0x0027 ++ ++#define USB_VENDOR_ID_CHIC 0x05fe ++#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 ++ ++#define USB_VENDOR_ID_CHICONY 0x04f2 ++#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 ++#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d ++#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618 ++#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123 ++#define USB_DEVICE_ID_CHICONY_AK1D 0x1125 ++ ++#define USB_VENDOR_ID_CHUNGHWAT 0x2247 ++#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001 ++ ++#define USB_VENDOR_ID_CIDC 0x1677 ++ ++#define USB_VENDOR_ID_CMEDIA 0x0d8c ++#define USB_DEVICE_ID_CM109 0x000e ++ ++#define USB_VENDOR_ID_CODEMERCS 0x07c0 ++#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 ++#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff ++ ++#define USB_VENDOR_ID_CREATIVELABS 0x041e ++#define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801 ++ ++#define USB_VENDOR_ID_CVTOUCH 0x1ff7 ++#define USB_DEVICE_ID_CVTOUCH_SCREEN 0x0013 ++ ++#define USB_VENDOR_ID_CYGNAL 0x10c4 ++#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a ++#define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH 0x81b9 ++ ++#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244 ++ ++#define USB_VENDOR_ID_CYPRESS 0x04b4 ++#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 ++#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 ++#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 ++#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 ++#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 ++#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1 ++#define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81 ++#define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001 ++ ++#define USB_VENDOR_ID_DATA_MODUL 0x7374 ++#define USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH 0x1201 ++ ++#define USB_VENDOR_ID_DEALEXTREAME 0x10c5 ++#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a ++ ++#define USB_VENDOR_ID_DELORME 0x1163 ++#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 ++#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 ++ ++#define USB_VENDOR_ID_DMI 0x0c0b ++#define USB_DEVICE_ID_DMI_ENC 0x5fab ++ ++#define USB_VENDOR_ID_DRAGONRISE 0x0079 ++ ++#define USB_VENDOR_ID_DWAV 0x0eef ++#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 ++#define USB_DEVICE_ID_DWAV_TOUCHCONTROLLER 0x0002 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207 0x7207 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A 0x722A ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E 0x725e ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262 0x7262 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 ++#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 ++ ++#define USB_VENDOR_ID_ELAN 0x04f3 ++#define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089 ++#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B 0x009b ++#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F 0x016f ++ ++#define USB_VENDOR_ID_ELECOM 0x056e ++#define USB_DEVICE_ID_ELECOM_BM084 0x0061 ++ ++#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 ++ ++#define USB_VENDOR_ID_ELO 0x04E7 ++#define USB_DEVICE_ID_ELO_TS2515 0x0022 ++#define USB_DEVICE_ID_ELO_TS2700 0x0020 ++ ++#define USB_VENDOR_ID_EMS 0x2006 ++#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 ++ ++#define USB_VENDOR_ID_FLATFROG 0x25b5 ++#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002 ++ ++#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f ++#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 ++ ++#define USB_VENDOR_ID_ETT 0x0664 ++#define USB_DEVICE_ID_TC5UH 0x0309 ++#define USB_DEVICE_ID_TC4UM 0x0306 ++ ++#define USB_VENDOR_ID_ETURBOTOUCH 0x22b9 ++#define USB_DEVICE_ID_ETURBOTOUCH 0x0006 ++ ++#define USB_VENDOR_ID_EZKEY 0x0518 ++#define USB_DEVICE_ID_BTC_8193 0x0002 ++ ++#define USB_VENDOR_ID_FORMOSA 0x147a ++#define USB_DEVICE_ID_FORMOSA_IR_RECEIVER 0xe03e ++ ++#define USB_VENDOR_ID_FREESCALE 0x15A2 ++#define USB_DEVICE_ID_FREESCALE_MX28 0x004F ++ ++#define USB_VENDOR_ID_FRUCTEL 0x25B6 ++#define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002 ++ ++#define USB_VENDOR_ID_GAMERON 0x0810 ++#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001 ++#define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002 ++ ++#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc ++#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003 ++#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100 ++#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101 0x0101 ++#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102 0x0102 ++#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106 0x0106 ++#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a ++#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100 ++ ++#define USB_VENDOR_ID_GLAB 0x06c2 ++#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 ++#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 ++#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 ++#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044 ++#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045 ++#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051 ++#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 ++#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 ++ ++#define USB_VENDOR_ID_GOODTOUCH 0x1aad ++#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f ++ ++#define USB_VENDOR_ID_GOTOP 0x08f2 ++#define USB_DEVICE_ID_SUPER_Q2 0x007f ++#define USB_DEVICE_ID_GOGOPEN 0x00ce ++#define USB_DEVICE_ID_PENPOWER 0x00f4 ++ ++#define USB_VENDOR_ID_GREENASIA 0x0e8f ++#define USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD 0x3013 ++ ++#define USB_VENDOR_ID_GRETAGMACBETH 0x0971 ++#define USB_DEVICE_ID_GRETAGMACBETH_HUEY 0x2005 ++ ++#define USB_VENDOR_ID_GRIFFIN 0x077d ++#define USB_DEVICE_ID_POWERMATE 0x0410 ++#define USB_DEVICE_ID_SOUNDKNOB 0x04AA ++#define USB_DEVICE_ID_RADIOSHARK 0x627a ++ ++#define USB_VENDOR_ID_GTCO 0x078c ++#define USB_DEVICE_ID_GTCO_90 0x0090 ++#define USB_DEVICE_ID_GTCO_100 0x0100 ++#define USB_DEVICE_ID_GTCO_101 0x0101 ++#define USB_DEVICE_ID_GTCO_103 0x0103 ++#define USB_DEVICE_ID_GTCO_104 0x0104 ++#define USB_DEVICE_ID_GTCO_105 0x0105 ++#define USB_DEVICE_ID_GTCO_106 0x0106 ++#define USB_DEVICE_ID_GTCO_107 0x0107 ++#define USB_DEVICE_ID_GTCO_108 0x0108 ++#define USB_DEVICE_ID_GTCO_200 0x0200 ++#define USB_DEVICE_ID_GTCO_201 0x0201 ++#define USB_DEVICE_ID_GTCO_202 0x0202 ++#define USB_DEVICE_ID_GTCO_203 0x0203 ++#define USB_DEVICE_ID_GTCO_204 0x0204 ++#define USB_DEVICE_ID_GTCO_205 0x0205 ++#define USB_DEVICE_ID_GTCO_206 0x0206 ++#define USB_DEVICE_ID_GTCO_207 0x0207 ++#define USB_DEVICE_ID_GTCO_300 0x0300 ++#define USB_DEVICE_ID_GTCO_301 0x0301 ++#define USB_DEVICE_ID_GTCO_302 0x0302 ++#define USB_DEVICE_ID_GTCO_303 0x0303 ++#define USB_DEVICE_ID_GTCO_304 0x0304 ++#define USB_DEVICE_ID_GTCO_305 0x0305 ++#define USB_DEVICE_ID_GTCO_306 0x0306 ++#define USB_DEVICE_ID_GTCO_307 0x0307 ++#define USB_DEVICE_ID_GTCO_308 0x0308 ++#define USB_DEVICE_ID_GTCO_309 0x0309 ++#define USB_DEVICE_ID_GTCO_400 0x0400 ++#define USB_DEVICE_ID_GTCO_401 0x0401 ++#define USB_DEVICE_ID_GTCO_402 0x0402 ++#define USB_DEVICE_ID_GTCO_403 0x0403 ++#define USB_DEVICE_ID_GTCO_404 0x0404 ++#define USB_DEVICE_ID_GTCO_405 0x0405 ++#define USB_DEVICE_ID_GTCO_500 0x0500 ++#define USB_DEVICE_ID_GTCO_501 0x0501 ++#define USB_DEVICE_ID_GTCO_502 0x0502 ++#define USB_DEVICE_ID_GTCO_503 0x0503 ++#define USB_DEVICE_ID_GTCO_504 0x0504 ++#define USB_DEVICE_ID_GTCO_1000 0x1000 ++#define USB_DEVICE_ID_GTCO_1001 0x1001 ++#define USB_DEVICE_ID_GTCO_1002 0x1002 ++#define USB_DEVICE_ID_GTCO_1003 0x1003 ++#define USB_DEVICE_ID_GTCO_1004 0x1004 ++#define USB_DEVICE_ID_GTCO_1005 0x1005 ++#define USB_DEVICE_ID_GTCO_1006 0x1006 ++#define USB_DEVICE_ID_GTCO_1007 0x1007 ++ ++#define USB_VENDOR_ID_GYRATION 0x0c16 ++#define USB_DEVICE_ID_GYRATION_REMOTE 0x0002 ++#define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003 ++#define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008 ++ ++#define USB_VENDOR_ID_HANWANG 0x0b57 ++#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000 ++#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff ++ ++#define USB_VENDOR_ID_HANVON 0x20b3 ++#define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18 ++ ++#define USB_VENDOR_ID_HANVON_ALT 0x22ed ++#define USB_DEVICE_ID_HANVON_ALT_MULTITOUCH 0x1010 ++ ++#define USB_VENDOR_ID_HAPP 0x078b ++#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 ++#define USB_DEVICE_ID_UGCI_FLYING 0x0020 ++#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 ++ ++#define USB_VENDOR_ID_HUION 0x256c ++#define USB_DEVICE_ID_HUION_580 0x006e ++ ++#define USB_VENDOR_ID_IDEACOM 0x1cb6 ++#define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 ++#define USB_DEVICE_ID_IDEACOM_IDC6651 0x6651 ++ ++#define USB_VENDOR_ID_ILITEK 0x222a ++#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001 ++ ++#define USB_VENDOR_ID_INTEL_0 0x8086 ++#define USB_VENDOR_ID_INTEL_1 0x8087 ++#define USB_DEVICE_ID_INTEL_HID_SENSOR 0x09fa ++ ++#define USB_VENDOR_ID_STM_0 0x0483 ++#define USB_DEVICE_ID_STM_HID_SENSOR 0x91d1 ++ ++#define USB_VENDOR_ID_ION 0x15e4 ++#define USB_DEVICE_ID_ICADE 0x0132 ++ ++#define USB_VENDOR_ID_HOLTEK 0x1241 ++#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015 ++ ++#define USB_VENDOR_ID_HOLTEK_ALT 0x04d9 ++#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055 ++#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A 0xa04a ++#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067 0xa067 ++#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 ++#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 ++#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 ++ ++#define USB_VENDOR_ID_IMATION 0x0718 ++#define USB_DEVICE_ID_DISC_STAKKA 0xd000 ++ ++#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 ++#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 ++ ++#define USB_VENDOR_ID_JABRA 0x0b0e ++#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 ++#define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 ++ ++#define USB_VENDOR_ID_JESS 0x0c45 ++#define USB_DEVICE_ID_JESS_YUREX 0x1010 ++ ++#define USB_VENDOR_ID_JESS2 0x0f30 ++#define USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD 0x0111 ++ ++#define USB_VENDOR_ID_KBGEAR 0x084e ++#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 ++ ++#define USB_VENDOR_ID_KENSINGTON 0x047d ++#define USB_DEVICE_ID_KS_SLIMBLADE 0x2041 ++ ++#define USB_VENDOR_ID_KWORLD 0x1b80 ++#define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700 ++ ++#define USB_VENDOR_ID_KEYTOUCH 0x0926 ++#define USB_DEVICE_ID_KEYTOUCH_IEC 0x3333 ++ ++#define USB_VENDOR_ID_KYE 0x0458 ++#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 ++#define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE 0x0138 ++#define USB_DEVICE_ID_GENIUS_MANTICORE 0x0153 ++#define USB_DEVICE_ID_GENIUS_GX_IMPERATOR 0x4018 ++#define USB_DEVICE_ID_KYE_GPEN_560 0x5003 ++#define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 ++#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 ++#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a ++#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 ++ ++#define USB_VENDOR_ID_LABTEC 0x1020 ++#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 ++ ++#define USB_VENDOR_ID_LCPOWER 0x1241 ++#define USB_DEVICE_ID_LCPOWER_LC1000 0xf767 ++ ++#define USB_VENDOR_ID_LD 0x0f11 ++#define USB_DEVICE_ID_LD_CASSY 0x1000 ++#define USB_DEVICE_ID_LD_CASSY2 0x1001 ++#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 ++#define USB_DEVICE_ID_LD_POCKETCASSY2 0x1011 ++#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 ++#define USB_DEVICE_ID_LD_MOBILECASSY2 0x1021 ++#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE 0x1031 ++#define USB_DEVICE_ID_LD_MICROCASSYCURRENT 0x1032 ++#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 ++#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 ++#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 ++#define USB_DEVICE_ID_LD_JWM 0x1080 ++#define USB_DEVICE_ID_LD_DMMP 0x1081 ++#define USB_DEVICE_ID_LD_UMIP 0x1090 ++#define USB_DEVICE_ID_LD_UMIC 0x10A0 ++#define USB_DEVICE_ID_LD_UMIB 0x10B0 ++#define USB_DEVICE_ID_LD_XRAY 0x1100 ++#define USB_DEVICE_ID_LD_XRAY2 0x1101 ++#define USB_DEVICE_ID_LD_XRAYCT 0x1110 ++#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 ++#define USB_DEVICE_ID_LD_MOTOR 0x1210 ++#define USB_DEVICE_ID_LD_COM3LAB 0x2000 ++#define USB_DEVICE_ID_LD_TELEPORT 0x2010 ++#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 ++#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 ++#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 ++#define USB_DEVICE_ID_LD_MOSTANALYSER 0x2050 ++#define USB_DEVICE_ID_LD_MOSTANALYSER2 0x2051 ++#define USB_DEVICE_ID_LD_ABSESP 0x2060 ++#define USB_DEVICE_ID_LD_AUTODATABUS 0x2070 ++#define USB_DEVICE_ID_LD_MCT 0x2080 ++#define USB_DEVICE_ID_LD_HYBRID 0x2090 ++#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 ++ ++#define USB_VENDOR_ID_LENOVO 0x17ef ++#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 ++ ++#define USB_VENDOR_ID_LG 0x1fd2 ++#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 ++ ++#define USB_VENDOR_ID_LOGITECH 0x046d ++#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e ++#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 ++#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 ++#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f ++#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306 ++#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a ++#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 ++#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 ++#define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216 ++#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 ++#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 ++#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 ++#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 ++#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 ++#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 ++#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 ++#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 ++#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298 ++#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 ++#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a ++#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b ++#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c ++#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a ++#define USB_DEVICE_ID_S510_RECEIVER 0xc50c ++#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 ++#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 ++#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 ++#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b ++#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532 ++#define USB_DEVICE_ID_SPACETRAVELLER 0xc623 ++#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 ++#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 ++#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 ++#define USB_DEVICE_ID_DINOVO_MINI 0xc71f ++#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03 ++#define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL 0xca04 ++ ++#define USB_VENDOR_ID_LUMIO 0x202e ++#define USB_DEVICE_ID_CRYSTALTOUCH 0x0006 ++#define USB_DEVICE_ID_CRYSTALTOUCH_DUAL 0x0007 ++ ++#define USB_VENDOR_ID_MADCATZ 0x0738 ++#define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 ++ ++#define USB_VENDOR_ID_MCC 0x09db ++#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 ++#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a ++ ++#define USB_VENDOR_ID_MGE 0x0463 ++#define USB_DEVICE_ID_MGE_UPS 0xffff ++#define USB_DEVICE_ID_MGE_UPS1 0x0001 ++ ++#define USB_VENDOR_ID_MICROCHIP 0x04d8 ++#define USB_DEVICE_ID_PICKIT1 0x0032 ++#define USB_DEVICE_ID_PICKIT2 0x0033 ++#define USB_DEVICE_ID_PICOLCD 0xc002 ++#define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002 ++ ++#define USB_VENDOR_ID_MICROSOFT 0x045e ++#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b ++#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d ++#define USB_DEVICE_ID_MS_NE4K 0x00db ++#define USB_DEVICE_ID_MS_NE4K_JP 0x00dc ++#define USB_DEVICE_ID_MS_LK6K 0x00f9 ++#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 ++#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 ++#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730 ++#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c ++ ++#define USB_VENDOR_ID_MOJO 0x8282 ++#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 ++ ++#define USB_VENDOR_ID_MONTEREY 0x0566 ++#define USB_DEVICE_ID_GENIUS_KB29E 0x3004 ++ ++#define USB_VENDOR_ID_MSI 0x1770 ++#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00 ++ ++#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 ++#define USB_DEVICE_ID_N_S_HARMONY 0xc359 ++ ++#define USB_VENDOR_ID_NATSU 0x08b7 ++#define USB_DEVICE_ID_NATSU_GAMEPAD 0x0001 ++ ++#define USB_VENDOR_ID_NCR 0x0404 ++#define USB_DEVICE_ID_NCR_FIRST 0x0300 ++#define USB_DEVICE_ID_NCR_LAST 0x03ff ++ ++#define USB_VENDOR_ID_NEC 0x073e ++#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 ++ ++#define USB_VENDOR_ID_NEXIO 0x1870 ++#define USB_DEVICE_ID_NEXIO_MULTITOUCH_420 0x010d ++#define USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750 0x0110 ++ ++#define USB_VENDOR_ID_NEXTWINDOW 0x1926 ++#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003 ++ ++#define USB_VENDOR_ID_NINTENDO 0x057e ++#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306 ++#define USB_DEVICE_ID_NINTENDO_WIIMOTE2 0x0330 ++ ++#define USB_VENDOR_ID_NOVATEK 0x0603 ++#define USB_DEVICE_ID_NOVATEK_PCT 0x0600 ++#define USB_DEVICE_ID_NOVATEK_MOUSE 0x1602 ++ ++#define USB_VENDOR_ID_NTRIG 0x1b96 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2 0x0004 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_3 0x0005 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_4 0x0006 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_5 0x0007 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_6 0x0008 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_7 0x0009 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_8 0x000A ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_9 0x000B ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_10 0x000C ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_11 0x000D ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_12 0x000E ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_13 0x000F ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_14 0x0010 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_15 0x0011 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013 ++#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014 ++#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500 ++ ++#define USB_VENDOR_ID_ONTRAK 0x0a07 ++#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 ++ ++#define USB_VENDOR_ID_ORTEK 0x05a4 ++#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 ++#define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 ++ ++#define USB_VENDOR_ID_OUYA 0x2836 ++#define USB_DEVICE_ID_OUYA_CONTROLLER 0x0001 ++ ++#define USB_VENDOR_ID_PANASONIC 0x04da ++#define USB_DEVICE_ID_PANABOARD_UBT780 0x1044 ++#define USB_DEVICE_ID_PANABOARD_UBT880 0x104d ++ ++#define USB_VENDOR_ID_PANJIT 0x134c ++ ++#define USB_VENDOR_ID_PANTHERLORD 0x0810 ++#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 ++ ++#define USB_VENDOR_ID_PENMOUNT 0x14e1 ++#define USB_DEVICE_ID_PENMOUNT_PCI 0x3500 ++ ++#define USB_VENDOR_ID_PETALYNX 0x18b1 ++#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 ++ ++#define USB_VENDOR_ID_PHILIPS 0x0471 ++#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617 ++#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1 0x206c ++#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2 0x20cc ++#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3 0x0613 ++ ++#define USB_VENDOR_ID_PI_ENGINEERING 0x05f3 ++#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff ++ ++#define USB_VENDOR_ID_PIXART 0x093a ++#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN 0x8001 ++#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1 0x8002 ++#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2 0x8003 ++ ++#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 ++#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 ++ ++#define USB_VENDOR_ID_POWERCOM 0x0d9f ++#define USB_DEVICE_ID_POWERCOM_UPS 0x0002 ++ ++#define USB_VENDOR_ID_PRODIGE 0x05af ++#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 ++ ++#define USB_VENDOR_ID_QUANTA 0x0408 ++#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 ++#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 ++#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 ++ ++#define USB_VENDOR_ID_REALTEK 0x0bda ++#define USB_DEVICE_ID_REALTEK_READER 0x0152 ++ ++#define USB_VENDOR_ID_ROCCAT 0x1e7d ++#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 ++#define USB_DEVICE_ID_ROCCAT_ISKU 0x319c ++#define USB_DEVICE_ID_ROCCAT_ISKUFX 0x3264 ++#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced ++#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 ++#define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe ++#define USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL 0x2db4 ++#define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 ++#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 ++#define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e ++#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 ++#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 ++#define USB_DEVICE_ID_ROCCAT_RYOS_MK 0x3138 ++#define USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW 0x31ce ++#define USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO 0x3232 ++#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a ++ ++#define USB_VENDOR_ID_SAITEK 0x06a3 ++#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 ++#define USB_DEVICE_ID_SAITEK_PS1000 0x0621 ++ ++#define USB_VENDOR_ID_SAMSUNG 0x0419 ++#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 ++#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600 ++ ++#define USB_VENDOR_ID_SENNHEISER 0x1395 ++#define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c ++ ++#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f ++#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002 ++ ++#define USB_VENDOR_ID_SIGMATEL 0x066F ++#define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780 ++ ++#define USB_VENDOR_ID_SIS_TOUCH 0x0457 ++#define USB_DEVICE_ID_SIS9200_TOUCH 0x9200 ++#define USB_DEVICE_ID_SIS817_TOUCH 0x0817 ++#define USB_DEVICE_ID_SIS_TS 0x1013 ++#define USB_DEVICE_ID_SIS1030_TOUCH 0x1030 ++ ++#define USB_VENDOR_ID_SKYCABLE 0x1223 ++#define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 ++ ++#define USB_VENDOR_ID_SONY 0x054c ++#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b ++#define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE 0x0374 ++#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 ++#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 ++#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4 ++#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f ++#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 ++#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000 ++ ++#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 ++#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 ++#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046 ++ ++#define USB_VENDOR_ID_STANTUM 0x1f87 ++#define USB_DEVICE_ID_MTP 0x0002 ++ ++#define USB_VENDOR_ID_STANTUM_STM 0x0483 ++#define USB_DEVICE_ID_MTP_STM 0x3261 ++ ++#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403 ++#define USB_DEVICE_ID_MTP_SITRONIX 0x5001 ++ ++#define USB_VENDOR_ID_STEELSERIES 0x1038 ++#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410 ++ ++#define USB_VENDOR_ID_SUN 0x0430 ++#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab ++ ++#define USB_VENDOR_ID_SUNPLUS 0x04fc ++#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 ++ ++#define USB_VENDOR_ID_SYMBOL 0x05e0 ++#define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800 ++#define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300 ++ ++#define USB_VENDOR_ID_SYNAPTICS 0x06cb ++#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 ++#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 ++#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 ++#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 ++#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 ++#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 ++#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 ++#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 ++#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 ++#define USB_DEVICE_ID_SYNAPTICS_LTS1 0x0af8 ++#define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10 ++#define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 ++#define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 ++#define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 ++ ++#define USB_VENDOR_ID_THINGM 0x27b8 ++#define USB_DEVICE_ID_BLINK1 0x01ed ++ ++#define USB_VENDOR_ID_THRUSTMASTER 0x044f ++ ++#define USB_VENDOR_ID_TIVO 0x150a ++#define USB_DEVICE_ID_TIVO_SLIDE_BT 0x1200 ++#define USB_DEVICE_ID_TIVO_SLIDE 0x1201 ++#define USB_DEVICE_ID_TIVO_SLIDE_PRO 0x1203 ++ ++#define USB_VENDOR_ID_TOPSEED 0x0766 ++#define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 ++ ++#define USB_VENDOR_ID_TOPSEED2 0x1784 ++#define USB_DEVICE_ID_TOPSEED2_RF_COMBO 0x0004 ++#define USB_DEVICE_ID_TOPSEED2_PERIPAD_701 0x0016 ++ ++#define USB_VENDOR_ID_TOPMAX 0x0663 ++#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 ++ ++#define USB_VENDOR_ID_TOUCH_INTL 0x1e5e ++#define USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH 0x0313 ++ ++#define USB_VENDOR_ID_TOUCHPACK 0x1bfd ++#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688 ++ ++#define USB_VENDOR_ID_TPV 0x25aa ++#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN 0x8883 ++ ++#define USB_VENDOR_ID_TURBOX 0x062a ++#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 ++#define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100 ++ ++#define USB_VENDOR_ID_TWINHAN 0x6253 ++#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100 ++ ++#define USB_VENDOR_ID_UCLOGIC 0x5543 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_KNA5 0x6001 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_TWA60 0x0064 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 ++#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 ++#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 ++ ++#define USB_VENDOR_ID_UNITEC 0x227d ++#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 ++#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19 0x0a19 ++ ++#define USB_VENDOR_ID_VELLEMAN 0x10cf ++#define USB_DEVICE_ID_VELLEMAN_K8055_FIRST 0x5500 ++#define USB_DEVICE_ID_VELLEMAN_K8055_LAST 0x5503 ++#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061 ++#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068 ++ ++#define USB_VENDOR_ID_VERNIER 0x08f7 ++#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 ++#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 ++#define USB_DEVICE_ID_VERNIER_SKIP 0x0003 ++#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 ++#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006 ++ ++#define USB_VENDOR_ID_WACOM 0x056a ++#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 ++#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD ++ ++#define USB_VENDOR_ID_WALTOP 0x172f ++#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH 0x0032 ++#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH 0x0034 ++#define USB_DEVICE_ID_WALTOP_Q_PAD 0x0037 ++#define USB_DEVICE_ID_WALTOP_PID_0038 0x0038 ++#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH 0x0501 ++#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500 ++#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502 ++ ++#define USB_VENDOR_ID_WISEGROUP 0x0925 ++#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005 ++#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 ++#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 ++#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 ++#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888 ++#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800 ++#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 ++ ++#define USB_VENDOR_ID_WISEGROUP_LTD 0x6666 ++#define USB_VENDOR_ID_WISEGROUP_LTD2 0x6677 ++#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 ++#define USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO 0x8801 ++#define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802 ++#define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804 ++ ++#define USB_VENDOR_ID_WISTRON 0x0fb8 ++#define USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH 0x1109 ++ ++#define USB_VENDOR_ID_X_TENSIONS 0x1ae7 ++#define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE 0x9001 ++ ++#define USB_VENDOR_ID_XAT 0x2505 ++#define USB_DEVICE_ID_XAT_CSR 0x0220 ++ ++#define USB_VENDOR_ID_XIN_MO 0x16c0 ++#define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE 0x05e1 ++ ++#define USB_VENDOR_ID_XIROKU 0x1477 ++#define USB_DEVICE_ID_XIROKU_SPX 0x1006 ++#define USB_DEVICE_ID_XIROKU_MPX 0x1007 ++#define USB_DEVICE_ID_XIROKU_CSR 0x100e ++#define USB_DEVICE_ID_XIROKU_SPX1 0x1021 ++#define USB_DEVICE_ID_XIROKU_CSR1 0x1022 ++#define USB_DEVICE_ID_XIROKU_MPX1 0x1023 ++#define USB_DEVICE_ID_XIROKU_SPX2 0x1024 ++#define USB_DEVICE_ID_XIROKU_CSR2 0x1025 ++#define USB_DEVICE_ID_XIROKU_MPX2 0x1026 ++ ++#define USB_VENDOR_ID_YEALINK 0x6993 ++#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 ++ ++#define USB_VENDOR_ID_ZEROPLUS 0x0c12 ++ ++#define USB_VENDOR_ID_ZYDACRON 0x13EC ++#define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006 ++ ++#define USB_VENDOR_ID_ZYTRONIC 0x14c8 ++#define USB_DEVICE_ID_ZYTRONIC_ZXY100 0x0005 ++ ++#define USB_VENDOR_ID_PRIMAX 0x0461 ++#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 ++ ++ ++#endif +diff -Nur linux-3.14.36/drivers/hid/hid-ouya.c linux-openelec/drivers/hid/hid-ouya.c +--- linux-3.14.36/drivers/hid/hid-ouya.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hid/hid-ouya.c 2015-07-24 18:03:29.964842002 -0500 +@@ -0,0 +1,260 @@ ++/* ++ * HID driver for OUYA Game Controller(s) ++ * ++ * Copyright (c) 2013 OUYA ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "hid-ids.h" ++ ++#define OUYA_TOUCHPAD_FIXUP (1 << 0) ++ ++struct ouya_sc { ++ unsigned long quirks; ++}; ++ ++/* Fixed report descriptor */ ++static __u8 ouya_rdesc_fixed[] = { ++ ++ 0x05, 0x01, /* Usage Page (Desktop), */ ++ 0x09, 0x05, /* Usage (Game Pad), */ ++ ++ 0xA1, 0x01, /* Collection (Application), */ ++ 0x85, 0x07, /* Report ID (7), */ ++ ++ 0xA1, 0x00, /* Collection (Physical), */ ++ 0x09, 0x30, /* Usage (X), */ ++ 0x09, 0x31, /* Usage (Y), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x35, 0x00, /* Physical Minimum (0), */ ++ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ ++ 0x95, 0x02, /* Report Count (2), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0xC0, /* End Collection, */ ++ ++ 0xA1, 0x00, /* Collection (Physical), */ ++ 0x09, 0x33, /* Usage (Rx), */ ++ 0x09, 0x34, /* Usage (Ry), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x35, 0x00, /* Physical Minimum (0), */ ++ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ ++ 0x95, 0x02, /* Report Count (2), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0xC0, /* End Collection, */ ++ ++ 0xA1, 0x00, /* Collection (Physical), */ ++ 0x09, 0x32, /* Usage (Z), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x35, 0x00, /* Physical Minimum (0), */ ++ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0xC0, /* End Collection, */ ++ ++ 0xA1, 0x00, /* Collection (Physical), */ ++ 0x09, 0x35, /* Usage (Rz), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x35, 0x00, /* Physical Minimum (0), */ ++ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0xC0, /* End Collection, */ ++ ++ 0x05, 0x09, /* Usage Page (Button), */ ++ 0x19, 0x01, /* Usage Minimum (01h), */ ++ 0x29, 0x10, /* Usage Maximum (10h), */ ++ 0x95, 0x10, /* Report Count (16), */ ++ 0x75, 0x01, /* Report Size (1), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ ++ /* ORIGINAL REPORT DESCRIPTOR FOR TOUCHPAD INPUT */ ++ /* 06 00 ff a1 02 09 02 15 00 26 ff 00 35 00 46 ff 00 95 03 75 08 81 02 c0 */ ++ ++ 0x06, 0x00, 0xFF, /* Usage Page (Custom), */ ++ 0x09, 0x02, /* Usage (Mouse), */ ++ 0x09, 0x01, /* Usage (Pointer), */ ++ 0xA1, 0x00, /* Collection (Physical), */ ++ 0x05, 0x09, /* Usage Page (Button), */ ++ 0x19, 0x01, /* Usage Minimum (01h), */ ++ 0x29, 0x03, /* Usage Maximum (03h), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x25, 0x01, /* Logical Maximum (1), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0x75, 0x01, /* Report Size (1), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0x75, 0x05, /* Report Size (5), */ ++ 0x81, 0x01, /* Input (Constant), */ ++ 0x05, 0x01, /* Usage Page (Desktop), */ ++ 0x09, 0x30, /* Usage (X), */ ++ 0x09, 0x31, /* Usage (Y), */ ++ 0x15, 0x81, /* Logical Minimum (-127), */ ++ 0x25, 0x7f, /* Logical Maximum (127), */ ++ 0x95, 0x02, /* Report Count (2), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x81, 0x06, /* Input (Relative), */ ++ 0xC0, /* End Collection, */ ++ ++ 0x06, 0x00, 0xFF, /* Usage Page (Custom), */ ++ 0xA1, 0x02, /* Collection (Logical), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x95, 0x07, /* Report Count (7), */ ++ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x09, 0x01, /* Usage (Pointer), */ ++ 0x91, 0x02, /* Output (Variable), */ ++ 0xC0, /* End Collection, */ ++ ++ 0xC0, /* End Collection */ ++ ++ ++ 0x06, 0x00, 0xFF, /* Usage Page (Custom), */ ++ 0x05, 0x0C, /* Usage Page (Consumer), */ ++ 0x09, 0x01, /* Usage (Consumer Control), */ ++ ++ 0xA1, 0x01, /* Collection (Application), */ ++ 0x85, 0x03, /* Report ID (3), */ ++ 0x05, 0x01, /* Usage Page (Desktop), */ ++ 0x09, 0x06, /* Usage (Keyboard), */ ++ 0xA1, 0x02, /* Collection (Logical), */ ++ 0x05, 0x06, /* Usage Page (Generic), */ ++ 0x09, 0x20, /* Usage (Battery Strgth), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x06, 0xBC, 0xFF, /* Usage Page (Custom), */ ++ ++ 0x0A, 0xAD, 0xBD, /* UNKNOWN */ ++ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x95, 0x06, /* Report Count (6), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0xC0, /* End Collection, */ ++ ++ 0xC0, /* End Collection */ ++ ++ 0x00 ++}; ++ ++static __u8 *ouya_report_fixup(struct hid_device *hdev, __u8 *rdesc, ++ unsigned int *rsize) ++{ ++ struct ouya_sc *sc = hid_get_drvdata(hdev); ++ ++ if (sc->quirks & OUYA_TOUCHPAD_FIXUP) { ++ rdesc = ouya_rdesc_fixed; ++ *rsize = sizeof(ouya_rdesc_fixed); ++ } ++ return rdesc; ++} ++ ++static int ouya_input_mapping(struct hid_device *hdev, struct hid_input *hi, ++ struct hid_field *field, struct hid_usage *usage, ++ unsigned long **bit, int *max) ++{ ++ struct ouya_sc *sc = hid_get_drvdata(hdev); ++ ++ if (!(sc->quirks & OUYA_TOUCHPAD_FIXUP)) { ++ return 0; ++ } ++ ++ if ((usage->hid & 0x90000) == 0x90000 && ++ (field->physical & 0xff000000) == 0xff000000 && ++ usage->collection_index == 5 && ++ field->report_count == 3) { ++ ++ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_MOUSE + (usage->hid - 0x90001)); ++ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int ouya_probe(struct hid_device *hdev, const struct hid_device_id *id) ++{ ++ int ret; ++ struct ouya_sc *sc; ++ ++ sc = kzalloc(sizeof(*sc), GFP_KERNEL); ++ if (sc == NULL) { ++ hid_err(hdev, "can't alloc ouya descriptor\n"); ++ return -ENOMEM; ++ } ++ ++ if(((hdev->version & 0xff00) == 0x0100 && (hdev->version & 0xff) >= 0x04) || ++ ((hdev->version & 0xff00) == 0xe100 && (hdev->version & 0xff) >= 0x3a)) { ++ hid_info(hdev, "ouya controller - new version\n"); ++ sc->quirks = OUYA_TOUCHPAD_FIXUP; ++ } else { ++ sc->quirks = 0; ++ } ++ hid_set_drvdata(hdev, sc); ++ ++ ret = hid_parse(hdev); ++ if (ret) { ++ hid_err(hdev, "parse failed\n"); ++ goto err_free; ++ } ++ ++ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ++ HID_CONNECT_HIDDEV_FORCE); ++ if (ret) { ++ hid_err(hdev, "hw start failed\n"); ++ goto err_free; ++ } ++ ++ return 0; ++ ++err_free: ++ kfree(sc); ++ return ret; ++} ++ ++static void ouya_remove(struct hid_device *hdev) ++{ ++ hid_hw_stop(hdev); ++ kfree(hid_get_drvdata(hdev)); ++} ++ ++static const struct hid_device_id ouya_devices[] = { ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_OUYA, USB_DEVICE_ID_OUYA_CONTROLLER) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(hid, ouya_devices); ++ ++static struct hid_driver ouya_driver = { ++ .name = "ouya", ++ .id_table = ouya_devices, ++ .probe = ouya_probe, ++ .remove = ouya_remove, ++ .input_mapping = ouya_input_mapping, ++ .report_fixup = ouya_report_fixup ++}; ++ ++static int __init ouya_init(void) ++{ ++ return hid_register_driver(&ouya_driver); ++} ++ ++static void __exit ouya_exit(void) ++{ ++ hid_unregister_driver(&ouya_driver); ++} ++ ++module_init(ouya_init); ++module_exit(ouya_exit); +diff -Nur linux-3.14.36/drivers/hid/hid-sony.c linux-openelec/drivers/hid/hid-sony.c +--- linux-3.14.36/drivers/hid/hid-sony.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hid/hid-sony.c 2015-07-24 18:03:30.052842002 -0500 +@@ -546,6 +546,24 @@ + return 1; + } + ++static int ps3remote_setup_repeat(struct hid_device *hdev) ++{ ++ struct hid_input *hidinput = list_first_entry(&hdev->inputs, ++ struct hid_input, list); ++ struct input_dev *input = hidinput->input; ++ ++ /* ++ * Set up autorepeat defaults per the remote control subsystem; ++ * this must be done after hid_hw_start(), as having these non-zero ++ * at the time of input_register_device() tells the input system that ++ * the hardware does the autorepeat, and the PS3 remote does not. ++ */ ++ set_bit(EV_REP, input->evbit); ++ input->rep[REP_DELAY] = 500; ++ input->rep[REP_PERIOD] = 125; ++ ++ return 0; ++} + + /* Sony Vaio VGX has wrongly mouse pointer declared as constant */ + static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, +@@ -1074,6 +1092,8 @@ + } + else if (sc->quirks & SIXAXIS_CONTROLLER_BT) + ret = sixaxis_set_operational_bt(hdev); ++ else if (sc->quirks & PS3REMOTE) ++ ret = ps3remote_setup_repeat(hdev); + else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { + /* Report 5 (31 bytes) is used to send data to the controller via USB */ + ret = sony_set_output_report(sc, 0x05, 248); +@@ -1150,6 +1170,9 @@ + .driver_data = DUALSHOCK4_CONTROLLER_USB }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + .driver_data = DUALSHOCK4_CONTROLLER_BT }, ++ /* SMK-Link Universal Remote Control VP3700 */ ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SONY_PS3_BDREMOTE), ++ .driver_data = PS3REMOTE }, + { } + }; + MODULE_DEVICE_TABLE(hid, sony_devices); +diff -Nur linux-3.14.36/drivers/hid/hid-sony.c.orig linux-openelec/drivers/hid/hid-sony.c.orig +--- linux-3.14.36/drivers/hid/hid-sony.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hid/hid-sony.c.orig 2015-07-24 18:03:30.044842002 -0500 +@@ -0,0 +1,1188 @@ ++/* ++ * HID driver for Sony / PS2 / PS3 BD devices. ++ * ++ * Copyright (c) 1999 Andreas Gal ++ * Copyright (c) 2000-2005 Vojtech Pavlik ++ * Copyright (c) 2005 Michael Haboustak for Concept2, Inc ++ * Copyright (c) 2008 Jiri Slaby ++ * Copyright (c) 2012 David Dillow ++ * Copyright (c) 2006-2013 Jiri Kosina ++ * Copyright (c) 2013 Colin Leitner ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++/* NOTE: in order for the Sony PS3 BD Remote Control to be found by ++ * a Bluetooth host, the key combination Start+Enter has to be kept pressed ++ * for about 7 seconds with the Bluetooth Host Controller in discovering mode. ++ * ++ * There will be no PIN request from the device. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hid-ids.h" ++ ++#define VAIO_RDESC_CONSTANT BIT(0) ++#define SIXAXIS_CONTROLLER_USB BIT(1) ++#define SIXAXIS_CONTROLLER_BT BIT(2) ++#define BUZZ_CONTROLLER BIT(3) ++#define PS3REMOTE BIT(4) ++#define DUALSHOCK4_CONTROLLER_USB BIT(5) ++#define DUALSHOCK4_CONTROLLER_BT BIT(6) ++ ++#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB) ++#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_USB) ++ ++#define MAX_LEDS 4 ++ ++static const u8 sixaxis_rdesc_fixup[] = { ++ 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, ++ 0x81, 0x01, 0x75, 0x10, 0x95, 0x04, 0x26, 0xFF, ++ 0x03, 0x46, 0xFF, 0x03, 0x09, 0x01, 0x81, 0x02 ++}; ++ ++static const u8 sixaxis_rdesc_fixup2[] = { ++ 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, ++ 0x85, 0x01, 0x75, 0x08, 0x95, 0x01, 0x15, 0x00, ++ 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, ++ 0x13, 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 0x45, ++ 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, ++ 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06, 0x00, 0xff, ++ 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, ++ 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75, 0x08, 0x95, ++ 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, ++ 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02, ++ 0xc0, 0x05, 0x01, 0x95, 0x13, 0x09, 0x01, 0x81, ++ 0x02, 0x95, 0x0c, 0x81, 0x01, 0x75, 0x10, 0x95, ++ 0x04, 0x26, 0xff, 0x03, 0x46, 0xff, 0x03, 0x09, ++ 0x01, 0x81, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0x02, ++ 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, ++ 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75, 0x08, 0x95, ++ 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, ++ 0x85, 0xef, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, ++ 0xb1, 0x02, 0xc0, 0xc0, ++}; ++ ++/* The default descriptor doesn't provide mapping for the accelerometers ++ * or orientation sensors. This fixed descriptor maps the accelerometers ++ * to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors ++ * to usage values 0x43, 0x44 and 0x45. ++ */ ++static u8 dualshock4_usb_rdesc[] = { ++ 0x05, 0x01, /* Usage Page (Desktop), */ ++ 0x09, 0x05, /* Usage (Gamepad), */ ++ 0xA1, 0x01, /* Collection (Application), */ ++ 0x85, 0x01, /* Report ID (1), */ ++ 0x09, 0x30, /* Usage (X), */ ++ 0x09, 0x31, /* Usage (Y), */ ++ 0x09, 0x32, /* Usage (Z), */ ++ 0x09, 0x35, /* Usage (Rz), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x95, 0x04, /* Report Count (4), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x09, 0x39, /* Usage (Hat Switch), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x25, 0x07, /* Logical Maximum (7), */ ++ 0x35, 0x00, /* Physical Minimum (0), */ ++ 0x46, 0x3B, 0x01, /* Physical Maximum (315), */ ++ 0x65, 0x14, /* Unit (Degrees), */ ++ 0x75, 0x04, /* Report Size (4), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0x81, 0x42, /* Input (Variable, Null State), */ ++ 0x65, 0x00, /* Unit, */ ++ 0x05, 0x09, /* Usage Page (Button), */ ++ 0x19, 0x01, /* Usage Minimum (01h), */ ++ 0x29, 0x0E, /* Usage Maximum (0Eh), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x25, 0x01, /* Logical Maximum (1), */ ++ 0x75, 0x01, /* Report Size (1), */ ++ 0x95, 0x0E, /* Report Count (14), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ ++ 0x09, 0x20, /* Usage (20h), */ ++ 0x75, 0x06, /* Report Size (6), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x25, 0x7F, /* Logical Maximum (127), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x05, 0x01, /* Usage Page (Desktop), */ ++ 0x09, 0x33, /* Usage (Rx), */ ++ 0x09, 0x34, /* Usage (Ry), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x95, 0x02, /* Report Count (2), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ ++ 0x09, 0x21, /* Usage (21h), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x05, 0x01, /* Usage Page (Desktop), */ ++ 0x19, 0x40, /* Usage Minimum (40h), */ ++ 0x29, 0x42, /* Usage Maximum (42h), */ ++ 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ ++ 0x26, 0x00, 0x7F, /* Logical Maximum (32767), */ ++ 0x75, 0x10, /* Report Size (16), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x19, 0x43, /* Usage Minimum (43h), */ ++ 0x29, 0x45, /* Usage Maximum (45h), */ ++ 0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */ ++ 0x26, 0x00, 0x40, /* Logical Maximum (16384), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ ++ 0x09, 0x21, /* Usage (21h), */ ++ 0x15, 0x00, /* Logical Minimum (0), */ ++ 0x25, 0xFF, /* Logical Maximum (255), */ ++ 0x75, 0x08, /* Report Size (8), */ ++ 0x95, 0x27, /* Report Count (39), */ ++ 0x81, 0x02, /* Input (Variable), */ ++ 0x85, 0x05, /* Report ID (5), */ ++ 0x09, 0x22, /* Usage (22h), */ ++ 0x95, 0x1F, /* Report Count (31), */ ++ 0x91, 0x02, /* Output (Variable), */ ++ 0x85, 0x04, /* Report ID (4), */ ++ 0x09, 0x23, /* Usage (23h), */ ++ 0x95, 0x24, /* Report Count (36), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x02, /* Report ID (2), */ ++ 0x09, 0x24, /* Usage (24h), */ ++ 0x95, 0x24, /* Report Count (36), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x08, /* Report ID (8), */ ++ 0x09, 0x25, /* Usage (25h), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x10, /* Report ID (16), */ ++ 0x09, 0x26, /* Usage (26h), */ ++ 0x95, 0x04, /* Report Count (4), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x11, /* Report ID (17), */ ++ 0x09, 0x27, /* Usage (27h), */ ++ 0x95, 0x02, /* Report Count (2), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x12, /* Report ID (18), */ ++ 0x06, 0x02, 0xFF, /* Usage Page (FF02h), */ ++ 0x09, 0x21, /* Usage (21h), */ ++ 0x95, 0x0F, /* Report Count (15), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x13, /* Report ID (19), */ ++ 0x09, 0x22, /* Usage (22h), */ ++ 0x95, 0x16, /* Report Count (22), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x14, /* Report ID (20), */ ++ 0x06, 0x05, 0xFF, /* Usage Page (FF05h), */ ++ 0x09, 0x20, /* Usage (20h), */ ++ 0x95, 0x10, /* Report Count (16), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x15, /* Report ID (21), */ ++ 0x09, 0x21, /* Usage (21h), */ ++ 0x95, 0x2C, /* Report Count (44), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x06, 0x80, 0xFF, /* Usage Page (FF80h), */ ++ 0x85, 0x80, /* Report ID (128), */ ++ 0x09, 0x20, /* Usage (20h), */ ++ 0x95, 0x06, /* Report Count (6), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x81, /* Report ID (129), */ ++ 0x09, 0x21, /* Usage (21h), */ ++ 0x95, 0x06, /* Report Count (6), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x82, /* Report ID (130), */ ++ 0x09, 0x22, /* Usage (22h), */ ++ 0x95, 0x05, /* Report Count (5), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x83, /* Report ID (131), */ ++ 0x09, 0x23, /* Usage (23h), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x84, /* Report ID (132), */ ++ 0x09, 0x24, /* Usage (24h), */ ++ 0x95, 0x04, /* Report Count (4), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x85, /* Report ID (133), */ ++ 0x09, 0x25, /* Usage (25h), */ ++ 0x95, 0x06, /* Report Count (6), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x86, /* Report ID (134), */ ++ 0x09, 0x26, /* Usage (26h), */ ++ 0x95, 0x06, /* Report Count (6), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x87, /* Report ID (135), */ ++ 0x09, 0x27, /* Usage (27h), */ ++ 0x95, 0x23, /* Report Count (35), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x88, /* Report ID (136), */ ++ 0x09, 0x28, /* Usage (28h), */ ++ 0x95, 0x22, /* Report Count (34), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x89, /* Report ID (137), */ ++ 0x09, 0x29, /* Usage (29h), */ ++ 0x95, 0x02, /* Report Count (2), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x90, /* Report ID (144), */ ++ 0x09, 0x30, /* Usage (30h), */ ++ 0x95, 0x05, /* Report Count (5), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x91, /* Report ID (145), */ ++ 0x09, 0x31, /* Usage (31h), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x92, /* Report ID (146), */ ++ 0x09, 0x32, /* Usage (32h), */ ++ 0x95, 0x03, /* Report Count (3), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0x93, /* Report ID (147), */ ++ 0x09, 0x33, /* Usage (33h), */ ++ 0x95, 0x0C, /* Report Count (12), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA0, /* Report ID (160), */ ++ 0x09, 0x40, /* Usage (40h), */ ++ 0x95, 0x06, /* Report Count (6), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA1, /* Report ID (161), */ ++ 0x09, 0x41, /* Usage (41h), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA2, /* Report ID (162), */ ++ 0x09, 0x42, /* Usage (42h), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA3, /* Report ID (163), */ ++ 0x09, 0x43, /* Usage (43h), */ ++ 0x95, 0x30, /* Report Count (48), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA4, /* Report ID (164), */ ++ 0x09, 0x44, /* Usage (44h), */ ++ 0x95, 0x0D, /* Report Count (13), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA5, /* Report ID (165), */ ++ 0x09, 0x45, /* Usage (45h), */ ++ 0x95, 0x15, /* Report Count (21), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA6, /* Report ID (166), */ ++ 0x09, 0x46, /* Usage (46h), */ ++ 0x95, 0x15, /* Report Count (21), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xF0, /* Report ID (240), */ ++ 0x09, 0x47, /* Usage (47h), */ ++ 0x95, 0x3F, /* Report Count (63), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xF1, /* Report ID (241), */ ++ 0x09, 0x48, /* Usage (48h), */ ++ 0x95, 0x3F, /* Report Count (63), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xF2, /* Report ID (242), */ ++ 0x09, 0x49, /* Usage (49h), */ ++ 0x95, 0x0F, /* Report Count (15), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA7, /* Report ID (167), */ ++ 0x09, 0x4A, /* Usage (4Ah), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA8, /* Report ID (168), */ ++ 0x09, 0x4B, /* Usage (4Bh), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xA9, /* Report ID (169), */ ++ 0x09, 0x4C, /* Usage (4Ch), */ ++ 0x95, 0x08, /* Report Count (8), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xAA, /* Report ID (170), */ ++ 0x09, 0x4E, /* Usage (4Eh), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xAB, /* Report ID (171), */ ++ 0x09, 0x4F, /* Usage (4Fh), */ ++ 0x95, 0x39, /* Report Count (57), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xAC, /* Report ID (172), */ ++ 0x09, 0x50, /* Usage (50h), */ ++ 0x95, 0x39, /* Report Count (57), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xAD, /* Report ID (173), */ ++ 0x09, 0x51, /* Usage (51h), */ ++ 0x95, 0x0B, /* Report Count (11), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xAE, /* Report ID (174), */ ++ 0x09, 0x52, /* Usage (52h), */ ++ 0x95, 0x01, /* Report Count (1), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xAF, /* Report ID (175), */ ++ 0x09, 0x53, /* Usage (53h), */ ++ 0x95, 0x02, /* Report Count (2), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0x85, 0xB0, /* Report ID (176), */ ++ 0x09, 0x54, /* Usage (54h), */ ++ 0x95, 0x3F, /* Report Count (63), */ ++ 0xB1, 0x02, /* Feature (Variable), */ ++ 0xC0 /* End Collection */ ++}; ++ ++static __u8 ps3remote_rdesc[] = { ++ 0x05, 0x01, /* GUsagePage Generic Desktop */ ++ 0x09, 0x05, /* LUsage 0x05 [Game Pad] */ ++ 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */ ++ ++ /* Use collection 1 for joypad buttons */ ++ 0xA1, 0x02, /* MCollection Logical (interrelated data) */ ++ ++ /* Ignore the 1st byte, maybe it is used for a controller ++ * number but it's not needed for correct operation */ ++ 0x75, 0x08, /* GReportSize 0x08 [8] */ ++ 0x95, 0x01, /* GReportCount 0x01 [1] */ ++ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ ++ ++ /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these ++ * buttons multiple keypresses are allowed */ ++ 0x05, 0x09, /* GUsagePage Button */ ++ 0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */ ++ 0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */ ++ 0x14, /* GLogicalMinimum [0] */ ++ 0x25, 0x01, /* GLogicalMaximum 0x01 [1] */ ++ 0x75, 0x01, /* GReportSize 0x01 [1] */ ++ 0x95, 0x18, /* GReportCount 0x18 [24] */ ++ 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ ++ ++ 0xC0, /* MEndCollection */ ++ ++ /* Use collection 2 for remote control buttons */ ++ 0xA1, 0x02, /* MCollection Logical (interrelated data) */ ++ ++ /* 5th byte is used for remote control buttons */ ++ 0x05, 0x09, /* GUsagePage Button */ ++ 0x18, /* LUsageMinimum [No button pressed] */ ++ 0x29, 0xFE, /* LUsageMaximum 0xFE [Button 254] */ ++ 0x14, /* GLogicalMinimum [0] */ ++ 0x26, 0xFE, 0x00, /* GLogicalMaximum 0x00FE [254] */ ++ 0x75, 0x08, /* GReportSize 0x08 [8] */ ++ 0x95, 0x01, /* GReportCount 0x01 [1] */ ++ 0x80, /* MInput */ ++ ++ /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at ++ * 0xff and 11th is for press indication */ ++ 0x75, 0x08, /* GReportSize 0x08 [8] */ ++ 0x95, 0x06, /* GReportCount 0x06 [6] */ ++ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ ++ ++ /* 12th byte is for battery strength */ ++ 0x05, 0x06, /* GUsagePage Generic Device Controls */ ++ 0x09, 0x20, /* LUsage 0x20 [Battery Strength] */ ++ 0x14, /* GLogicalMinimum [0] */ ++ 0x25, 0x05, /* GLogicalMaximum 0x05 [5] */ ++ 0x75, 0x08, /* GReportSize 0x08 [8] */ ++ 0x95, 0x01, /* GReportCount 0x01 [1] */ ++ 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ ++ ++ 0xC0, /* MEndCollection */ ++ ++ 0xC0 /* MEndCollection [Game Pad] */ ++}; ++ ++static const unsigned int ps3remote_keymap_joypad_buttons[] = { ++ [0x01] = KEY_SELECT, ++ [0x02] = BTN_THUMBL, /* L3 */ ++ [0x03] = BTN_THUMBR, /* R3 */ ++ [0x04] = BTN_START, ++ [0x05] = KEY_UP, ++ [0x06] = KEY_RIGHT, ++ [0x07] = KEY_DOWN, ++ [0x08] = KEY_LEFT, ++ [0x09] = BTN_TL2, /* L2 */ ++ [0x0a] = BTN_TR2, /* R2 */ ++ [0x0b] = BTN_TL, /* L1 */ ++ [0x0c] = BTN_TR, /* R1 */ ++ [0x0d] = KEY_OPTION, /* options/triangle */ ++ [0x0e] = KEY_BACK, /* back/circle */ ++ [0x0f] = BTN_0, /* cross */ ++ [0x10] = KEY_SCREEN, /* view/square */ ++ [0x11] = KEY_HOMEPAGE, /* PS button */ ++ [0x14] = KEY_ENTER, ++}; ++static const unsigned int ps3remote_keymap_remote_buttons[] = { ++ [0x00] = KEY_1, ++ [0x01] = KEY_2, ++ [0x02] = KEY_3, ++ [0x03] = KEY_4, ++ [0x04] = KEY_5, ++ [0x05] = KEY_6, ++ [0x06] = KEY_7, ++ [0x07] = KEY_8, ++ [0x08] = KEY_9, ++ [0x09] = KEY_0, ++ [0x0e] = KEY_ESC, /* return */ ++ [0x0f] = KEY_CLEAR, ++ [0x16] = KEY_EJECTCD, ++ [0x1a] = KEY_MENU, /* top menu */ ++ [0x28] = KEY_TIME, ++ [0x30] = KEY_PREVIOUS, ++ [0x31] = KEY_NEXT, ++ [0x32] = KEY_PLAY, ++ [0x33] = KEY_REWIND, /* scan back */ ++ [0x34] = KEY_FORWARD, /* scan forward */ ++ [0x38] = KEY_STOP, ++ [0x39] = KEY_PAUSE, ++ [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */ ++ [0x60] = KEY_FRAMEBACK, /* slow/step back */ ++ [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */ ++ [0x63] = KEY_SUBTITLE, ++ [0x64] = KEY_AUDIO, ++ [0x65] = KEY_ANGLE, ++ [0x70] = KEY_INFO, /* display */ ++ [0x80] = KEY_BLUE, ++ [0x81] = KEY_RED, ++ [0x82] = KEY_GREEN, ++ [0x83] = KEY_YELLOW, ++}; ++ ++static const unsigned int buzz_keymap[] = { ++ /* The controller has 4 remote buzzers, each with one LED and 5 ++ * buttons. ++ * ++ * We use the mapping chosen by the controller, which is: ++ * ++ * Key Offset ++ * ------------------- ++ * Buzz 1 ++ * Blue 5 ++ * Orange 4 ++ * Green 3 ++ * Yellow 2 ++ * ++ * So, for example, the orange button on the third buzzer is mapped to ++ * BTN_TRIGGER_HAPPY14 ++ */ ++ [ 1] = BTN_TRIGGER_HAPPY1, ++ [ 2] = BTN_TRIGGER_HAPPY2, ++ [ 3] = BTN_TRIGGER_HAPPY3, ++ [ 4] = BTN_TRIGGER_HAPPY4, ++ [ 5] = BTN_TRIGGER_HAPPY5, ++ [ 6] = BTN_TRIGGER_HAPPY6, ++ [ 7] = BTN_TRIGGER_HAPPY7, ++ [ 8] = BTN_TRIGGER_HAPPY8, ++ [ 9] = BTN_TRIGGER_HAPPY9, ++ [10] = BTN_TRIGGER_HAPPY10, ++ [11] = BTN_TRIGGER_HAPPY11, ++ [12] = BTN_TRIGGER_HAPPY12, ++ [13] = BTN_TRIGGER_HAPPY13, ++ [14] = BTN_TRIGGER_HAPPY14, ++ [15] = BTN_TRIGGER_HAPPY15, ++ [16] = BTN_TRIGGER_HAPPY16, ++ [17] = BTN_TRIGGER_HAPPY17, ++ [18] = BTN_TRIGGER_HAPPY18, ++ [19] = BTN_TRIGGER_HAPPY19, ++ [20] = BTN_TRIGGER_HAPPY20, ++}; ++ ++struct sony_sc { ++ struct hid_device *hdev; ++ struct led_classdev *leds[MAX_LEDS]; ++ struct hid_report *output_report; ++ unsigned long quirks; ++ struct work_struct state_worker; ++ ++#ifdef CONFIG_SONY_FF ++ __u8 left; ++ __u8 right; ++#endif ++ ++ __u8 worker_initialized; ++ __u8 led_state[MAX_LEDS]; ++ __u8 led_count; ++}; ++ ++static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, ++ unsigned int *rsize) ++{ ++ *rsize = sizeof(ps3remote_rdesc); ++ return ps3remote_rdesc; ++} ++ ++static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, ++ struct hid_field *field, struct hid_usage *usage, ++ unsigned long **bit, int *max) ++{ ++ unsigned int key = usage->hid & HID_USAGE; ++ ++ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) ++ return -1; ++ ++ switch (usage->collection_index) { ++ case 1: ++ if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons)) ++ return -1; ++ ++ key = ps3remote_keymap_joypad_buttons[key]; ++ if (!key) ++ return -1; ++ break; ++ case 2: ++ if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons)) ++ return -1; ++ ++ key = ps3remote_keymap_remote_buttons[key]; ++ if (!key) ++ return -1; ++ break; ++ default: ++ return -1; ++ } ++ ++ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); ++ return 1; ++} ++ ++static int ps3remote_setup_repeat(struct hid_device *hdev) ++{ ++ struct hid_input *hidinput = list_first_entry(&hdev->inputs, ++ struct hid_input, list); ++ struct input_dev *input = hidinput->input; ++ ++ /* ++ * Set up autorepeat defaults per the remote control subsystem; ++ * this must be done after hid_hw_start(), as having these non-zero ++ * at the time of input_register_device() tells the input system that ++ * the hardware does the autorepeat, and the PS3 remote does not. ++ */ ++ set_bit(EV_REP, input->evbit); ++ input->rep[REP_DELAY] = 500; ++ input->rep[REP_PERIOD] = 125; ++ ++ return 0; ++} ++ ++/* Sony Vaio VGX has wrongly mouse pointer declared as constant */ ++static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, ++ unsigned int *rsize) ++{ ++ struct sony_sc *sc = hid_get_drvdata(hdev); ++ ++ /* ++ * Some Sony RF receivers wrongly declare the mouse pointer as a ++ * a constant non-data variable. ++ */ ++ if ((sc->quirks & VAIO_RDESC_CONSTANT) && *rsize >= 56 && ++ /* usage page: generic desktop controls */ ++ /* rdesc[0] == 0x05 && rdesc[1] == 0x01 && */ ++ /* usage: mouse */ ++ rdesc[2] == 0x09 && rdesc[3] == 0x02 && ++ /* input (usage page for x,y axes): constant, variable, relative */ ++ rdesc[54] == 0x81 && rdesc[55] == 0x07) { ++ hid_info(hdev, "Fixing up Sony RF Receiver report descriptor\n"); ++ /* input: data, variable, relative */ ++ rdesc[55] = 0x06; ++ } ++ ++ /* ++ * The default Dualshock 4 USB descriptor doesn't assign ++ * the gyroscope values to corresponding axes so we need a ++ * modified one. ++ */ ++ if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && *rsize == 467) { ++ hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n"); ++ rdesc = dualshock4_usb_rdesc; ++ *rsize = sizeof(dualshock4_usb_rdesc); ++ } ++ ++ /* The HID descriptor exposed over BT has a trailing zero byte */ ++ if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) || ++ ((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) && ++ rdesc[83] == 0x75) { ++ hid_info(hdev, "Fixing up Sony Sixaxis report descriptor\n"); ++ memcpy((void *)&rdesc[83], (void *)&sixaxis_rdesc_fixup, ++ sizeof(sixaxis_rdesc_fixup)); ++ } else if (sc->quirks & SIXAXIS_CONTROLLER_USB && ++ *rsize > sizeof(sixaxis_rdesc_fixup2)) { ++ hid_info(hdev, "Sony Sixaxis clone detected. Using original report descriptor (size: %d clone; %d new)\n", ++ *rsize, (int)sizeof(sixaxis_rdesc_fixup2)); ++ *rsize = sizeof(sixaxis_rdesc_fixup2); ++ memcpy(rdesc, &sixaxis_rdesc_fixup2, *rsize); ++ } ++ ++ if (sc->quirks & PS3REMOTE) ++ return ps3remote_fixup(hdev, rdesc, rsize); ++ ++ return rdesc; ++} ++ ++static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, ++ __u8 *rd, int size) ++{ ++ struct sony_sc *sc = hid_get_drvdata(hdev); ++ ++ /* Sixaxis HID report has acclerometers/gyro with MSByte first, this ++ * has to be BYTE_SWAPPED before passing up to joystick interface ++ */ ++ if ((sc->quirks & (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)) && ++ rd[0] == 0x01 && size == 49) { ++ swap(rd[41], rd[42]); ++ swap(rd[43], rd[44]); ++ swap(rd[45], rd[46]); ++ swap(rd[47], rd[48]); ++ } ++ ++ return 0; ++} ++ ++static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, ++ struct hid_field *field, struct hid_usage *usage, ++ unsigned long **bit, int *max) ++{ ++ struct sony_sc *sc = hid_get_drvdata(hdev); ++ ++ if (sc->quirks & BUZZ_CONTROLLER) { ++ unsigned int key = usage->hid & HID_USAGE; ++ ++ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) ++ return -1; ++ ++ switch (usage->collection_index) { ++ case 1: ++ if (key >= ARRAY_SIZE(buzz_keymap)) ++ return -1; ++ ++ key = buzz_keymap[key]; ++ if (!key) ++ return -1; ++ break; ++ default: ++ return -1; ++ } ++ ++ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); ++ return 1; ++ } ++ ++ if (sc->quirks & PS3REMOTE) ++ return ps3remote_mapping(hdev, hi, field, usage, bit, max); ++ ++ /* Let hid-core decide for the others */ ++ return 0; ++} ++ ++/* ++ * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP ++ * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() ++ * so we need to override that forcing HID Output Reports on the Control EP. ++ * ++ * There is also another issue about HID Output Reports via USB, the Sixaxis ++ * does not want the report_id as part of the data packet, so we have to ++ * discard buf[0] when sending the actual control message, even for numbered ++ * reports, humpf! ++ */ ++static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, ++ size_t count, unsigned char report_type) ++{ ++ struct usb_interface *intf = to_usb_interface(hid->dev.parent); ++ struct usb_device *dev = interface_to_usbdev(intf); ++ struct usb_host_interface *interface = intf->cur_altsetting; ++ int report_id = buf[0]; ++ int ret; ++ ++ if (report_type == HID_OUTPUT_REPORT) { ++ /* Don't send the Report ID */ ++ buf++; ++ count--; ++ } ++ ++ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ++ HID_REQ_SET_REPORT, ++ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, ++ ((report_type + 1) << 8) | report_id, ++ interface->desc.bInterfaceNumber, buf, count, ++ USB_CTRL_SET_TIMEOUT); ++ ++ /* Count also the Report ID, in case of an Output report. */ ++ if (ret > 0 && report_type == HID_OUTPUT_REPORT) ++ ret++; ++ ++ return ret; ++} ++ ++/* ++ * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller ++ * to "operational". Without this, the ps3 controller will not report any ++ * events. ++ */ ++static int sixaxis_set_operational_usb(struct hid_device *hdev) ++{ ++ int ret; ++ char *buf = kmalloc(18, GFP_KERNEL); ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ ret = hdev->hid_get_raw_report(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT); ++ ++ if (ret < 0) ++ hid_err(hdev, "can't set operational mode\n"); ++ ++ kfree(buf); ++ ++ return ret; ++} ++ ++static int sixaxis_set_operational_bt(struct hid_device *hdev) ++{ ++ unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; ++ return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); ++} ++ ++static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds) ++{ ++ struct list_head *report_list = ++ &hdev->report_enum[HID_OUTPUT_REPORT].report_list; ++ struct hid_report *report = list_entry(report_list->next, ++ struct hid_report, list); ++ __s32 *value = report->field[0]->value; ++ ++ value[0] = 0x00; ++ value[1] = leds[0] ? 0xff : 0x00; ++ value[2] = leds[1] ? 0xff : 0x00; ++ value[3] = leds[2] ? 0xff : 0x00; ++ value[4] = leds[3] ? 0xff : 0x00; ++ value[5] = 0x00; ++ value[6] = 0x00; ++ hid_hw_request(hdev, report, HID_REQ_SET_REPORT); ++} ++ ++static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count) ++{ ++ struct sony_sc *drv_data = hid_get_drvdata(hdev); ++ int n; ++ ++ BUG_ON(count > MAX_LEDS); ++ ++ if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) { ++ buzz_set_leds(hdev, leds); ++ } else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) || ++ (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB)) { ++ for (n = 0; n < count; n++) ++ drv_data->led_state[n] = leds[n]; ++ schedule_work(&drv_data->state_worker); ++ } ++} ++ ++static void sony_led_set_brightness(struct led_classdev *led, ++ enum led_brightness value) ++{ ++ struct device *dev = led->dev->parent; ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ struct sony_sc *drv_data; ++ ++ int n; ++ ++ drv_data = hid_get_drvdata(hdev); ++ if (!drv_data) { ++ hid_err(hdev, "No device data\n"); ++ return; ++ } ++ ++ for (n = 0; n < drv_data->led_count; n++) { ++ if (led == drv_data->leds[n]) { ++ if (value != drv_data->led_state[n]) { ++ drv_data->led_state[n] = value; ++ sony_set_leds(hdev, drv_data->led_state, drv_data->led_count); ++ } ++ break; ++ } ++ } ++} ++ ++static enum led_brightness sony_led_get_brightness(struct led_classdev *led) ++{ ++ struct device *dev = led->dev->parent; ++ struct hid_device *hdev = container_of(dev, struct hid_device, dev); ++ struct sony_sc *drv_data; ++ ++ int n; ++ int on = 0; ++ ++ drv_data = hid_get_drvdata(hdev); ++ if (!drv_data) { ++ hid_err(hdev, "No device data\n"); ++ return LED_OFF; ++ } ++ ++ for (n = 0; n < drv_data->led_count; n++) { ++ if (led == drv_data->leds[n]) { ++ on = !!(drv_data->led_state[n]); ++ break; ++ } ++ } ++ ++ return on ? LED_FULL : LED_OFF; ++} ++ ++static void sony_leds_remove(struct hid_device *hdev) ++{ ++ struct sony_sc *drv_data; ++ struct led_classdev *led; ++ int n; ++ ++ drv_data = hid_get_drvdata(hdev); ++ BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT)); ++ ++ for (n = 0; n < drv_data->led_count; n++) { ++ led = drv_data->leds[n]; ++ drv_data->leds[n] = NULL; ++ if (!led) ++ continue; ++ led_classdev_unregister(led); ++ kfree(led); ++ } ++ ++ drv_data->led_count = 0; ++} ++ ++static int sony_leds_init(struct hid_device *hdev) ++{ ++ struct sony_sc *drv_data; ++ int n, ret = 0; ++ int max_brightness; ++ int use_colors; ++ struct led_classdev *led; ++ size_t name_sz; ++ char *name; ++ size_t name_len; ++ const char *name_fmt; ++ static const char * const color_str[] = { "red", "green", "blue" }; ++ static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 }; ++ ++ drv_data = hid_get_drvdata(hdev); ++ BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT)); ++ ++ if (drv_data->quirks & BUZZ_CONTROLLER) { ++ drv_data->led_count = 4; ++ max_brightness = 1; ++ use_colors = 0; ++ name_len = strlen("::buzz#"); ++ name_fmt = "%s::buzz%d"; ++ /* Validate expected report characteristics. */ ++ if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7)) ++ return -ENODEV; ++ } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) { ++ drv_data->led_count = 3; ++ max_brightness = 255; ++ use_colors = 1; ++ name_len = 0; ++ name_fmt = "%s:%s"; ++ } else { ++ drv_data->led_count = 4; ++ max_brightness = 1; ++ use_colors = 0; ++ name_len = strlen("::sony#"); ++ name_fmt = "%s::sony%d"; ++ } ++ ++ /* Clear LEDs as we have no way of reading their initial state. This is ++ * only relevant if the driver is loaded after somebody actively set the ++ * LEDs to on */ ++ sony_set_leds(hdev, initial_values, drv_data->led_count); ++ ++ name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1; ++ ++ for (n = 0; n < drv_data->led_count; n++) { ++ ++ if (use_colors) ++ name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2; ++ ++ led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); ++ if (!led) { ++ hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); ++ ret = -ENOMEM; ++ goto error_leds; ++ } ++ ++ name = (void *)(&led[1]); ++ if (use_colors) ++ snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_str[n]); ++ else ++ snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1); ++ led->name = name; ++ led->brightness = 0; ++ led->max_brightness = max_brightness; ++ led->brightness_get = sony_led_get_brightness; ++ led->brightness_set = sony_led_set_brightness; ++ ++ ret = led_classdev_register(&hdev->dev, led); ++ if (ret) { ++ hid_err(hdev, "Failed to register LED %d\n", n); ++ kfree(led); ++ goto error_leds; ++ } ++ ++ drv_data->leds[n] = led; ++ } ++ ++ return ret; ++ ++error_leds: ++ sony_leds_remove(hdev); ++ ++ return ret; ++} ++ ++static void sixaxis_state_worker(struct work_struct *work) ++{ ++ struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); ++ unsigned char buf[] = { ++ 0x01, ++ 0x00, 0xff, 0x00, 0xff, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0xff, 0x27, 0x10, 0x00, 0x32, ++ 0xff, 0x27, 0x10, 0x00, 0x32, ++ 0xff, 0x27, 0x10, 0x00, 0x32, ++ 0xff, 0x27, 0x10, 0x00, 0x32, ++ 0x00, 0x00, 0x00, 0x00, 0x00 ++ }; ++ ++#ifdef CONFIG_SONY_FF ++ buf[3] = sc->right ? 1 : 0; ++ buf[5] = sc->left; ++#endif ++ ++ buf[10] |= sc->led_state[0] << 1; ++ buf[10] |= sc->led_state[1] << 2; ++ buf[10] |= sc->led_state[2] << 3; ++ buf[10] |= sc->led_state[3] << 4; ++ ++ sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf), ++ HID_OUTPUT_REPORT); ++} ++ ++static void dualshock4_state_worker(struct work_struct *work) ++{ ++ struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); ++ struct hid_device *hdev = sc->hdev; ++ struct hid_report *report = sc->output_report; ++ __s32 *value = report->field[0]->value; ++ ++ value[0] = 0x03; ++ ++#ifdef CONFIG_SONY_FF ++ value[3] = sc->right; ++ value[4] = sc->left; ++#endif ++ ++ value[5] = sc->led_state[0]; ++ value[6] = sc->led_state[1]; ++ value[7] = sc->led_state[2]; ++ ++ hid_hw_request(hdev, report, HID_REQ_SET_REPORT); ++} ++ ++#ifdef CONFIG_SONY_FF ++static int sony_play_effect(struct input_dev *dev, void *data, ++ struct ff_effect *effect) ++{ ++ struct hid_device *hid = input_get_drvdata(dev); ++ struct sony_sc *sc = hid_get_drvdata(hid); ++ ++ if (effect->type != FF_RUMBLE) ++ return 0; ++ ++ sc->left = effect->u.rumble.strong_magnitude / 256; ++ sc->right = effect->u.rumble.weak_magnitude / 256; ++ ++ schedule_work(&sc->state_worker); ++ return 0; ++} ++ ++static int sony_init_ff(struct hid_device *hdev) ++{ ++ struct hid_input *hidinput = list_entry(hdev->inputs.next, ++ struct hid_input, list); ++ struct input_dev *input_dev = hidinput->input; ++ ++ input_set_capability(input_dev, EV_FF, FF_RUMBLE); ++ return input_ff_create_memless(input_dev, NULL, sony_play_effect); ++} ++ ++#else ++static int sony_init_ff(struct hid_device *hdev) ++{ ++ return 0; ++} ++#endif ++ ++static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size) ++{ ++ struct list_head *head, *list; ++ struct hid_report *report; ++ struct hid_device *hdev = sc->hdev; ++ ++ list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list; ++ ++ list_for_each(head, list) { ++ report = list_entry(head, struct hid_report, list); ++ ++ if (report->id == req_id) { ++ if (report->size < req_size) { ++ hid_err(hdev, "Output report 0x%02x (%i bits) is smaller than requested size (%i bits)\n", ++ req_id, report->size, req_size); ++ return -EINVAL; ++ } ++ sc->output_report = report; ++ return 0; ++ } ++ } ++ ++ hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id); ++ ++ return -EINVAL; ++} ++ ++static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ++{ ++ int ret; ++ unsigned long quirks = id->driver_data; ++ struct sony_sc *sc; ++ unsigned int connect_mask = HID_CONNECT_DEFAULT; ++ ++ sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); ++ if (sc == NULL) { ++ hid_err(hdev, "can't alloc sony descriptor\n"); ++ return -ENOMEM; ++ } ++ ++ sc->quirks = quirks; ++ hid_set_drvdata(hdev, sc); ++ sc->hdev = hdev; ++ ++ ret = hid_parse(hdev); ++ if (ret) { ++ hid_err(hdev, "parse failed\n"); ++ return ret; ++ } ++ ++ if (sc->quirks & VAIO_RDESC_CONSTANT) ++ connect_mask |= HID_CONNECT_HIDDEV_FORCE; ++ else if (sc->quirks & SIXAXIS_CONTROLLER_USB) ++ connect_mask |= HID_CONNECT_HIDDEV_FORCE; ++ else if (sc->quirks & SIXAXIS_CONTROLLER_BT) ++ connect_mask |= HID_CONNECT_HIDDEV_FORCE; ++ ++ ret = hid_hw_start(hdev, connect_mask); ++ if (ret) { ++ hid_err(hdev, "hw start failed\n"); ++ return ret; ++ } ++ ++ if (sc->quirks & SIXAXIS_CONTROLLER_USB) { ++ hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; ++ ret = sixaxis_set_operational_usb(hdev); ++ ++ sc->worker_initialized = 1; ++ INIT_WORK(&sc->state_worker, sixaxis_state_worker); ++ } ++ else if (sc->quirks & SIXAXIS_CONTROLLER_BT) ++ ret = sixaxis_set_operational_bt(hdev); ++ else if (sc->quirks & PS3REMOTE) ++ ret = ps3remote_setup_repeat(hdev); ++ else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { ++ /* Report 5 (31 bytes) is used to send data to the controller via USB */ ++ ret = sony_set_output_report(sc, 0x05, 248); ++ if (ret < 0) ++ goto err_stop; ++ ++ sc->worker_initialized = 1; ++ INIT_WORK(&sc->state_worker, dualshock4_state_worker); ++ } else { ++ ret = 0; ++ } ++ ++ if (ret < 0) ++ goto err_stop; ++ ++ if (sc->quirks & SONY_LED_SUPPORT) { ++ ret = sony_leds_init(hdev); ++ if (ret < 0) ++ goto err_stop; ++ } ++ ++ if (sc->quirks & SONY_FF_SUPPORT) { ++ ret = sony_init_ff(hdev); ++ if (ret < 0) ++ goto err_stop; ++ } ++ ++ return 0; ++err_stop: ++ if (sc->quirks & SONY_LED_SUPPORT) ++ sony_leds_remove(hdev); ++ hid_hw_stop(hdev); ++ return ret; ++} ++ ++static void sony_remove(struct hid_device *hdev) ++{ ++ struct sony_sc *sc = hid_get_drvdata(hdev); ++ ++ if (sc->quirks & SONY_LED_SUPPORT) ++ sony_leds_remove(hdev); ++ ++ if (sc->worker_initialized) ++ cancel_work_sync(&sc->state_worker); ++ ++ hid_hw_stop(hdev); ++} ++ ++static const struct hid_device_id sony_devices[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER), ++ .driver_data = SIXAXIS_CONTROLLER_USB }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER), ++ .driver_data = SIXAXIS_CONTROLLER_USB }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER), ++ .driver_data = SIXAXIS_CONTROLLER_BT }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE), ++ .driver_data = VAIO_RDESC_CONSTANT }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE), ++ .driver_data = VAIO_RDESC_CONSTANT }, ++ /* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as ++ * Logitech joystick from the device descriptor. */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER), ++ .driver_data = BUZZ_CONTROLLER }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER), ++ .driver_data = BUZZ_CONTROLLER }, ++ /* PS3 BD Remote Control */ ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE), ++ .driver_data = PS3REMOTE }, ++ /* Logitech Harmony Adapter for PS3 */ ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3), ++ .driver_data = PS3REMOTE }, ++ /* Sony Dualshock 4 controllers for PS4 */ ++ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), ++ .driver_data = DUALSHOCK4_CONTROLLER_USB }, ++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), ++ .driver_data = DUALSHOCK4_CONTROLLER_BT }, ++ { } ++}; ++MODULE_DEVICE_TABLE(hid, sony_devices); ++ ++static struct hid_driver sony_driver = { ++ .name = "sony", ++ .id_table = sony_devices, ++ .input_mapping = sony_mapping, ++ .probe = sony_probe, ++ .remove = sony_remove, ++ .report_fixup = sony_report_fixup, ++ .raw_event = sony_raw_event ++}; ++module_hid_driver(sony_driver); ++ ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/hid/hid-spinelplus.c linux-openelec/drivers/hid/hid-spinelplus.c +--- linux-3.14.36/drivers/hid/hid-spinelplus.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hid/hid-spinelplus.c 2015-07-24 18:03:29.980842002 -0500 +@@ -0,0 +1,104 @@ ++/* ++ * HID driver for "PHILIPS MCE USB IR Receiver- Spinel plus" remotes ++ * ++ * Copyright (c) 2010 Panagiotis Skintzos ++ * ++ * Renamed to Spinel, cleanup and modified to also support ++ * Spinel Plus 0471:20CC by Stephan Raue 2012. ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "hid-ids.h" ++ ++#define spinelplus_map_key(c) set_bit(EV_REP, hi->input->evbit); \ ++ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) ++ ++static int spinelplus_input_mapping(struct hid_device *hdev, ++ struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, ++ unsigned long **bit, int *max) ++{ ++ switch (usage->hid) { ++ case 0xffbc000d: spinelplus_map_key(KEY_MEDIA); break; ++ case 0xffbc0024: spinelplus_map_key(KEY_MEDIA); break; ++ case 0xffbc0027: spinelplus_map_key(KEY_ZOOM); break; ++ case 0xffbc0033: spinelplus_map_key(KEY_HOME); break; ++ case 0xffbc0035: spinelplus_map_key(KEY_CAMERA); break; ++ case 0xffbc0036: spinelplus_map_key(KEY_EPG); break; ++ case 0xffbc0037: spinelplus_map_key(KEY_DVD); break; ++ case 0xffbc0038: spinelplus_map_key(KEY_HOME); break; ++ case 0xffbc0039: spinelplus_map_key(KEY_MP3); break; ++ case 0xffbc003a: spinelplus_map_key(KEY_VIDEO); break; ++ case 0xffbc005a: spinelplus_map_key(KEY_TEXT); break; ++ case 0xffbc005b: spinelplus_map_key(KEY_RED); break; ++ case 0xffbc005c: spinelplus_map_key(KEY_GREEN); break; ++ case 0xffbc005d: spinelplus_map_key(KEY_YELLOW); break; ++ case 0xffbc005e: spinelplus_map_key(KEY_BLUE); break; ++ default: ++ return 0; ++ } ++ return 1; ++} ++ ++static int spinelplus_probe(struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ int ret; ++ /* Connect only to hid input (not hiddev & hidraw)*/ ++ unsigned int cmask = HID_CONNECT_HIDINPUT; ++ ++ ret = hid_parse(hdev); ++ if (ret) { ++ dev_err(&hdev->dev, "parse failed\n"); ++ goto err_free; ++ } ++ ++ ret = hid_hw_start(hdev, cmask); ++ if (ret) { ++ dev_err(&hdev->dev, "hw start failed\n"); ++ goto err_free; ++ } ++ ++ return 0; ++err_free: ++ return ret; ++} ++ ++static const struct hid_device_id spinelplus_devices[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS,USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS,USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS,USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(hid, spinelplus_devices); ++ ++static struct hid_driver spinelplus_driver = { ++ .name = "SpinelPlus", ++ .id_table = spinelplus_devices, ++ .input_mapping = spinelplus_input_mapping, ++ .probe = spinelplus_probe, ++}; ++ ++static int __init spinelplus_init(void) ++{ ++ return hid_register_driver(&spinelplus_driver); ++} ++ ++static void __exit spinelplus_exit(void) ++{ ++ hid_unregister_driver(&spinelplus_driver); ++} ++ ++module_init(spinelplus_init); ++module_exit(spinelplus_exit); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/hid/hid-tivo.c linux-openelec/drivers/hid/hid-tivo.c +--- linux-3.14.36/drivers/hid/hid-tivo.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hid/hid-tivo.c 2015-07-24 18:03:29.956842002 -0500 +@@ -64,6 +64,7 @@ + /* TiVo Slide Bluetooth remote, pairs with a Broadcom dongle */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) }, + { } + }; + MODULE_DEVICE_TABLE(hid, tivo_devices); +diff -Nur linux-3.14.36/drivers/hid/Kconfig linux-openelec/drivers/hid/Kconfig +--- linux-3.14.36/drivers/hid/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hid/Kconfig 2015-07-24 18:03:29.980842002 -0500 +@@ -490,6 +490,12 @@ + - Ortek WKB-2000 + - Skycable wireless presenter + ++config HID_OUYA ++ tristate "OUYA Game Controller" ++ depends on USB_HID ++ ---help--- ++ Support for OUYA Game Controller. ++ + config HID_PANTHERLORD + tristate "Pantherlord/GreenAsia game controller" + depends on HID +@@ -640,6 +646,12 @@ + ---help--- + Support for Steelseries SRW-S1 steering wheel + ++config HID_SPINELPLUS ++ tristate "Spinel Plus remote control" ++ depends on USB_HID ++ ---help--- ++ Say Y here if you have a Spinel Plus (0471:206c/20cc/0613) remote ++ + config HID_SUNPLUS + tristate "Sunplus wireless desktop" + depends on HID +diff -Nur linux-3.14.36/drivers/hid/Kconfig.orig linux-openelec/drivers/hid/Kconfig.orig +--- linux-3.14.36/drivers/hid/Kconfig.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hid/Kconfig.orig 2015-07-24 18:03:29.964842002 -0500 +@@ -0,0 +1,817 @@ ++# ++# HID driver configuration ++# ++menu "HID support" ++ depends on INPUT ++ ++config HID ++ tristate "HID bus support" ++ depends on INPUT ++ default y ++ ---help--- ++ A human interface device (HID) is a type of computer device that ++ interacts directly with and takes input from humans. The term "HID" ++ most commonly used to refer to the USB-HID specification, but other ++ devices (such as, but not strictly limited to, Bluetooth) are ++ designed using HID specification (this involves certain keyboards, ++ mice, tablets, etc). This option adds the HID bus to the kernel, ++ together with generic HID layer code. The HID devices are added and ++ removed from the HID bus by the transport-layer drivers, such as ++ usbhid (USB_HID) and hidp (BT_HIDP). ++ ++ For docs and specs, see http://www.usb.org/developers/hidpage/ ++ ++ If unsure, say Y. ++ ++if HID ++ ++config HID_BATTERY_STRENGTH ++ bool "Battery level reporting for HID devices" ++ depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY ++ default n ++ ---help--- ++ This option adds support of reporting battery strength (for HID devices ++ that support this feature) through power_supply class so that userspace ++ tools, such as upower, can display it. ++ ++config HIDRAW ++ bool "/dev/hidraw raw HID device support" ++ depends on HID ++ ---help--- ++ Say Y here if you want to support HID devices (from the USB ++ specification standpoint) that aren't strictly user interface ++ devices, like monitor controls and Uninterruptable Power Supplies. ++ ++ This module supports these devices separately using a separate ++ event interface on /dev/hidraw. ++ ++ There is also a /dev/hiddev configuration option in the USB HID ++ configuration menu. In comparison to hiddev, this device does not process ++ the hid events at all (no parsing, no lookups). This lets applications ++ to work on raw hid events when they want to, and avoid using transport-specific ++ userspace libhid/libusb libraries. ++ ++ If unsure, say Y. ++ ++config UHID ++ tristate "User-space I/O driver support for HID subsystem" ++ depends on HID ++ default n ++ ---help--- ++ Say Y here if you want to provide HID I/O Drivers from user-space. ++ This allows to write I/O drivers in user-space and feed the data from ++ the device into the kernel. The kernel parses the HID reports, loads the ++ corresponding HID Device Driver or provides input devices on top of your ++ user-space device. ++ ++ This driver cannot be used to parse HID-reports in user-space and write ++ special HID-drivers. You should use hidraw for that. ++ Instead, this driver allows to write the transport-layer driver in ++ user-space like USB-HID and Bluetooth-HID do in kernel-space. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called uhid. ++ ++config HID_GENERIC ++ tristate "Generic HID driver" ++ depends on HID ++ default HID ++ ---help--- ++ Support for generic devices on the HID bus. This includes most ++ keyboards and mice, joysticks, tablets and digitizers. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called hid-generic. ++ ++ If unsure, say Y. ++ ++menu "Special HID drivers" ++ depends on HID ++ ++config HID_A4TECH ++ tristate "A4 tech mice" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for A4 tech X5 and WOP-35 / Trust 450L mice. ++ ++config HID_ACRUX ++ tristate "ACRUX game controller support" ++ depends on HID ++ ---help--- ++ Say Y here if you want to enable support for ACRUX game controllers. ++ ++config HID_ACRUX_FF ++ bool "ACRUX force feedback support" ++ depends on HID_ACRUX ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you want to enable force feedback support for ACRUX ++ game controllers. ++ ++config HID_APPLE ++ tristate "Apple {i,Power,Mac}Books" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for some Apple devices which less or more break ++ HID specification. ++ ++ Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, ++ MacBooks, MacBook Pros and Apple Aluminum. ++ ++config HID_APPLEIR ++ tristate "Apple infrared receiver" ++ depends on (USB_HID) ++ ---help--- ++ Support for Apple infrared remote control. All the Apple computers from ++ 2005 onwards include such a port, except the unibody Macbook (2009), ++ and Mac Pros. This receiver is also used in the Apple TV set-top box ++ prior to the 2010 model. ++ ++ Say Y here if you want support for Apple infrared remote control. ++ ++config HID_AUREAL ++ tristate "Aureal" ++ depends on HID ++ ---help--- ++ Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes. ++ ++config HID_BELKIN ++ tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Belkin Flip KVM and Wireless keyboard. ++ ++config HID_CHERRY ++ tristate "Cherry Cymotion keyboard" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Cherry Cymotion keyboard. ++ ++config HID_CHICONY ++ tristate "Chicony Tactical pad" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Chicony Tactical pad. ++ ++config HID_PRODIKEYS ++ tristate "Prodikeys PC-MIDI Keyboard support" ++ depends on HID && SND ++ select SND_RAWMIDI ++ ---help--- ++ Support for Prodikeys PC-MIDI Keyboard device support. ++ Say Y here to enable support for this device. ++ - Prodikeys PC-MIDI keyboard. ++ The Prodikeys PC-MIDI acts as a USB Audio device, with one MIDI ++ input and one MIDI output. These MIDI jacks appear as ++ a sound "card" in the ALSA sound system. ++ Note: if you say N here, this device will still function as a basic ++ multimedia keyboard, but will lack support for the musical keyboard ++ and some additional multimedia keys. ++ ++config HID_CYPRESS ++ tristate "Cypress mouse and barcode readers" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for cypress mouse and barcode readers. ++ ++config HID_DRAGONRISE ++ tristate "DragonRise Inc. game controller" ++ depends on HID ++ ---help--- ++ Say Y here if you have DragonRise Inc. game controllers. ++ These might be branded as: ++ - Tesun USB-703 ++ - Media-tech MT1504 "Rogue" ++ - DVTech JS19 "Gear" ++ - Defender Game Master ++ ++config DRAGONRISE_FF ++ bool "DragonRise Inc. force feedback" ++ depends on HID_DRAGONRISE ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you want to enable force feedback support for DragonRise Inc. ++ game controllers. ++ ++config HID_EMS_FF ++ tristate "EMS Production Inc. force feedback support" ++ depends on HID ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you want to enable force feedback support for devices by ++ EMS Production Ltd. ++ Currently the following devices are known to be supported: ++ - Trio Linker Plus II ++ ++config HID_ELECOM ++ tristate "ELECOM BM084 bluetooth mouse" ++ depends on HID ++ ---help--- ++ Support for the ELECOM BM084 (bluetooth mouse). ++ ++config HID_ELO ++ tristate "ELO USB 4000/4500 touchscreen" ++ depends on USB_HID ++ ---help--- ++ Support for the ELO USB 4000/4500 touchscreens. Note that this is for ++ different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO. ++ ++config HID_EZKEY ++ tristate "Ezkey BTC 8193 keyboard" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Ezkey BTC 8193 keyboard. ++ ++config HID_HOLTEK ++ tristate "Holtek HID devices" ++ depends on USB_HID ++ ---help--- ++ Support for Holtek based devices: ++ - Holtek On Line Grip based game controller ++ - Trust GXT 18 Gaming Keyboard ++ - Sharkoon Drakonia / Perixx MX-2000 gaming mice ++ - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 / ++ Zalman ZM-GM1 ++ - SHARKOON DarkGlider Gaming mouse ++ - LEETGION Hellion Gaming Mouse ++ ++config HOLTEK_FF ++ bool "Holtek On Line Grip force feedback support" ++ depends on HID_HOLTEK ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you have a Holtek On Line Grip based game controller ++ and want to have force feedback support for it. ++ ++config HID_HUION ++ tristate "Huion tablets" ++ depends on USB_HID ++ ---help--- ++ Support for Huion 580 tablet. ++ ++config HID_KEYTOUCH ++ tristate "Keytouch HID devices" ++ depends on HID ++ ---help--- ++ Support for Keytouch HID devices not fully compliant with ++ the specification. Currently supported: ++ - Keytouch IEC 60945 ++ ++config HID_KYE ++ tristate "KYE/Genius devices" ++ depends on HID ++ ---help--- ++ Support for KYE/Genius devices not fully compliant with HID standard: ++ - Ergo Mouse ++ - EasyPen i405X tablet ++ - MousePen i608X tablet ++ - EasyPen M610X tablet ++ ++config HID_UCLOGIC ++ tristate "UC-Logic" ++ depends on HID ++ ---help--- ++ Support for UC-Logic tablets. ++ ++config HID_WALTOP ++ tristate "Waltop" ++ depends on HID ++ ---help--- ++ Support for Waltop tablets. ++ ++config HID_GYRATION ++ tristate "Gyration remote control" ++ depends on HID ++ ---help--- ++ Support for Gyration remote control. ++ ++config HID_ICADE ++ tristate "ION iCade arcade controller" ++ depends on HID ++ ---help--- ++ Support for the ION iCade arcade controller to work as a joystick. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hid-icade. ++ ++config HID_TWINHAN ++ tristate "Twinhan IR remote control" ++ depends on HID ++ ---help--- ++ Support for Twinhan IR remote control. ++ ++config HID_KENSINGTON ++ tristate "Kensington Slimblade Trackball" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Kensington Slimblade Trackball. ++ ++config HID_LCPOWER ++ tristate "LC-Power" ++ depends on HID ++ ---help--- ++ Support for LC-Power RC1000MCE RF remote control. ++ ++config HID_LENOVO_TPKBD ++ tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" ++ depends on HID ++ select NEW_LEDS ++ select LEDS_CLASS ++ ---help--- ++ Support for the Lenovo ThinkPad USB Keyboard with TrackPoint. ++ ++ Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint ++ and would like to use device-specific features like changing the ++ sensitivity of the trackpoint, using the microphone mute button or ++ controlling the mute and microphone mute LEDs. ++ ++config HID_LOGITECH ++ tristate "Logitech devices" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Logitech devices that are not fully compliant with HID standard. ++ ++config HID_LOGITECH_DJ ++ tristate "Logitech Unifying receivers full support" ++ depends on HIDRAW ++ depends on HID_LOGITECH ++ ---help--- ++ Say Y if you want support for Logitech Unifying receivers and devices. ++ Unifying receivers are capable of pairing up to 6 Logitech compliant ++ devices to the same receiver. Without this driver it will be handled by ++ generic USB_HID driver and all incoming events will be multiplexed ++ into a single mouse and a single keyboard device. ++ ++config LOGITECH_FF ++ bool "Logitech force feedback support" ++ depends on HID_LOGITECH ++ select INPUT_FF_MEMLESS ++ help ++ Say Y here if you have one of these devices: ++ - Logitech WingMan Cordless RumblePad ++ - Logitech WingMan Cordless RumblePad 2 ++ - Logitech WingMan Force 3D ++ - Logitech Formula Force EX ++ - Logitech WingMan Formula Force GP ++ ++ and if you want to enable force feedback for them. ++ Note: if you say N here, this device will still be supported, but without ++ force feedback. ++ ++config LOGIRUMBLEPAD2_FF ++ bool "Logitech force feedback support (variant 2)" ++ depends on HID_LOGITECH ++ select INPUT_FF_MEMLESS ++ help ++ Say Y here if you want to enable force feedback support for: ++ - Logitech RumblePad ++ - Logitech Rumblepad 2 ++ - Logitech Formula Vibration Feedback Wheel ++ ++config LOGIG940_FF ++ bool "Logitech Flight System G940 force feedback support" ++ depends on HID_LOGITECH ++ select INPUT_FF_MEMLESS ++ help ++ Say Y here if you want to enable force feedback support for Logitech ++ Flight System G940 devices. ++ ++config LOGIWHEELS_FF ++ bool "Logitech wheels configuration and force feedback support" ++ depends on HID_LOGITECH ++ select INPUT_FF_MEMLESS ++ default LOGITECH_FF ++ help ++ Say Y here if you want to enable force feedback and range setting ++ support for following Logitech wheels: ++ - Logitech Driving Force ++ - Logitech Driving Force Pro ++ - Logitech Driving Force GT ++ - Logitech G25 ++ - Logitech G27 ++ - Logitech MOMO/MOMO 2 ++ - Logitech Formula Force EX ++ ++config HID_MAGICMOUSE ++ tristate "Apple Magic Mouse/Trackpad multi-touch support" ++ depends on HID ++ ---help--- ++ Support for the Apple Magic Mouse/Trackpad multi-touch. ++ ++ Say Y here if you want support for the multi-touch features of the ++ Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad. ++ ++config HID_MICROSOFT ++ tristate "Microsoft non-fully HID-compliant devices" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Microsoft devices that are not fully compliant with HID standard. ++ ++config HID_MONTEREY ++ tristate "Monterey Genius KB29E keyboard" if EXPERT ++ depends on HID ++ default !EXPERT ++ ---help--- ++ Support for Monterey Genius KB29E. ++ ++config HID_MULTITOUCH ++ tristate "HID Multitouch panels" ++ depends on HID ++ ---help--- ++ Generic support for HID multitouch panels. ++ ++ Say Y here if you have one of the following devices: ++ - 3M PCT touch screens ++ - ActionStar dual touch panels ++ - Atmel panels ++ - Cando dual touch panels ++ - Chunghwa panels ++ - CVTouch panels ++ - Cypress TrueTouch panels ++ - Elan Microelectronics touch panels ++ - Elo TouchSystems IntelliTouch Plus panels ++ - GeneralTouch 'Sensing Win7-TwoFinger' panels ++ - GoodTouch panels ++ - Hanvon dual touch panels ++ - Ilitek dual touch panels ++ - IrTouch Infrared USB panels ++ - LG Display panels (Dell ST2220Tc) ++ - Lumio CrystalTouch panels ++ - MosArt dual-touch panels ++ - Panasonic multitouch panels ++ - PenMount dual touch panels ++ - Perixx Peripad 701 touchpad ++ - PixArt optical touch screen ++ - Pixcir dual touch panels ++ - Quanta panels ++ - eGalax dual-touch panels, including the Joojoo and Wetab tablets ++ - SiS multitouch panels ++ - Stantum multitouch panels ++ - Touch International Panels ++ - Unitec Panels ++ - Wistron optical touch panels ++ - XAT optical touch panels ++ - Xiroku optical touch panels ++ - Zytronic touch panels ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hid-multitouch. ++ ++config HID_NTRIG ++ tristate "N-Trig touch screen" ++ depends on USB_HID ++ ---help--- ++ Support for N-Trig touch screen. ++ ++config HID_ORTEK ++ tristate "Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad" ++ depends on HID ++ ---help--- ++ There are certain devices which have LogicalMaximum wrong in the keyboard ++ usage page of their report descriptor. The most prevailing ones so far ++ are manufactured by Ortek, thus the name of the driver. Currently ++ supported devices by this driver are ++ ++ - Ortek PKB-1700 ++ - Ortek WKB-2000 ++ - Skycable wireless presenter ++ ++config HID_OUYA ++ tristate "OUYA Game Controller" ++ depends on USB_HID ++ ---help--- ++ Support for OUYA Game Controller. ++ ++config HID_PANTHERLORD ++ tristate "Pantherlord/GreenAsia game controller" ++ depends on HID ++ ---help--- ++ Say Y here if you have a PantherLord/GreenAsia based game controller ++ or adapter. ++ ++config PANTHERLORD_FF ++ bool "Pantherlord force feedback support" ++ depends on HID_PANTHERLORD ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you have a PantherLord/GreenAsia based game controller ++ or adapter and want to enable force feedback support for it. ++ ++config HID_PETALYNX ++ tristate "Petalynx Maxter remote control" ++ depends on HID ++ ---help--- ++ Support for Petalynx Maxter remote control. ++ ++config HID_PICOLCD ++ tristate "PicoLCD (graphic version)" ++ depends on HID ++ ---help--- ++ This provides support for Minibox PicoLCD devices, currently ++ only the graphical ones are supported. ++ ++ This includes support for the following device features: ++ - Keypad ++ - Switching between Firmware and Flash mode ++ - EEProm / Flash access (via debugfs) ++ Features selectively enabled: ++ - Framebuffer for monochrome 256x64 display ++ - Backlight control ++ - Contrast control ++ - General purpose outputs ++ Features that are not (yet) supported: ++ - IR ++ ++config HID_PICOLCD_FB ++ bool "Framebuffer support" if EXPERT ++ default !EXPERT ++ depends on HID_PICOLCD ++ depends on HID_PICOLCD=FB || FB=y ++ select FB_DEFERRED_IO ++ select FB_SYS_FILLRECT ++ select FB_SYS_COPYAREA ++ select FB_SYS_IMAGEBLIT ++ select FB_SYS_FOPS ++ ---help--- ++ Provide access to PicoLCD's 256x64 monochrome display via a ++ framebuffer device. ++ ++config HID_PICOLCD_BACKLIGHT ++ bool "Backlight control" if EXPERT ++ default !EXPERT ++ depends on HID_PICOLCD ++ depends on HID_PICOLCD=BACKLIGHT_CLASS_DEVICE || BACKLIGHT_CLASS_DEVICE=y ++ ---help--- ++ Provide access to PicoLCD's backlight control via backlight ++ class. ++ ++config HID_PICOLCD_LCD ++ bool "Contrast control" if EXPERT ++ default !EXPERT ++ depends on HID_PICOLCD ++ depends on HID_PICOLCD=LCD_CLASS_DEVICE || LCD_CLASS_DEVICE=y ++ ---help--- ++ Provide access to PicoLCD's LCD contrast via lcd class. ++ ++config HID_PICOLCD_LEDS ++ bool "GPO via leds class" if EXPERT ++ default !EXPERT ++ depends on HID_PICOLCD ++ depends on HID_PICOLCD=LEDS_CLASS || LEDS_CLASS=y ++ ---help--- ++ Provide access to PicoLCD's GPO pins via leds class. ++ ++config HID_PICOLCD_CIR ++ bool "CIR via RC class" if EXPERT ++ default !EXPERT ++ depends on HID_PICOLCD ++ depends on HID_PICOLCD=RC_CORE || RC_CORE=y ++ ---help--- ++ Provide access to PicoLCD's CIR interface via remote control (LIRC). ++ ++config HID_PRIMAX ++ tristate "Primax non-fully HID-compliant devices" ++ depends on HID ++ ---help--- ++ Support for Primax devices that are not fully compliant with the ++ HID standard. ++ ++config HID_ROCCAT ++ tristate "Roccat device support" ++ depends on USB_HID ++ ---help--- ++ Support for Roccat devices. ++ Say Y here if you have a Roccat mouse or keyboard and want ++ support for its special functionalities. ++ ++config HID_SAITEK ++ tristate "Saitek non-fully HID-compliant devices" ++ depends on HID ++ ---help--- ++ Support for Saitek devices that are not fully compliant with the ++ HID standard. ++ ++ Currently only supports the PS1000 controller. ++ ++config HID_SAMSUNG ++ tristate "Samsung InfraRed remote control or keyboards" ++ depends on HID ++ ---help--- ++ Support for Samsung InfraRed remote control or keyboards. ++ ++config HID_SONY ++ tristate "Sony PS2/3 accessories" ++ depends on USB_HID ++ depends on NEW_LEDS ++ depends on LEDS_CLASS ++ ---help--- ++ Support for ++ ++ * Sony PS3 6-axis controllers ++ * Buzz controllers ++ * Sony PS3 Blue-ray Disk Remote Control (Bluetooth) ++ * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth) ++ ++config SONY_FF ++ bool "Sony PS2/3 accessories force feedback support" ++ depends on HID_SONY ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you have a Sony PS2/3 accessory and want to enable force ++ feedback support for it. ++ ++config HID_SPEEDLINK ++ tristate "Speedlink VAD Cezanne mouse support" ++ depends on HID ++ ---help--- ++ Support for Speedlink Vicious and Divine Cezanne mouse. ++ ++config HID_STEELSERIES ++ tristate "Steelseries SRW-S1 steering wheel support" ++ depends on HID ++ ---help--- ++ Support for Steelseries SRW-S1 steering wheel ++ ++config HID_SUNPLUS ++ tristate "Sunplus wireless desktop" ++ depends on HID ++ ---help--- ++ Support for Sunplus wireless desktop. ++ ++config HID_GREENASIA ++ tristate "GreenAsia (Product ID 0x12) game controller support" ++ depends on HID ++ ---help--- ++ Say Y here if you have a GreenAsia (Product ID 0x12) based game ++ controller or adapter. ++ ++config GREENASIA_FF ++ bool "GreenAsia (Product ID 0x12) force feedback support" ++ depends on HID_GREENASIA ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you have a GreenAsia (Product ID 0x12) based game controller ++ (like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter ++ and want to enable force feedback support for it. ++ ++config HID_HYPERV_MOUSE ++ tristate "Microsoft Hyper-V mouse driver" ++ depends on HYPERV ++ ---help--- ++ Select this option to enable the Hyper-V mouse driver. ++ ++config HID_SMARTJOYPLUS ++ tristate "SmartJoy PLUS PS2/USB adapter support" ++ depends on HID ++ ---help--- ++ Support for SmartJoy PLUS PS2/USB adapter, Super Dual Box, ++ Super Joy Box 3 Pro, Super Dual Box Pro, and Super Joy Box 5 Pro. ++ ++ Note that DDR (Dance Dance Revolution) mode is not supported, nor ++ is pressure sensitive buttons on the pro models. ++ ++config SMARTJOYPLUS_FF ++ bool "SmartJoy PLUS PS2/USB adapter force feedback support" ++ depends on HID_SMARTJOYPLUS ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you have a SmartJoy PLUS PS2/USB adapter and want to ++ enable force feedback support for it. ++ ++config HID_TIVO ++ tristate "TiVo Slide Bluetooth remote control support" ++ depends on HID ++ ---help--- ++ Say Y if you have a TiVo Slide Bluetooth remote control. ++ ++config HID_TOPSEED ++ tristate "TopSeed Cyberlink, BTC Emprex, Conceptronic remote control support" ++ depends on HID ++ ---help--- ++ Say Y if you have a TopSeed Cyberlink or BTC Emprex or Conceptronic ++ CLLRCMCE remote control. ++ ++config HID_THINGM ++ tristate "ThingM blink(1) USB RGB LED" ++ depends on HID ++ depends on LEDS_CLASS ++ ---help--- ++ Support for the ThingM blink(1) USB RGB LED. This driver registers a ++ Linux LED class instance, plus additional sysfs attributes to control ++ RGB colors, fade time and playing. The device is exposed through hidraw ++ to access other functions. ++ ++config HID_THRUSTMASTER ++ tristate "ThrustMaster devices support" ++ depends on HID ++ ---help--- ++ Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or ++ a THRUSTMASTER Ferrari GT Rumble Wheel. ++ ++config THRUSTMASTER_FF ++ bool "ThrustMaster devices force feedback support" ++ depends on HID_THRUSTMASTER ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or 3, ++ a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT ++ Rumble Force or Force Feedback Wheel. ++ ++config HID_WACOM ++ tristate "Wacom Bluetooth devices support" ++ depends on HID ++ depends on LEDS_CLASS ++ select POWER_SUPPLY ++ ---help--- ++ Support for Wacom Graphire Bluetooth and Intuos4 WL tablets. ++ ++config HID_WIIMOTE ++ tristate "Nintendo Wii / Wii U peripherals" ++ depends on HID ++ depends on LEDS_CLASS ++ select POWER_SUPPLY ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Support for Nintendo Wii and Wii U Bluetooth peripherals. Supported ++ devices are the Wii Remote and its extension devices, but also devices ++ based on the Wii Remote like the Wii U Pro Controller or the ++ Wii Balance Board. ++ ++ Support for all official Nintendo extensions is available, however, 3rd ++ party extensions might not be supported. Please report these devices to: ++ http://github.com/dvdhrm/xwiimote/issues ++ ++ Other Nintendo Wii U peripherals that are IEEE 802.11 based (including ++ the Wii U Gamepad) might be supported in the future. But currently ++ support is limited to Bluetooth based devices. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hid-wiimote. ++ ++config HID_XINMO ++ tristate "Xin-Mo non-fully compliant devices" ++ depends on HID ++ ---help--- ++ Support for Xin-Mo devices that are not fully compliant with the HID ++ standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here ++ if you have a Xin-Mo Dual Arcade controller. ++ ++config HID_ZEROPLUS ++ tristate "Zeroplus based game controller support" ++ depends on HID ++ ---help--- ++ Say Y here if you have a Zeroplus based game controller. ++ ++config ZEROPLUS_FF ++ bool "Zeroplus based game controller force feedback support" ++ depends on HID_ZEROPLUS ++ select INPUT_FF_MEMLESS ++ ---help--- ++ Say Y here if you have a Zeroplus based game controller and want ++ to have force feedback support for it. ++ ++config HID_ZYDACRON ++ tristate "Zydacron remote control support" ++ depends on HID ++ ---help--- ++ Support for Zydacron remote control. ++ ++config HID_SENSOR_HUB ++ tristate "HID Sensors framework support" ++ depends on HID ++ select MFD_CORE ++ default n ++ ---help--- ++ Support for HID Sensor framework. This creates a MFD instance ++ for a sensor hub and identifies all the sensors connected to it. ++ Each sensor is registered as a MFD cell, so that sensor specific ++ processing can be done in a separate driver. Each sensor ++ drivers can use the service provided by this driver to register ++ for events and handle data streams. Each sensor driver can format ++ data and present to user mode using input or IIO interface. ++ ++endmenu ++ ++endif # HID ++ ++source "drivers/hid/usbhid/Kconfig" ++ ++source "drivers/hid/i2c-hid/Kconfig" ++ ++endmenu +diff -Nur linux-3.14.36/drivers/hid/Makefile linux-openelec/drivers/hid/Makefile +--- linux-3.14.36/drivers/hid/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hid/Makefile 2015-07-24 18:03:29.980842002 -0500 +@@ -67,6 +67,7 @@ + obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o + obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o + obj-$(CONFIG_HID_ORTEK) += hid-ortek.o ++obj-$(CONFIG_HID_OUYA) += hid-ouya.o + obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o + obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o + obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o +@@ -101,6 +102,7 @@ + obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o + obj-$(CONFIG_HID_SONY) += hid-sony.o + obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o ++obj-$(CONFIG_HID_SPINELPLUS) += hid-spinelplus.o + obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o + obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o + obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o +diff -Nur linux-3.14.36/drivers/hid/Makefile.orig linux-openelec/drivers/hid/Makefile.orig +--- linux-3.14.36/drivers/hid/Makefile.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hid/Makefile.orig 2015-07-24 18:03:29.968842002 -0500 +@@ -0,0 +1,126 @@ ++# ++# Makefile for the HID driver ++# ++hid-y := hid-core.o hid-input.o ++ ++ifdef CONFIG_DEBUG_FS ++ hid-objs += hid-debug.o ++endif ++ ++obj-$(CONFIG_HID) += hid.o ++obj-$(CONFIG_UHID) += uhid.o ++ ++obj-$(CONFIG_HID_GENERIC) += hid-generic.o ++ ++hid-$(CONFIG_HIDRAW) += hidraw.o ++ ++hid-logitech-y := hid-lg.o ++ifdef CONFIG_LOGITECH_FF ++ hid-logitech-y += hid-lgff.o ++endif ++ifdef CONFIG_LOGIRUMBLEPAD2_FF ++ hid-logitech-y += hid-lg2ff.o ++endif ++ifdef CONFIG_LOGIG940_FF ++ hid-logitech-y += hid-lg3ff.o ++endif ++ifdef CONFIG_LOGIWHEELS_FF ++ hid-logitech-y += hid-lg4ff.o ++endif ++ ++hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o ++ifdef CONFIG_DEBUG_FS ++ hid-wiimote-y += hid-wiimote-debug.o ++endif ++ ++obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o ++obj-$(CONFIG_HID_ACRUX) += hid-axff.o ++obj-$(CONFIG_HID_APPLE) += hid-apple.o ++obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o ++obj-$(CONFIG_HID_AUREAL) += hid-aureal.o ++obj-$(CONFIG_HID_BELKIN) += hid-belkin.o ++obj-$(CONFIG_HID_CHERRY) += hid-cherry.o ++obj-$(CONFIG_HID_CHICONY) += hid-chicony.o ++obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o ++obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o ++obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o ++obj-$(CONFIG_HID_ELECOM) += hid-elecom.o ++obj-$(CONFIG_HID_ELO) += hid-elo.o ++obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o ++obj-$(CONFIG_HID_GYRATION) += hid-gyration.o ++obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o ++obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o ++obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o ++obj-$(CONFIG_HID_HUION) += hid-huion.o ++obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o ++obj-$(CONFIG_HID_ICADE) += hid-icade.o ++obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o ++obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o ++obj-$(CONFIG_HID_KYE) += hid-kye.o ++obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o ++obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o ++obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o ++obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o ++obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o ++obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o ++obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o ++obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o ++obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o ++obj-$(CONFIG_HID_ORTEK) += hid-ortek.o ++obj-$(CONFIG_HID_OUYA) += hid-ouya.o ++obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o ++obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o ++obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o ++obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o ++hid-picolcd-y += hid-picolcd_core.o ++ifdef CONFIG_HID_PICOLCD_FB ++hid-picolcd-y += hid-picolcd_fb.o ++endif ++ifdef CONFIG_HID_PICOLCD_BACKLIGHT ++hid-picolcd-y += hid-picolcd_backlight.o ++endif ++ifdef CONFIG_HID_PICOLCD_LCD ++hid-picolcd-y += hid-picolcd_lcd.o ++endif ++ifdef CONFIG_HID_PICOLCD_LEDS ++hid-picolcd-y += hid-picolcd_leds.o ++endif ++ifdef CONFIG_HID_PICOLCD_CIR ++hid-picolcd-y += hid-picolcd_cir.o ++endif ++ifdef CONFIG_DEBUG_FS ++hid-picolcd-y += hid-picolcd_debugfs.o ++endif ++ ++obj-$(CONFIG_HID_PRIMAX) += hid-primax.o ++obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ ++ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ ++ hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ ++ hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o ++obj-$(CONFIG_HID_SAITEK) += hid-saitek.o ++obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o ++obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o ++obj-$(CONFIG_HID_SONY) += hid-sony.o ++obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o ++obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o ++obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o ++obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o ++obj-$(CONFIG_HID_THINGM) += hid-thingm.o ++obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o ++obj-$(CONFIG_HID_TIVO) += hid-tivo.o ++obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o ++obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o ++obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o ++obj-$(CONFIG_HID_XINMO) += hid-xinmo.o ++obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o ++obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o ++obj-$(CONFIG_HID_WACOM) += hid-wacom.o ++obj-$(CONFIG_HID_WALTOP) += hid-waltop.o ++obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o ++obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o ++ ++obj-$(CONFIG_USB_HID) += usbhid/ ++obj-$(CONFIG_USB_MOUSE) += usbhid/ ++obj-$(CONFIG_USB_KBD) += usbhid/ ++ ++obj-$(CONFIG_I2C_HID) += i2c-hid/ +diff -Nur linux-3.14.36/drivers/hwmon/Kconfig linux-openelec/drivers/hwmon/Kconfig +--- linux-3.14.36/drivers/hwmon/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hwmon/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -1584,4 +1584,19 @@ + + endif # ACPI + ++config SENSORS_MAG3110 ++ tristate "Freescale MAG3110 e-compass sensor" ++ depends on I2C && SYSFS ++ help ++ If you say yes here you get support for the Freescale MAG3110 ++ e-compass sensor. ++ This driver can also be built as a module. If so, the module ++ will be called mag3110. ++ ++config MXC_MMA8451 ++ tristate "MMA8451 device driver" ++ depends on I2C ++ depends on INPUT_POLLDEV ++ default y ++ + endif # HWMON +diff -Nur linux-3.14.36/drivers/hwmon/mag3110.c linux-openelec/drivers/hwmon/mag3110.c +--- linux-3.14.36/drivers/hwmon/mag3110.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hwmon/mag3110.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,611 @@ ++/* ++ * ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MAG3110_DRV_NAME "mag3110" ++#define MAG3110_ID 0xC4 ++#define MAG3110_XYZ_DATA_LEN 6 ++#define MAG3110_STATUS_ZYXDR 0x08 ++ ++#define MAG3110_AC_MASK (0x01) ++#define MAG3110_AC_OFFSET 0 ++#define MAG3110_DR_MODE_MASK (0x7 << 5) ++#define MAG3110_DR_MODE_OFFSET 5 ++#define MAG3110_IRQ_USED 0 ++ ++#define POLL_INTERVAL_MAX 500 ++#define POLL_INTERVAL 100 ++#define INT_TIMEOUT 1000 ++#define DEFAULT_POSITION 2 ++/* register enum for mag3110 registers */ ++enum { ++ MAG3110_DR_STATUS = 0x00, ++ MAG3110_OUT_X_MSB, ++ MAG3110_OUT_X_LSB, ++ MAG3110_OUT_Y_MSB, ++ MAG3110_OUT_Y_LSB, ++ MAG3110_OUT_Z_MSB, ++ MAG3110_OUT_Z_LSB, ++ MAG3110_WHO_AM_I, ++ ++ MAG3110_OFF_X_MSB, ++ MAG3110_OFF_X_LSB, ++ MAG3110_OFF_Y_MSB, ++ MAG3110_OFF_Y_LSB, ++ MAG3110_OFF_Z_MSB, ++ MAG3110_OFF_Z_LSB, ++ ++ MAG3110_DIE_TEMP, ++ ++ MAG3110_CTRL_REG1 = 0x10, ++ MAG3110_CTRL_REG2, ++}; ++enum { ++ MAG_STANDBY, ++ MAG_ACTIVED ++}; ++struct mag3110_data { ++ struct i2c_client *client; ++ struct input_polled_dev *poll_dev; ++ struct device *hwmon_dev; ++ wait_queue_head_t waitq; ++ bool data_ready; ++ u8 ctl_reg1; ++ int active; ++ int position; ++}; ++static short MAGHAL[8][3][3] = { ++ { {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} }, ++ { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }, ++ { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} }, ++ { {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} }, ++ ++ { {0, 1, 0}, {1, 0, 0}, {0, 0, -1} }, ++ { {1, 0, 0}, {0, -1, 0}, {0, 0, -1} }, ++ { {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} }, ++ { {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} }, ++}; ++ ++static struct mag3110_data *mag3110_pdata; ++/*! ++ * This function do one mag3110 register read. ++ */ ++static DEFINE_MUTEX(mag3110_lock); ++static int mag3110_adjust_position(short *x, short *y, short *z) ++{ ++ short rawdata[3], data[3]; ++ int i, j; ++ int position = mag3110_pdata->position; ++ if (position < 0 || position > 7) ++ position = 0; ++ rawdata[0] = *x; ++ rawdata[1] = *y; ++ rawdata[2] = *z; ++ for (i = 0; i < 3; i++) { ++ data[i] = 0; ++ for (j = 0; j < 3; j++) ++ data[i] += rawdata[j] * MAGHAL[position][i][j]; ++ } ++ *x = data[0]; ++ *y = data[1]; ++ *z = data[2]; ++ return 0; ++} ++ ++static int mag3110_read_reg(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/*! ++ * This function do one mag3110 register write. ++ */ ++static int mag3110_write_reg(struct i2c_client *client, u8 reg, char value) ++{ ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(client, reg, value); ++ if (ret < 0) ++ dev_err(&client->dev, "i2c write failed\n"); ++ return ret; ++} ++ ++/*! ++ * This function do multiple mag3110 registers read. ++ */ ++static int mag3110_read_block_data(struct i2c_client *client, u8 reg, ++ int count, u8 *addr) ++{ ++ if (i2c_smbus_read_i2c_block_data(client, reg, count, addr) < count) { ++ dev_err(&client->dev, "i2c block read failed\n"); ++ return -1; ++ } ++ ++ return count; ++} ++ ++/* ++ * Initialization function ++ */ ++static int mag3110_init_client(struct i2c_client *client) ++{ ++ int val, ret; ++ ++ /* enable automatic resets */ ++ val = 0x80; ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG2, val); ++ ++ /* set default data rate to 10HZ */ ++ val = mag3110_read_reg(client, MAG3110_CTRL_REG1); ++ val |= (0x0 << MAG3110_DR_MODE_OFFSET); ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, val); ++ ++ return ret; ++} ++ ++/*************************************************************** ++* ++* read sensor data from mag3110 ++* ++***************************************************************/ ++static int mag3110_read_data(short *x, short *y, short *z) ++{ ++ struct mag3110_data *data; ++ int retry = 3; ++ u8 tmp_data[MAG3110_XYZ_DATA_LEN]; ++ int result; ++ if (!mag3110_pdata || mag3110_pdata->active == MAG_STANDBY) ++ return -EINVAL; ++ ++ data = mag3110_pdata; ++#if MAG3110_IRQ_USED ++ if (!wait_event_interruptible_timeout ++ (data->waitq, data->data_ready != 0, ++ msecs_to_jiffies(INT_TIMEOUT))) { ++ dev_dbg(&data->client->dev, "interrupt not received\n"); ++ return -ETIME; ++ } ++#else ++ do { ++ msleep(1); ++ result = i2c_smbus_read_byte_data(data->client, ++ MAG3110_DR_STATUS); ++ retry--; ++ } while (!(result & MAG3110_STATUS_ZYXDR) && retry > 0); ++ /* Clear data_ready flag after data is read out */ ++ if (retry == 0) ++ return -EINVAL; ++#endif ++ ++ data->data_ready = 0; ++ ++ if (mag3110_read_block_data(data->client, ++ MAG3110_OUT_X_MSB, MAG3110_XYZ_DATA_LEN, ++ tmp_data) < 0) ++ return -1; ++ ++ *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; ++ *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; ++ *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; ++ ++ return 0; ++} ++ ++static void report_abs(void) ++{ ++ struct input_dev *idev; ++ short x, y, z; ++ ++ mutex_lock(&mag3110_lock); ++ if (mag3110_read_data(&x, &y, &z) != 0) ++ goto out; ++ mag3110_adjust_position(&x, &y, &z); ++ idev = mag3110_pdata->poll_dev->input; ++ input_report_abs(idev, ABS_X, x); ++ input_report_abs(idev, ABS_Y, y); ++ input_report_abs(idev, ABS_Z, z); ++ input_sync(idev); ++out: ++ mutex_unlock(&mag3110_lock); ++} ++ ++static void mag3110_dev_poll(struct input_polled_dev *dev) ++{ ++ report_abs(); ++} ++ ++#if MAG3110_IRQ_USED ++static irqreturn_t mag3110_irq_handler(int irq, void *dev_id) ++{ ++ mag3110_pdata->data_ready = 1; ++ wake_up_interruptible(&mag3110_pdata->waitq); ++ ++ return IRQ_HANDLED; ++} ++#endif ++static ssize_t mag3110_enable_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client; ++ int val; ++ mutex_lock(&mag3110_lock); ++ client = mag3110_pdata->client; ++ val = mag3110_read_reg(client, MAG3110_CTRL_REG1) & MAG3110_AC_MASK; ++ ++ mutex_unlock(&mag3110_lock); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t mag3110_enable_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client; ++ int reg, ret; ++ long enable; ++ u8 tmp_data[MAG3110_XYZ_DATA_LEN]; ++ ++ ret = strict_strtol(buf, 10, &enable); ++ if (ret) { ++ dev_err(dev, "string to long error\n"); ++ return ret; ++ } ++ ++ mutex_lock(&mag3110_lock); ++ client = mag3110_pdata->client; ++ reg = mag3110_read_reg(client, MAG3110_CTRL_REG1); ++ if (enable && mag3110_pdata->active == MAG_STANDBY) { ++ reg |= MAG3110_AC_MASK; ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); ++ if (!ret) ++ mag3110_pdata->active = MAG_ACTIVED; ++ } else if (!enable && mag3110_pdata->active == MAG_ACTIVED) { ++ reg &= ~MAG3110_AC_MASK; ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); ++ if (!ret) ++ mag3110_pdata->active = MAG_STANDBY; ++ } ++ ++ if (mag3110_pdata->active == MAG_ACTIVED) { ++ msleep(100); ++ /* Read out MSB data to clear interrupt flag automatically */ ++ mag3110_read_block_data(client, MAG3110_OUT_X_MSB, ++ MAG3110_XYZ_DATA_LEN, tmp_data); ++ } ++ mutex_unlock(&mag3110_lock); ++ return count; ++} ++ ++static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, ++ mag3110_enable_show, mag3110_enable_store); ++ ++static ssize_t mag3110_dr_mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client; ++ int val; ++ ++ client = mag3110_pdata->client; ++ val = (mag3110_read_reg(client, MAG3110_CTRL_REG1) ++ & MAG3110_DR_MODE_MASK) >> MAG3110_DR_MODE_OFFSET; ++ ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t mag3110_dr_mode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client; ++ int reg, ret; ++ unsigned long val; ++ ++ /* This must be done when mag3110 is disabled */ ++ if ((strict_strtoul(buf, 10, &val) < 0) || (val > 7)) ++ return -EINVAL; ++ ++ client = mag3110_pdata->client; ++ reg = mag3110_read_reg(client, MAG3110_CTRL_REG1) & ++ ~MAG3110_DR_MODE_MASK; ++ reg |= (val << MAG3110_DR_MODE_OFFSET); ++ /* MAG3110_CTRL_REG1 bit 5-7: data rate mode */ ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); ++ if (ret < 0) ++ return ret; ++ ++ return count; ++} ++ ++static DEVICE_ATTR(dr_mode, S_IWUSR | S_IRUGO, ++ mag3110_dr_mode_show, mag3110_dr_mode_store); ++ ++static ssize_t mag3110_position_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int val; ++ mutex_lock(&mag3110_lock); ++ val = mag3110_pdata->position; ++ mutex_unlock(&mag3110_lock); ++ return sprintf(buf, "%d\n", val); ++} ++ ++static ssize_t mag3110_position_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ long position; ++ int ret; ++ ret = strict_strtol(buf, 10, &position); ++ if (ret) { ++ dev_err(dev, "string to long error\n"); ++ return ret; ++ } ++ ++ mutex_lock(&mag3110_lock); ++ mag3110_pdata->position = (int)position; ++ mutex_unlock(&mag3110_lock); ++ return count; ++} ++ ++static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, ++ mag3110_position_show, mag3110_position_store); ++ ++static struct attribute *mag3110_attributes[] = { ++ &dev_attr_enable.attr, ++ &dev_attr_dr_mode.attr, ++ &dev_attr_position.attr, ++ NULL ++}; ++ ++static const struct attribute_group mag3110_attr_group = { ++ .attrs = mag3110_attributes, ++}; ++ ++static int mag3110_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct i2c_adapter *adapter; ++ struct input_dev *idev; ++ struct mag3110_data *data; ++ int ret = 0; ++ struct regulator *vdd, *vdd_io; ++ u32 pos = 0; ++ struct device_node *of_node = client->dev.of_node; ++ vdd = NULL; ++ vdd_io = NULL; ++ ++ vdd = devm_regulator_get(&client->dev, "vdd"); ++ if (!IS_ERR(vdd)) { ++ ret = regulator_enable(vdd); ++ if (ret) { ++ dev_err(&client->dev, "vdd set voltage error\n"); ++ return ret; ++ } ++ } ++ ++ vdd_io = devm_regulator_get(&client->dev, "vddio"); ++ if (!IS_ERR(vdd_io)) { ++ ret = regulator_enable(vdd_io); ++ if (ret) { ++ dev_err(&client->dev, "vddio set voltage error\n"); ++ return ret; ++ } ++ } ++ ++ adapter = to_i2c_adapter(client->dev.parent); ++ if (!i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_I2C_BLOCK)) ++ return -EIO; ++ ++ dev_info(&client->dev, "check mag3110 chip ID\n"); ++ ret = mag3110_read_reg(client, MAG3110_WHO_AM_I); ++ ++ if (MAG3110_ID != ret) { ++ dev_err(&client->dev, ++ "read chip ID 0x%x is not equal to 0x%x!\n", ret, ++ MAG3110_ID); ++ return -EINVAL; ++ } ++ data = kzalloc(sizeof(struct mag3110_data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ data->client = client; ++ i2c_set_clientdata(client, data); ++ /* Init queue */ ++ init_waitqueue_head(&data->waitq); ++ ++ data->hwmon_dev = hwmon_device_register(&client->dev); ++ if (IS_ERR(data->hwmon_dev)) { ++ dev_err(&client->dev, "hwmon register failed!\n"); ++ ret = PTR_ERR(data->hwmon_dev); ++ goto error_rm_dev_sysfs; ++ } ++ ++ /*input poll device register */ ++ data->poll_dev = input_allocate_polled_device(); ++ if (!data->poll_dev) { ++ dev_err(&client->dev, "alloc poll device failed!\n"); ++ ret = -ENOMEM; ++ goto error_rm_hwmon_dev; ++ } ++ data->poll_dev->poll = mag3110_dev_poll; ++ data->poll_dev->poll_interval = POLL_INTERVAL; ++ data->poll_dev->poll_interval_max = POLL_INTERVAL_MAX; ++ idev = data->poll_dev->input; ++ idev->name = MAG3110_DRV_NAME; ++ idev->id.bustype = BUS_I2C; ++ idev->evbit[0] = BIT_MASK(EV_ABS); ++ input_set_abs_params(idev, ABS_X, -15000, 15000, 0, 0); ++ input_set_abs_params(idev, ABS_Y, -15000, 15000, 0, 0); ++ input_set_abs_params(idev, ABS_Z, -15000, 15000, 0, 0); ++ ret = input_register_polled_device(data->poll_dev); ++ if (ret) { ++ dev_err(&client->dev, "register poll device failed!\n"); ++ goto error_free_poll_dev; ++ } ++ ++ /*create device group in sysfs as user interface */ ++ ret = sysfs_create_group(&idev->dev.kobj, &mag3110_attr_group); ++ if (ret) { ++ dev_err(&client->dev, "create device file failed!\n"); ++ ret = -EINVAL; ++ goto error_rm_poll_dev; ++ } ++ /* set irq type to edge rising */ ++#if MAG3110_IRQ_USED ++ ret = request_irq(client->irq, mag3110_irq_handler, ++ IRQF_TRIGGER_RISING, client->dev.driver->name, idev); ++ if (ret < 0) { ++ dev_err(&client->dev, "failed to register irq %d!\n", ++ client->irq); ++ goto error_rm_dev_sysfs; ++ } ++#endif ++ /* Initialize mag3110 chip */ ++ mag3110_init_client(client); ++ mag3110_pdata = data; ++ mag3110_pdata->active = MAG_STANDBY; ++ ret = of_property_read_u32(of_node, "position", &pos); ++ if (ret) ++ pos = DEFAULT_POSITION; ++ mag3110_pdata->position = (int)pos; ++ dev_info(&client->dev, "mag3110 is probed\n"); ++ return 0; ++error_rm_dev_sysfs: ++ sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group); ++error_rm_poll_dev: ++ input_unregister_polled_device(data->poll_dev); ++error_free_poll_dev: ++ input_free_polled_device(data->poll_dev); ++error_rm_hwmon_dev: ++ hwmon_device_unregister(data->hwmon_dev); ++ ++ kfree(data); ++ mag3110_pdata = NULL; ++ ++ return ret; ++} ++ ++static int mag3110_remove(struct i2c_client *client) ++{ ++ struct mag3110_data *data; ++ int ret; ++ ++ data = i2c_get_clientdata(client); ++ ++ data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1); ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, ++ data->ctl_reg1 & ~MAG3110_AC_MASK); ++ ++ free_irq(client->irq, data); ++ input_unregister_polled_device(data->poll_dev); ++ input_free_polled_device(data->poll_dev); ++ hwmon_device_unregister(data->hwmon_dev); ++ sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group); ++ kfree(data); ++ mag3110_pdata = NULL; ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM ++static int mag3110_suspend(struct i2c_client *client, pm_message_t mesg) ++{ ++ int ret = 0; ++ struct mag3110_data *data = i2c_get_clientdata(client); ++ if (data->active == MAG_ACTIVED) { ++ data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1); ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, ++ data->ctl_reg1 & ~MAG3110_AC_MASK); ++ } ++ return ret; ++} ++ ++static int mag3110_resume(struct i2c_client *client) ++{ ++ int ret = 0; ++ u8 tmp_data[MAG3110_XYZ_DATA_LEN]; ++ struct mag3110_data *data = i2c_get_clientdata(client); ++ if (data->active == MAG_ACTIVED) { ++ ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, ++ data->ctl_reg1); ++ ++ if (data->ctl_reg1 & MAG3110_AC_MASK) { ++ /* Read out MSB data to clear interrupt ++ flag automatically */ ++ mag3110_read_block_data(client, MAG3110_OUT_X_MSB, ++ MAG3110_XYZ_DATA_LEN, tmp_data); ++ } ++ } ++ return ret; ++} ++ ++#else ++#define mag3110_suspend NULL ++#define mag3110_resume NULL ++#endif /* CONFIG_PM */ ++ ++static const struct i2c_device_id mag3110_id[] = { ++ {MAG3110_DRV_NAME, 0}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(i2c, mag3110_id); ++static struct i2c_driver mag3110_driver = { ++ .driver = {.name = MAG3110_DRV_NAME, ++ .owner = THIS_MODULE,}, ++ .suspend = mag3110_suspend, ++ .resume = mag3110_resume, ++ .probe = mag3110_probe, ++ .remove = mag3110_remove, ++ .id_table = mag3110_id, ++}; ++ ++static int __init mag3110_init(void) ++{ ++ return i2c_add_driver(&mag3110_driver); ++} ++ ++static void __exit mag3110_exit(void) ++{ ++ i2c_del_driver(&mag3110_driver); ++} ++ ++module_init(mag3110_init); ++module_exit(mag3110_exit); ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("Freescale mag3110 3-axis magnetometer driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/hwmon/Makefile linux-openelec/drivers/hwmon/Makefile +--- linux-3.14.36/drivers/hwmon/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/hwmon/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -142,6 +142,8 @@ + obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o + obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o + obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o ++obj-$(CONFIG_SENSORS_MAG3110) += mag3110.o ++obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o + + obj-$(CONFIG_PMBUS) += pmbus/ + +diff -Nur linux-3.14.36/drivers/hwmon/mxc_mma8451.c linux-openelec/drivers/hwmon/mxc_mma8451.c +--- linux-3.14.36/drivers/hwmon/mxc_mma8451.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/hwmon/mxc_mma8451.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,598 @@ ++/* ++ * mma8451.c - Linux kernel modules for 3-Axis Orientation/Motion ++ * Detection Sensor ++ * ++ * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MMA8451_I2C_ADDR 0x1C ++#define MMA8451_ID 0x1A ++#define MMA8452_ID 0x2A ++#define MMA8453_ID 0x3A ++ ++#define POLL_INTERVAL_MIN 1 ++#define POLL_INTERVAL_MAX 500 ++#define POLL_INTERVAL 100 /* msecs */ ++#define INPUT_FUZZ 32 ++#define INPUT_FLAT 32 ++#define MODE_CHANGE_DELAY_MS 100 ++ ++#define MMA8451_STATUS_ZYXDR 0x08 ++#define MMA8451_BUF_SIZE 7 ++#define DEFAULT_POSITION 0 ++ ++/* register enum for mma8451 registers */ ++enum { ++ MMA8451_STATUS = 0x00, ++ MMA8451_OUT_X_MSB, ++ MMA8451_OUT_X_LSB, ++ MMA8451_OUT_Y_MSB, ++ MMA8451_OUT_Y_LSB, ++ MMA8451_OUT_Z_MSB, ++ MMA8451_OUT_Z_LSB, ++ ++ MMA8451_F_SETUP = 0x09, ++ MMA8451_TRIG_CFG, ++ MMA8451_SYSMOD, ++ MMA8451_INT_SOURCE, ++ MMA8451_WHO_AM_I, ++ MMA8451_XYZ_DATA_CFG, ++ MMA8451_HP_FILTER_CUTOFF, ++ ++ MMA8451_PL_STATUS, ++ MMA8451_PL_CFG, ++ MMA8451_PL_COUNT, ++ MMA8451_PL_BF_ZCOMP, ++ MMA8451_P_L_THS_REG, ++ ++ MMA8451_FF_MT_CFG, ++ MMA8451_FF_MT_SRC, ++ MMA8451_FF_MT_THS, ++ MMA8451_FF_MT_COUNT, ++ ++ MMA8451_TRANSIENT_CFG = 0x1D, ++ MMA8451_TRANSIENT_SRC, ++ MMA8451_TRANSIENT_THS, ++ MMA8451_TRANSIENT_COUNT, ++ ++ MMA8451_PULSE_CFG, ++ MMA8451_PULSE_SRC, ++ MMA8451_PULSE_THSX, ++ MMA8451_PULSE_THSY, ++ MMA8451_PULSE_THSZ, ++ MMA8451_PULSE_TMLT, ++ MMA8451_PULSE_LTCY, ++ MMA8451_PULSE_WIND, ++ ++ MMA8451_ASLP_COUNT, ++ MMA8451_CTRL_REG1, ++ MMA8451_CTRL_REG2, ++ MMA8451_CTRL_REG3, ++ MMA8451_CTRL_REG4, ++ MMA8451_CTRL_REG5, ++ ++ MMA8451_OFF_X, ++ MMA8451_OFF_Y, ++ MMA8451_OFF_Z, ++ ++ MMA8451_REG_END, ++}; ++ ++/* The sensitivity is represented in counts/g. In 2g mode the ++sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512 ++counts/g and in 8g mode the sensitivity is 256 counts/g. ++ */ ++enum { ++ MODE_2G = 0, ++ MODE_4G, ++ MODE_8G, ++}; ++ ++enum { ++ MMA_STANDBY = 0, ++ MMA_ACTIVED, ++}; ++ ++/* mma8451 status */ ++struct mma8451_status { ++ u8 mode; ++ u8 ctl_reg1; ++ int active; ++ int position; ++}; ++ ++static struct mma8451_status mma_status; ++static struct input_polled_dev *mma8451_idev; ++static struct device *hwmon_dev; ++static struct i2c_client *mma8451_i2c_client; ++ ++static int senstive_mode = MODE_2G; ++static int ACCHAL[8][3][3] = { ++ { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} }, ++ { {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} }, ++ { {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} }, ++ { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }, ++ ++ { {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} }, ++ { {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} }, ++ { {0, 1, 0}, {1, 0, 0}, {0, 0, -1} }, ++ { {1, 0, 0}, {0, -1, 0}, {0, 0, -1} }, ++}; ++ ++static DEFINE_MUTEX(mma8451_lock); ++static int mma8451_adjust_position(short *x, short *y, short *z) ++{ ++ short rawdata[3], data[3]; ++ int i, j; ++ int position = mma_status.position; ++ if (position < 0 || position > 7) ++ position = 0; ++ rawdata[0] = *x; ++ rawdata[1] = *y; ++ rawdata[2] = *z; ++ for (i = 0; i < 3; i++) { ++ data[i] = 0; ++ for (j = 0; j < 3; j++) ++ data[i] += rawdata[j] * ACCHAL[position][i][j]; ++ } ++ *x = data[0]; ++ *y = data[1]; ++ *z = data[2]; ++ return 0; ++} ++ ++static int mma8451_change_mode(struct i2c_client *client, int mode) ++{ ++ int result; ++ ++ mma_status.ctl_reg1 = 0; ++ result = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 0); ++ if (result < 0) ++ goto out; ++ mma_status.active = MMA_STANDBY; ++ ++ result = i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG, ++ mode); ++ if (result < 0) ++ goto out; ++ mdelay(MODE_CHANGE_DELAY_MS); ++ mma_status.mode = mode; ++ ++ return 0; ++out: ++ dev_err(&client->dev, "error when init mma8451:(%d)", result); ++ return result; ++} ++ ++static int mma8451_read_data(short *x, short *y, short *z) ++{ ++ u8 tmp_data[MMA8451_BUF_SIZE]; ++ int ret; ++ ++ ret = i2c_smbus_read_i2c_block_data(mma8451_i2c_client, ++ MMA8451_OUT_X_MSB, 7, tmp_data); ++ if (ret < MMA8451_BUF_SIZE) { ++ dev_err(&mma8451_i2c_client->dev, "i2c block read failed\n"); ++ return -EIO; ++ } ++ ++ *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; ++ *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; ++ *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; ++ return 0; ++} ++ ++static void report_abs(void) ++{ ++ short x, y, z; ++ int result; ++ int retry = 3; ++ ++ mutex_lock(&mma8451_lock); ++ if (mma_status.active == MMA_STANDBY) ++ goto out; ++ /* wait for the data ready */ ++ do { ++ result = i2c_smbus_read_byte_data(mma8451_i2c_client, ++ MMA8451_STATUS); ++ retry--; ++ msleep(1); ++ } while (!(result & MMA8451_STATUS_ZYXDR) && retry > 0); ++ if (retry == 0) ++ goto out; ++ if (mma8451_read_data(&x, &y, &z) != 0) ++ goto out; ++ mma8451_adjust_position(&x, &y, &z); ++ input_report_abs(mma8451_idev->input, ABS_X, x); ++ input_report_abs(mma8451_idev->input, ABS_Y, y); ++ input_report_abs(mma8451_idev->input, ABS_Z, z); ++ input_sync(mma8451_idev->input); ++out: ++ mutex_unlock(&mma8451_lock); ++} ++ ++static void mma8451_dev_poll(struct input_polled_dev *dev) ++{ ++ report_abs(); ++} ++ ++static ssize_t mma8451_enable_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client; ++ u8 val; ++ int enable; ++ ++ mutex_lock(&mma8451_lock); ++ client = mma8451_i2c_client; ++ val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); ++ if ((val & 0x01) && mma_status.active == MMA_ACTIVED) ++ enable = 1; ++ else ++ enable = 0; ++ mutex_unlock(&mma8451_lock); ++ return sprintf(buf, "%d\n", enable); ++} ++ ++static ssize_t mma8451_enable_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client; ++ int ret; ++ unsigned long enable; ++ u8 val = 0; ++ ++ ret = strict_strtoul(buf, 10, &enable); ++ if (ret) { ++ dev_err(dev, "string transform error\n"); ++ return ret; ++ } ++ ++ mutex_lock(&mma8451_lock); ++ client = mma8451_i2c_client; ++ enable = (enable > 0) ? 1 : 0; ++ if (enable && mma_status.active == MMA_STANDBY) { ++ val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); ++ ret = ++ i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, ++ val | 0x01); ++ if (!ret) ++ mma_status.active = MMA_ACTIVED; ++ ++ } else if (enable == 0 && mma_status.active == MMA_ACTIVED) { ++ val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); ++ ret = ++ i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, ++ val & 0xFE); ++ if (!ret) ++ mma_status.active = MMA_STANDBY; ++ ++ } ++ mutex_unlock(&mma8451_lock); ++ return count; ++} ++ ++static ssize_t mma8451_position_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int position = 0; ++ mutex_lock(&mma8451_lock); ++ position = mma_status.position; ++ mutex_unlock(&mma8451_lock); ++ return sprintf(buf, "%d\n", position); ++} ++ ++static ssize_t mma8451_position_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ unsigned long position; ++ int ret; ++ ret = strict_strtoul(buf, 10, &position); ++ if (ret) { ++ dev_err(dev, "string transform error\n"); ++ return ret; ++ } ++ ++ mutex_lock(&mma8451_lock); ++ mma_status.position = (int)position; ++ mutex_unlock(&mma8451_lock); ++ return count; ++} ++ ++static ssize_t mma8451_scalemode_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int mode = 0; ++ mutex_lock(&mma8451_lock); ++ mode = (int)mma_status.mode; ++ mutex_unlock(&mma8451_lock); ++ ++ return sprintf(buf, "%d\n", mode); ++} ++ ++static ssize_t mma8451_scalemode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ unsigned long mode; ++ int ret, active_save; ++ struct i2c_client *client = mma8451_i2c_client; ++ ++ ret = strict_strtoul(buf, 10, &mode); ++ if (ret) { ++ dev_err(dev, "string transform error\n"); ++ goto out; ++ } ++ ++ if (mode > MODE_8G) { ++ dev_warn(dev, "not supported mode\n"); ++ ret = count; ++ goto out; ++ } ++ ++ mutex_lock(&mma8451_lock); ++ if (mode == mma_status.mode) { ++ ret = count; ++ goto out_unlock; ++ } ++ ++ active_save = mma_status.active; ++ ret = mma8451_change_mode(client, mode); ++ if (ret) ++ goto out_unlock; ++ ++ if (active_save == MMA_ACTIVED) { ++ ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 1); ++ ++ if (ret) ++ goto out_unlock; ++ mma_status.active = active_save; ++ } ++ ++out_unlock: ++ mutex_unlock(&mma8451_lock); ++out: ++ return ret; ++} ++ ++static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, ++ mma8451_enable_show, mma8451_enable_store); ++static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, ++ mma8451_position_show, mma8451_position_store); ++static DEVICE_ATTR(scalemode, S_IWUSR | S_IRUGO, ++ mma8451_scalemode_show, mma8451_scalemode_store); ++ ++static struct attribute *mma8451_attributes[] = { ++ &dev_attr_enable.attr, ++ &dev_attr_position.attr, ++ &dev_attr_scalemode.attr, ++ NULL ++}; ++ ++static const struct attribute_group mma8451_attr_group = { ++ .attrs = mma8451_attributes, ++}; ++ ++static int mma8451_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int result, client_id; ++ struct input_dev *idev; ++ struct i2c_adapter *adapter; ++ u32 pos; ++ struct device_node *of_node = client->dev.of_node; ++ struct regulator *vdd, *vdd_io; ++ ++ mma8451_i2c_client = client; ++ ++ vdd = devm_regulator_get(&client->dev, "vdd"); ++ if (!IS_ERR(vdd)) { ++ result = regulator_enable(vdd); ++ if (result) { ++ dev_err(&client->dev, "vdd set voltage error\n"); ++ return result; ++ } ++ } ++ ++ vdd_io = devm_regulator_get(&client->dev, "vddio"); ++ if (!IS_ERR(vdd_io)) { ++ result = regulator_enable(vdd_io); ++ if (result) { ++ dev_err(&client->dev, "vddio set voltage error\n"); ++ return result; ++ } ++ } ++ ++ adapter = to_i2c_adapter(client->dev.parent); ++ result = i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA); ++ if (!result) ++ goto err_out; ++ ++ client_id = i2c_smbus_read_byte_data(client, MMA8451_WHO_AM_I); ++ if (client_id != MMA8451_ID && client_id != MMA8452_ID ++ && client_id != MMA8453_ID) { ++ dev_err(&client->dev, ++ "read chip ID 0x%x is not equal to 0x%x or 0x%x!\n", ++ result, MMA8451_ID, MMA8452_ID); ++ result = -EINVAL; ++ goto err_out; ++ } ++ ++ /* Initialize the MMA8451 chip */ ++ result = mma8451_change_mode(client, senstive_mode); ++ if (result) { ++ dev_err(&client->dev, ++ "error when init mma8451 chip:(%d)\n", result); ++ goto err_out; ++ } ++ ++ hwmon_dev = hwmon_device_register(&client->dev); ++ if (!hwmon_dev) { ++ result = -ENOMEM; ++ dev_err(&client->dev, "error when register hwmon device\n"); ++ goto err_out; ++ } ++ ++ mma8451_idev = input_allocate_polled_device(); ++ if (!mma8451_idev) { ++ result = -ENOMEM; ++ dev_err(&client->dev, "alloc poll device failed!\n"); ++ goto err_alloc_poll_device; ++ } ++ mma8451_idev->poll = mma8451_dev_poll; ++ mma8451_idev->poll_interval = POLL_INTERVAL; ++ mma8451_idev->poll_interval_min = POLL_INTERVAL_MIN; ++ mma8451_idev->poll_interval_max = POLL_INTERVAL_MAX; ++ idev = mma8451_idev->input; ++ idev->name = "mma845x"; ++ idev->id.bustype = BUS_I2C; ++ idev->evbit[0] = BIT_MASK(EV_ABS); ++ ++ input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); ++ input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); ++ input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); ++ ++ result = input_register_polled_device(mma8451_idev); ++ if (result) { ++ dev_err(&client->dev, "register poll device failed!\n"); ++ goto err_register_polled_device; ++ } ++ result = sysfs_create_group(&idev->dev.kobj, &mma8451_attr_group); ++ if (result) { ++ dev_err(&client->dev, "create device file failed!\n"); ++ result = -EINVAL; ++ goto err_register_polled_device; ++ } ++ ++ result = of_property_read_u32(of_node, "position", &pos); ++ if (result) ++ pos = DEFAULT_POSITION; ++ mma_status.position = (int)pos; ++ ++ return 0; ++err_register_polled_device: ++ input_free_polled_device(mma8451_idev); ++err_alloc_poll_device: ++ hwmon_device_unregister(&client->dev); ++err_out: ++ return result; ++} ++ ++static int mma8451_stop_chip(struct i2c_client *client) ++{ ++ int ret = 0; ++ if (mma_status.active == MMA_ACTIVED) { ++ mma_status.ctl_reg1 = i2c_smbus_read_byte_data(client, ++ MMA8451_CTRL_REG1); ++ ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, ++ mma_status.ctl_reg1 & 0xFE); ++ } ++ return ret; ++} ++ ++static int mma8451_remove(struct i2c_client *client) ++{ ++ int ret; ++ ret = mma8451_stop_chip(client); ++ hwmon_device_unregister(hwmon_dev); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int mma8451_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ ++ return mma8451_stop_chip(client); ++} ++ ++static int mma8451_resume(struct device *dev) ++{ ++ int ret = 0; ++ struct i2c_client *client = to_i2c_client(dev); ++ if (mma_status.active == MMA_ACTIVED) ++ ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, ++ mma_status.ctl_reg1); ++ return ret; ++ ++} ++#endif ++ ++static const struct i2c_device_id mma8451_id[] = { ++ {"mma8451", 0}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, mma8451_id); ++ ++static SIMPLE_DEV_PM_OPS(mma8451_pm_ops, mma8451_suspend, mma8451_resume); ++static struct i2c_driver mma8451_driver = { ++ .driver = { ++ .name = "mma8451", ++ .owner = THIS_MODULE, ++ .pm = &mma8451_pm_ops, ++ }, ++ .probe = mma8451_probe, ++ .remove = mma8451_remove, ++ .id_table = mma8451_id, ++}; ++ ++static int __init mma8451_init(void) ++{ ++ /* register driver */ ++ int res; ++ ++ res = i2c_add_driver(&mma8451_driver); ++ if (res < 0) { ++ printk(KERN_INFO "add mma8451 i2c driver failed\n"); ++ return -ENODEV; ++ } ++ return res; ++} ++ ++static void __exit mma8451_exit(void) ++{ ++ i2c_del_driver(&mma8451_driver); ++} ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("MMA8451 3-Axis Orientation/Motion Detection Sensor driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(mma8451_init); ++module_exit(mma8451_exit); +diff -Nur linux-3.14.36/drivers/i2c/busses/i2c-imx.c linux-openelec/drivers/i2c/busses/i2c-imx.c +--- linux-3.14.36/drivers/i2c/busses/i2c-imx.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/i2c/busses/i2c-imx.c 2015-05-06 12:05:42.000000000 -0500 +@@ -184,6 +184,9 @@ + int stopped; + unsigned int ifdr; /* IMX_I2C_IFDR */ + const struct imx_i2c_hwdata *hwdata; ++ ++ unsigned int cur_clk; ++ unsigned int bitrate; + }; + + static const struct imx_i2c_hwdata imx1_i2c_hwdata = { +@@ -305,6 +308,51 @@ + return 0; + } + ++static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx) ++{ ++ struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div; ++ unsigned ndivs = i2c_imx->hwdata->ndivs; ++ unsigned int i2c_clk_rate; ++ unsigned int div; ++ int i; ++ ++ /* Divider value calculation */ ++ i2c_clk_rate = clk_get_rate(i2c_imx->clk); ++ if (i2c_imx->cur_clk == i2c_clk_rate) ++ return; ++ else ++ i2c_imx->cur_clk = i2c_clk_rate; ++ ++ div = (i2c_clk_rate + i2c_imx->bitrate - 1) / i2c_imx->bitrate; ++ if (div < i2c_clk_div[0].div) ++ i = 0; ++ else if (div > i2c_clk_div[ndivs - 1].div) ++ i = ndivs - 1; ++ else ++ for (i = 0; i2c_clk_div[i].div < div; i++) ++ ; ++ ++ /* Store divider value */ ++ i2c_imx->ifdr = imx_i2c_clk_div[i].val; ++ ++ /* ++ * There dummy delay is calculated. ++ * It should be about one I2C clock period long. ++ * This delay is used in I2C bus disable function ++ * to fix chip hardware bug. ++ */ ++ i2c_imx->disable_delay = (500000U * i2c_clk_div[i].div ++ + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); ++ ++ /* dev_dbg() can't be used, because adapter is not yet registered */ ++#ifdef CONFIG_I2C_DEBUG_BUS ++ dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", ++ __func__, i2c_clk_rate, div); ++ dev_dbg(&i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", ++ __func__, i2c_clk_div[i].val, i2c_clk_div[i].div); ++#endif ++} ++ + static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) + { + unsigned int temp = 0; +@@ -312,6 +360,7 @@ + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + ++ i2c_imx_set_clk(i2c_imx); + result = clk_prepare_enable(i2c_imx->clk); + if (result) + return result; +@@ -367,45 +416,6 @@ + clk_disable_unprepare(i2c_imx->clk); + } + +-static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, +- unsigned int rate) +-{ +- struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div; +- unsigned int i2c_clk_rate; +- unsigned int div; +- int i; +- +- /* Divider value calculation */ +- i2c_clk_rate = clk_get_rate(i2c_imx->clk); +- div = (i2c_clk_rate + rate - 1) / rate; +- if (div < i2c_clk_div[0].div) +- i = 0; +- else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div) +- i = i2c_imx->hwdata->ndivs - 1; +- else +- for (i = 0; i2c_clk_div[i].div < div; i++); +- +- /* Store divider value */ +- i2c_imx->ifdr = i2c_clk_div[i].val; +- +- /* +- * There dummy delay is calculated. +- * It should be about one I2C clock period long. +- * This delay is used in I2C bus disable function +- * to fix chip hardware bug. +- */ +- i2c_imx->disable_delay = (500000U * i2c_clk_div[i].div +- + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); +- +- /* dev_dbg() can't be used, because adapter is not yet registered */ +-#ifdef CONFIG_I2C_DEBUG_BUS +- dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", +- __func__, i2c_clk_rate, div); +- dev_dbg(&i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", +- __func__, i2c_clk_div[i].val, i2c_clk_div[i].div); +-#endif +-} +- + static irqreturn_t i2c_imx_isr(int irq, void *dev_id) + { + struct imx_i2c_struct *i2c_imx = dev_id; +@@ -600,7 +610,6 @@ + struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev); + void __iomem *base; + int irq, ret; +- u32 bitrate; + + dev_dbg(&pdev->dev, "<%s>\n", __func__); + +@@ -664,12 +673,12 @@ + i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); + + /* Set up clock divider */ +- bitrate = IMX_I2C_BIT_RATE; ++ i2c_imx->bitrate = IMX_I2C_BIT_RATE; + ret = of_property_read_u32(pdev->dev.of_node, +- "clock-frequency", &bitrate); ++ "clock-frequency", &i2c_imx->bitrate); + if (ret < 0 && pdata && pdata->bitrate) +- bitrate = pdata->bitrate; +- i2c_imx_set_clk(i2c_imx, bitrate); ++ i2c_imx->bitrate = pdata->bitrate; ++ i2c_imx_set_clk(i2c_imx); + + /* Set up chip registers to defaults */ + imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, +diff -Nur linux-3.14.36/drivers/input/joystick/xpad.c linux-openelec/drivers/input/joystick/xpad.c +--- linux-3.14.36/drivers/input/joystick/xpad.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/input/joystick/xpad.c 2015-07-24 18:03:30.064842002 -0500 +@@ -176,7 +176,6 @@ + { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, +- { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, + { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } + }; + +@@ -282,17 +281,21 @@ + struct urb *irq_out; /* urb for interrupt out report */ + unsigned char *odata; /* output data */ + dma_addr_t odata_dma; +- struct mutex odata_mutex; ++ spinlock_t odata_lock; + #endif + + #if defined(CONFIG_JOYSTICK_XPAD_LEDS) + struct xpad_led *led; + #endif ++ ++ int joydev_id; + + char phys[64]; /* physical device path */ + + int mapping; /* map d-pad to buttons or to axes */ + int xtype; /* type of xbox device */ ++ ++ const char *name; + }; + + /* +@@ -436,6 +439,109 @@ + + input_sync(dev); + } ++static void xpad_send_led_command(struct usb_xpad *xpad, int command); ++static int xpad_open(struct input_dev *dev); ++static void xpad_close(struct input_dev *dev); ++static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs); ++static int xpad_init_ff(struct usb_xpad *xpad); ++static int xpad_find_joydev(struct device *dev, void *data) ++{ ++ if (strstr(dev_name(dev), "js")) ++ return 1; ++ ++ return 0; ++} ++ ++static struct workqueue_struct *my_wq; ++ ++typedef struct { ++ struct work_struct my_work; ++ struct usb_xpad *xpad; ++} my_work_t; ++ ++static void my_wq_function( struct work_struct *work) ++{ ++ my_work_t *my_work = (my_work_t *)work; ++ ++ struct usb_xpad *xpad = my_work->xpad; ++ ++ if (xpad->pad_present) { ++ ++ struct input_dev *input_dev; ++ int i; ++ ++ input_dev = input_allocate_device(); ++ ++ xpad->dev = input_dev; ++ input_dev->name = xpad->name; ++ input_dev->phys = xpad->phys; ++ usb_to_input_id(xpad->udev, &input_dev->id); ++ input_dev->dev.parent = &xpad->intf->dev; ++ ++ input_set_drvdata(input_dev, xpad); ++ ++ input_dev->open = xpad_open; ++ input_dev->close = xpad_close; ++ ++ input_dev->evbit[0] = BIT_MASK(EV_KEY); ++ ++ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { ++ input_dev->evbit[0] |= BIT_MASK(EV_ABS); ++ /* set up axes */ ++ for (i = 0; xpad_abs[i] >= 0; i++) ++ xpad_set_up_abs(input_dev, xpad_abs[i]); ++ } ++ ++ /* set up standard buttons */ ++ for (i = 0; xpad_common_btn[i] >= 0; i++) ++ __set_bit(xpad_common_btn[i], input_dev->keybit); ++ ++ /* set up model-specific ones */ ++ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) { ++ for (i = 0; xpad360_btn[i] >= 0; i++) ++ __set_bit(xpad360_btn[i], input_dev->keybit); ++ } else { ++ for (i = 0; xpad_btn[i] >= 0; i++) ++ __set_bit(xpad_btn[i], input_dev->keybit); ++ } ++ ++ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { ++ for (i = 0; xpad_btn_pad[i] >= 0; i++) ++ __set_bit(xpad_btn_pad[i], input_dev->keybit); ++ } else { ++ for (i = 0; xpad_abs_pad[i] >= 0; i++) ++ xpad_set_up_abs(input_dev, xpad_abs_pad[i]); ++ } ++ ++ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { ++ for (i = 0; xpad_btn_triggers[i] >= 0; i++) ++ __set_bit(xpad_btn_triggers[i], input_dev->keybit); ++ } else { ++ for (i = 0; xpad_abs_triggers[i] >= 0; i++) ++ xpad_set_up_abs(input_dev, xpad_abs_triggers[i]); ++ } ++ ++ input_register_device(xpad->dev); ++ ++ { ++ struct device* joydev_dev = device_find_child(&xpad->dev->dev, NULL, xpad_find_joydev); ++ ++ if (joydev_dev) { ++// printk("found joydev child with minor %i\n", MINOR(joydev_dev->devt)); ++ xpad->joydev_id = MINOR(joydev_dev->devt); ++ xpad_send_led_command(xpad, (xpad->joydev_id % 4) + 2); ++ } ++ } ++ ++ xpad_init_ff(xpad); ++ } else { ++ input_unregister_device(xpad->dev); ++ } ++ ++ kfree( (void *)work ); ++ ++ return; ++} + + /* + * xpad360w_process_packet +@@ -457,11 +563,35 @@ + /* Presence change */ + if (data[0] & 0x08) { + if (data[1] & 0x80) { +- xpad->pad_present = 1; +- usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); +- } else +- xpad->pad_present = 0; ++ ++ if (!xpad->pad_present) ++ { ++ my_work_t * work; ++ xpad->pad_present = 1; ++ usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); ++ ++ work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL); ++ INIT_WORK( (struct work_struct *)work, my_wq_function ); ++ work->xpad = xpad; ++ queue_work( my_wq, (struct work_struct *)work ); ++ } ++ ++ } else { ++ if (xpad->pad_present) ++ { ++ my_work_t * work; ++ xpad->pad_present = 0; ++ ++ work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL); ++ INIT_WORK( (struct work_struct *)work, my_wq_function ); ++ work->xpad = xpad; ++ queue_work( my_wq, (struct work_struct *)work ); ++ } ++// printk("got kill packet for id %i\n", xpad->joydev_id); ++ } + } ++ ++// printk("xpad packet %hhX %hhX %hhX %hhX %hhX %hhX\n", data[0], data[1], data[2], data[3], data[4], data[5]); + + /* Valid pad data */ + if (!(data[1] & 0x1)) +@@ -477,6 +607,8 @@ + int retval, status; + + status = urb->status; ++ ++// printk("xpad_irq_in %i\n", status); + + switch (status) { + case 0: +@@ -585,8 +717,6 @@ + goto fail1; + } + +- mutex_init(&xpad->odata_mutex); +- + xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_out) { + error = -ENOMEM; +@@ -715,15 +845,38 @@ + + static void xpad_send_led_command(struct usb_xpad *xpad, int command) + { +- if (command >= 0 && command < 14) { +- mutex_lock(&xpad->odata_mutex); +- xpad->odata[0] = 0x01; +- xpad->odata[1] = 0x03; +- xpad->odata[2] = command; +- xpad->irq_out->transfer_buffer_length = 3; +- usb_submit_urb(xpad->irq_out, GFP_KERNEL); +- mutex_unlock(&xpad->odata_mutex); ++ if ((unsigned)command > 15) ++ return; ++ ++ spin_lock(&xpad->odata_lock); ++ ++ switch (xpad->xtype) { ++ ++ case XTYPE_XBOX360: ++ xpad->odata[0] = 0x01; ++ xpad->odata[1] = 0x03; ++ xpad->odata[2] = command; ++ xpad->irq_out->transfer_buffer_length = 3; ++ break; ++ case XTYPE_XBOX360W: ++ xpad->odata[0] = 0x00; ++ xpad->odata[1] = 0x00; ++ xpad->odata[2] = 0x08; ++ xpad->odata[3] = 0x40 + (command % 0x0e); ++ xpad->odata[4] = 0x00; ++ xpad->odata[5] = 0x00; ++ xpad->odata[6] = 0x00; ++ xpad->odata[7] = 0x00; ++ xpad->odata[8] = 0x00; ++ xpad->odata[9] = 0x00; ++ xpad->odata[10] = 0x00; ++ xpad->odata[11] = 0x00; ++ xpad->irq_out->transfer_buffer_length = 12; ++ break; + } ++ ++ usb_submit_urb(xpad->irq_out, GFP_KERNEL); ++ spin_unlock(&xpad->odata_lock); + } + + static void xpad_led_set(struct led_classdev *led_cdev, +@@ -742,8 +895,10 @@ + struct xpad_led *led; + struct led_classdev *led_cdev; + int error; ++ ++// printk("xpad_led_probe\n"); + +- if (xpad->xtype != XTYPE_XBOX360) ++ if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W) + return 0; + + xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); +@@ -766,11 +921,6 @@ + return error; + } + +- /* +- * Light up the segment corresponding to controller number +- */ +- xpad_send_led_command(xpad, (led_no % 4) + 2); +- + return 0; + } + +@@ -792,6 +942,7 @@ + static int xpad_open(struct input_dev *dev) + { + struct usb_xpad *xpad = input_get_drvdata(dev); ++// printk("xpad open driver data %x\n", (unsigned int)xpad); + + /* URB was submitted in probe */ + if (xpad->xtype == XTYPE_XBOX360W) +@@ -840,23 +991,24 @@ + { + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_xpad *xpad; +- struct input_dev *input_dev; + struct usb_endpoint_descriptor *ep_irq_in; + int i, error; ++ struct input_dev *input_dev; ++ ++ if (!my_wq) { ++ my_wq = create_workqueue("xpad_queue"); ++ } + + for (i = 0; xpad_device[i].idVendor; i++) { + if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && + (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) + break; + } +- ++ + xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL); +- input_dev = input_allocate_device(); +- if (!xpad || !input_dev) { +- error = -ENOMEM; +- goto fail1; +- } + ++ xpad->name = xpad_device[i].name; ++ + xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN, + GFP_KERNEL, &xpad->idata_dma); + if (!xpad->idata) { +@@ -892,65 +1044,12 @@ + xpad->mapping |= MAP_STICKS_TO_NULL; + } + +- xpad->dev = input_dev; +- usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); +- strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); +- +- input_dev->name = xpad_device[i].name; +- input_dev->phys = xpad->phys; +- usb_to_input_id(udev, &input_dev->id); +- input_dev->dev.parent = &intf->dev; +- +- input_set_drvdata(input_dev, xpad); +- +- input_dev->open = xpad_open; +- input_dev->close = xpad_close; +- +- input_dev->evbit[0] = BIT_MASK(EV_KEY); +- +- if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { +- input_dev->evbit[0] |= BIT_MASK(EV_ABS); +- /* set up axes */ +- for (i = 0; xpad_abs[i] >= 0; i++) +- xpad_set_up_abs(input_dev, xpad_abs[i]); +- } +- +- /* set up standard buttons */ +- for (i = 0; xpad_common_btn[i] >= 0; i++) +- __set_bit(xpad_common_btn[i], input_dev->keybit); +- +- /* set up model-specific ones */ +- if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) { +- for (i = 0; xpad360_btn[i] >= 0; i++) +- __set_bit(xpad360_btn[i], input_dev->keybit); +- } else { +- for (i = 0; xpad_btn[i] >= 0; i++) +- __set_bit(xpad_btn[i], input_dev->keybit); +- } +- +- if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { +- for (i = 0; xpad_btn_pad[i] >= 0; i++) +- __set_bit(xpad_btn_pad[i], input_dev->keybit); +- } else { +- for (i = 0; xpad_abs_pad[i] >= 0; i++) +- xpad_set_up_abs(input_dev, xpad_abs_pad[i]); +- } +- +- if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { +- for (i = 0; xpad_btn_triggers[i] >= 0; i++) +- __set_bit(xpad_btn_triggers[i], input_dev->keybit); +- } else { +- for (i = 0; xpad_abs_triggers[i] >= 0; i++) +- xpad_set_up_abs(input_dev, xpad_abs_triggers[i]); +- } +- + error = xpad_init_output(intf, xpad); + if (error) + goto fail3; + +- error = xpad_init_ff(xpad); +- if (error) +- goto fail4; ++ usb_make_path(xpad->udev, xpad->phys, sizeof(xpad->phys)); ++ strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); + + error = xpad_led_probe(xpad); + if (error) +@@ -964,10 +1063,6 @@ + xpad->irq_in->transfer_dma = xpad->idata_dma; + xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + +- error = input_register_device(xpad->dev); +- if (error) +- goto fail6; +- + usb_set_intfdata(intf, xpad); + + if (xpad->xtype == XTYPE_XBOX360W) { +@@ -975,6 +1070,7 @@ + * Setup the message to set the LEDs on the + * controller when it shows up + */ ++ spin_lock(&xpad->odata_lock); + xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->bulk_out) { + error = -ENOMEM; +@@ -1026,23 +1122,55 @@ + */ + xpad->irq_in->dev = xpad->udev; + error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); ++ ++ spin_unlock(&xpad->odata_lock); + if (error) + goto fail9; ++ ++ // I don't know how to check controller state on driver load so just slam them ++ // off so that people have to turn them on, triggering a state update ++ ++ // got the power off packet from an OSX reverse-engineered driver: ++ // http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/OsxDriver#toc1 ++ spin_lock(&xpad->odata_lock); ++ xpad->odata[0] = 0x00; ++ xpad->odata[1] = 0x00; ++ xpad->odata[2] = 0x08; ++ xpad->odata[3] = 0xC0; ++ xpad->odata[4] = 0x00; ++ xpad->odata[5] = 0x00; ++ xpad->odata[6] = 0x00; ++ xpad->odata[7] = 0x00; ++ xpad->odata[8] = 0x00; ++ xpad->odata[9] = 0x00; ++ xpad->odata[10] = 0x00; ++ xpad->odata[11] = 0x00; ++ xpad->irq_out->transfer_buffer_length = 12; ++ usb_submit_urb(xpad->irq_out, GFP_KERNEL); ++ spin_unlock(&xpad->odata_lock); ++ } else { ++ my_work_t *work; ++ xpad->pad_present = 1; ++ ++ work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL); ++ INIT_WORK( (struct work_struct *)work, my_wq_function ); ++ work->xpad = xpad; ++ queue_work( my_wq, (struct work_struct *)work ); + } + + return 0; + + fail9: kfree(xpad->bdata); + fail8: usb_free_urb(xpad->bulk_out); +- fail7: input_unregister_device(input_dev); +- input_dev = NULL; ++ fail7: //input_unregister_device(input_dev); ++ //input_dev = NULL; + fail6: xpad_led_disconnect(xpad); +- fail5: if (input_dev) +- input_ff_destroy(input_dev); ++ fail5: //if (input_dev) ++ //input_ff_destroy(input_dev); + fail4: xpad_deinit_output(xpad); + fail3: usb_free_urb(xpad->irq_in); + fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); +- fail1: input_free_device(input_dev); ++ fail1: //input_free_device(input_dev); + kfree(xpad); + return error; + +@@ -1052,8 +1180,14 @@ + { + struct usb_xpad *xpad = usb_get_intfdata (intf); + ++// printk("xpad_disconnect\n"); + xpad_led_disconnect(xpad); +- input_unregister_device(xpad->dev); ++ ++ if (xpad->pad_present) ++ { ++ xpad->pad_present = 0; ++ input_unregister_device(xpad->dev); ++ } + xpad_deinit_output(xpad); + + if (xpad->xtype == XTYPE_XBOX360W) { +diff -Nur linux-3.14.36/drivers/input/joystick/xpad.c.orig linux-openelec/drivers/input/joystick/xpad.c.orig +--- linux-3.14.36/drivers/input/joystick/xpad.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/input/joystick/xpad.c.orig 2015-07-24 18:03:30.056842002 -0500 +@@ -0,0 +1,1085 @@ ++/* ++ * X-Box gamepad driver ++ * ++ * Copyright (c) 2002 Marko Friedemann ++ * 2004 Oliver Schwartz , ++ * Steven Toth , ++ * Franz Lehner , ++ * Ivan Hawkes ++ * 2005 Dominic Cerquetti ++ * 2006 Adam Buchbinder ++ * 2007 Jan Kratochvil ++ * 2010 Christoph Fritz ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ++ * This driver is based on: ++ * - information from http://euc.jp/periphs/xbox-controller.ja.html ++ * - the iForce driver drivers/char/joystick/iforce.c ++ * - the skeleton-driver drivers/usb/usb-skeleton.c ++ * - Xbox 360 information http://www.free60.org/wiki/Gamepad ++ * ++ * Thanks to: ++ * - ITO Takayuki for providing essential xpad information on his website ++ * - Vojtech Pavlik - iforce driver / input subsystem ++ * - Greg Kroah-Hartman - usb-skeleton driver ++ * - XBOX Linux project - extra USB id's ++ * ++ * TODO: ++ * - fine tune axes (especially trigger axes) ++ * - fix "analog" buttons (reported as digital now) ++ * - get rumble working ++ * - need USB IDs for other dance pads ++ * ++ * History: ++ * ++ * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller" ++ * ++ * 2002-07-02 - 0.0.2 : basic working version ++ * - all axes and 9 of the 10 buttons work (german InterAct device) ++ * - the black button does not work ++ * ++ * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik ++ * - indentation fixes ++ * - usb + input init sequence fixes ++ * ++ * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3 ++ * - verified the lack of HID and report descriptors ++ * - verified that ALL buttons WORK ++ * - fixed d-pad to axes mapping ++ * ++ * 2002-07-17 - 0.0.5 : simplified d-pad handling ++ * ++ * 2004-10-02 - 0.0.6 : DDR pad support ++ * - borrowed from the XBOX linux kernel ++ * - USB id's for commonly used dance pads are present ++ * - dance pads will map D-PAD to buttons, not axes ++ * - pass the module paramater 'dpad_to_buttons' to force ++ * the D-PAD to map to buttons if your pad is not detected ++ * ++ * Later changes can be tracked in SCM. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_AUTHOR "Marko Friedemann " ++#define DRIVER_DESC "X-Box pad driver" ++ ++#define XPAD_PKT_LEN 32 ++ ++/* xbox d-pads should map to buttons, as is required for DDR pads ++ but we map them to axes when possible to simplify things */ ++#define MAP_DPAD_TO_BUTTONS (1 << 0) ++#define MAP_TRIGGERS_TO_BUTTONS (1 << 1) ++#define MAP_STICKS_TO_NULL (1 << 2) ++#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \ ++ MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL) ++ ++#define XTYPE_XBOX 0 ++#define XTYPE_XBOX360 1 ++#define XTYPE_XBOX360W 2 ++#define XTYPE_UNKNOWN 3 ++ ++static bool dpad_to_buttons; ++module_param(dpad_to_buttons, bool, S_IRUGO); ++MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); ++ ++static bool triggers_to_buttons; ++module_param(triggers_to_buttons, bool, S_IRUGO); ++MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads"); ++ ++static bool sticks_to_null; ++module_param(sticks_to_null, bool, S_IRUGO); ++MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads"); ++ ++static const struct xpad_device { ++ u16 idVendor; ++ u16 idProduct; ++ char *name; ++ u8 mapping; ++ u8 xtype; ++} xpad_device[] = { ++ { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX }, ++ { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX }, ++ { 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX }, ++ { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX }, ++ { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 }, ++ { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, ++ { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, ++ { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, ++ { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, ++ { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 }, ++ { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, ++ { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, ++ { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", 0, XTYPE_XBOX }, ++ { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", 0, XTYPE_XBOX }, ++ { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", 0, XTYPE_XBOX }, ++ { 0x0738, 0x4516, "Mad Catz Control Pad", 0, XTYPE_XBOX }, ++ { 0x0738, 0x4522, "Mad Catz LumiCON", 0, XTYPE_XBOX }, ++ { 0x0738, 0x4526, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX }, ++ { 0x0738, 0x4536, "Mad Catz MicroCON", 0, XTYPE_XBOX }, ++ { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, ++ { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX }, ++ { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, ++ { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, ++ { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, ++ { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, ++ { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 }, ++ { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, ++ { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, ++ { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX }, ++ { 0x0c12, 0x8810, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, ++ { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", 0, XTYPE_XBOX }, ++ { 0x0d2f, 0x0002, "Andamiro Pump It Up pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, ++ { 0x0e4c, 0x1097, "Radica Gamester Controller", 0, XTYPE_XBOX }, ++ { 0x0e4c, 0x2390, "Radica Games Jtech Controller", 0, XTYPE_XBOX }, ++ { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", 0, XTYPE_XBOX }, ++ { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX }, ++ { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX }, ++ { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, ++ { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, ++ { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, ++ { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX }, ++ { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, ++ { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, ++ { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, ++ { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, ++ { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX }, ++ { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, ++ { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, ++ { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, ++ { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, ++ { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, ++ { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 }, ++ { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 }, ++ { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, ++ { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, ++ { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, ++ { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 }, ++ { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 }, ++ { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 }, ++ { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, ++ { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } ++}; ++ ++/* buttons shared with xbox and xbox360 */ ++static const signed short xpad_common_btn[] = { ++ BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */ ++ BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ ++ -1 /* terminating entry */ ++}; ++ ++/* original xbox controllers only */ ++static const signed short xpad_btn[] = { ++ BTN_C, BTN_Z, /* "analog" buttons */ ++ -1 /* terminating entry */ ++}; ++ ++/* used when dpad is mapped to buttons */ ++static const signed short xpad_btn_pad[] = { ++ BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */ ++ BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */ ++ -1 /* terminating entry */ ++}; ++ ++/* used when triggers are mapped to buttons */ ++static const signed short xpad_btn_triggers[] = { ++ BTN_TL2, BTN_TR2, /* triggers left/right */ ++ -1 ++}; ++ ++ ++static const signed short xpad360_btn[] = { /* buttons for x360 controller */ ++ BTN_TL, BTN_TR, /* Button LB/RB */ ++ BTN_MODE, /* The big X button */ ++ -1 ++}; ++ ++static const signed short xpad_abs[] = { ++ ABS_X, ABS_Y, /* left stick */ ++ ABS_RX, ABS_RY, /* right stick */ ++ -1 /* terminating entry */ ++}; ++ ++/* used when dpad is mapped to axes */ ++static const signed short xpad_abs_pad[] = { ++ ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */ ++ -1 /* terminating entry */ ++}; ++ ++/* used when triggers are mapped to axes */ ++static const signed short xpad_abs_triggers[] = { ++ ABS_Z, ABS_RZ, /* triggers left/right */ ++ -1 ++}; ++ ++/* Xbox 360 has a vendor-specific class, so we cannot match it with only ++ * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we ++ * match against vendor id as well. Wired Xbox 360 devices have protocol 1, ++ * wireless controllers have protocol 129. */ ++#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \ ++ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ ++ .idVendor = (vend), \ ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ ++ .bInterfaceSubClass = 93, \ ++ .bInterfaceProtocol = (pr) ++#define XPAD_XBOX360_VENDOR(vend) \ ++ { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \ ++ { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) } ++ ++static struct usb_device_id xpad_table[] = { ++ { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ ++ XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ ++ XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ ++ XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ ++ { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ ++ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ ++ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */ ++ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ ++ XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ ++ XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */ ++ XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ ++ XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ ++ XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(usb, xpad_table); ++ ++struct usb_xpad { ++ struct input_dev *dev; /* input device interface */ ++ struct usb_device *udev; /* usb device */ ++ struct usb_interface *intf; /* usb interface */ ++ ++ int pad_present; ++ ++ struct urb *irq_in; /* urb for interrupt in report */ ++ unsigned char *idata; /* input data */ ++ dma_addr_t idata_dma; ++ ++ struct urb *bulk_out; ++ unsigned char *bdata; ++ ++#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) ++ struct urb *irq_out; /* urb for interrupt out report */ ++ unsigned char *odata; /* output data */ ++ dma_addr_t odata_dma; ++ struct mutex odata_mutex; ++#endif ++ ++#if defined(CONFIG_JOYSTICK_XPAD_LEDS) ++ struct xpad_led *led; ++#endif ++ ++ char phys[64]; /* physical device path */ ++ ++ int mapping; /* map d-pad to buttons or to axes */ ++ int xtype; /* type of xbox device */ ++}; ++ ++/* ++ * xpad_process_packet ++ * ++ * Completes a request by converting the data into events for the ++ * input subsystem. ++ * ++ * The used report descriptor was taken from ITO Takayukis website: ++ * http://euc.jp/periphs/xbox-controller.ja.html ++ */ ++ ++static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) ++{ ++ struct input_dev *dev = xpad->dev; ++ ++ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { ++ /* left stick */ ++ input_report_abs(dev, ABS_X, ++ (__s16) le16_to_cpup((__le16 *)(data + 12))); ++ input_report_abs(dev, ABS_Y, ++ ~(__s16) le16_to_cpup((__le16 *)(data + 14))); ++ ++ /* right stick */ ++ input_report_abs(dev, ABS_RX, ++ (__s16) le16_to_cpup((__le16 *)(data + 16))); ++ input_report_abs(dev, ABS_RY, ++ ~(__s16) le16_to_cpup((__le16 *)(data + 18))); ++ } ++ ++ /* triggers left/right */ ++ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { ++ input_report_key(dev, BTN_TL2, data[10]); ++ input_report_key(dev, BTN_TR2, data[11]); ++ } else { ++ input_report_abs(dev, ABS_Z, data[10]); ++ input_report_abs(dev, ABS_RZ, data[11]); ++ } ++ ++ /* digital pad */ ++ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { ++ /* dpad as buttons (left, right, up, down) */ ++ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); ++ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); ++ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); ++ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); ++ } else { ++ input_report_abs(dev, ABS_HAT0X, ++ !!(data[2] & 0x08) - !!(data[2] & 0x04)); ++ input_report_abs(dev, ABS_HAT0Y, ++ !!(data[2] & 0x02) - !!(data[2] & 0x01)); ++ } ++ ++ /* start/back buttons and stick press left/right */ ++ input_report_key(dev, BTN_START, data[2] & 0x10); ++ input_report_key(dev, BTN_SELECT, data[2] & 0x20); ++ input_report_key(dev, BTN_THUMBL, data[2] & 0x40); ++ input_report_key(dev, BTN_THUMBR, data[2] & 0x80); ++ ++ /* "analog" buttons A, B, X, Y */ ++ input_report_key(dev, BTN_A, data[4]); ++ input_report_key(dev, BTN_B, data[5]); ++ input_report_key(dev, BTN_X, data[6]); ++ input_report_key(dev, BTN_Y, data[7]); ++ ++ /* "analog" buttons black, white */ ++ input_report_key(dev, BTN_C, data[8]); ++ input_report_key(dev, BTN_Z, data[9]); ++ ++ input_sync(dev); ++} ++ ++/* ++ * xpad360_process_packet ++ * ++ * Completes a request by converting the data into events for the ++ * input subsystem. It is version for xbox 360 controller ++ * ++ * The used report descriptor was taken from: ++ * http://www.free60.org/wiki/Gamepad ++ */ ++ ++static void xpad360_process_packet(struct usb_xpad *xpad, ++ u16 cmd, unsigned char *data) ++{ ++ struct input_dev *dev = xpad->dev; ++ ++ /* digital pad */ ++ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { ++ /* dpad as buttons (left, right, up, down) */ ++ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); ++ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); ++ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); ++ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); ++ } else { ++ input_report_abs(dev, ABS_HAT0X, ++ !!(data[2] & 0x08) - !!(data[2] & 0x04)); ++ input_report_abs(dev, ABS_HAT0Y, ++ !!(data[2] & 0x02) - !!(data[2] & 0x01)); ++ } ++ ++ /* start/back buttons */ ++ input_report_key(dev, BTN_START, data[2] & 0x10); ++ input_report_key(dev, BTN_SELECT, data[2] & 0x20); ++ ++ /* stick press left/right */ ++ input_report_key(dev, BTN_THUMBL, data[2] & 0x40); ++ input_report_key(dev, BTN_THUMBR, data[2] & 0x80); ++ ++ /* buttons A,B,X,Y,TL,TR and MODE */ ++ input_report_key(dev, BTN_A, data[3] & 0x10); ++ input_report_key(dev, BTN_B, data[3] & 0x20); ++ input_report_key(dev, BTN_X, data[3] & 0x40); ++ input_report_key(dev, BTN_Y, data[3] & 0x80); ++ input_report_key(dev, BTN_TL, data[3] & 0x01); ++ input_report_key(dev, BTN_TR, data[3] & 0x02); ++ input_report_key(dev, BTN_MODE, data[3] & 0x04); ++ ++ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { ++ /* left stick */ ++ input_report_abs(dev, ABS_X, ++ (__s16) le16_to_cpup((__le16 *)(data + 6))); ++ input_report_abs(dev, ABS_Y, ++ ~(__s16) le16_to_cpup((__le16 *)(data + 8))); ++ ++ /* right stick */ ++ input_report_abs(dev, ABS_RX, ++ (__s16) le16_to_cpup((__le16 *)(data + 10))); ++ input_report_abs(dev, ABS_RY, ++ ~(__s16) le16_to_cpup((__le16 *)(data + 12))); ++ } ++ ++ /* triggers left/right */ ++ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { ++ input_report_key(dev, BTN_TL2, data[4]); ++ input_report_key(dev, BTN_TR2, data[5]); ++ } else { ++ input_report_abs(dev, ABS_Z, data[4]); ++ input_report_abs(dev, ABS_RZ, data[5]); ++ } ++ ++ input_sync(dev); ++} ++ ++/* ++ * xpad360w_process_packet ++ * ++ * Completes a request by converting the data into events for the ++ * input subsystem. It is version for xbox 360 wireless controller. ++ * ++ * Byte.Bit ++ * 00.1 - Status change: The controller or headset has connected/disconnected ++ * Bits 01.7 and 01.6 are valid ++ * 01.7 - Controller present ++ * 01.6 - Headset present ++ * 01.1 - Pad state (Bytes 4+) valid ++ * ++ */ ++ ++static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) ++{ ++ /* Presence change */ ++ if (data[0] & 0x08) { ++ if (data[1] & 0x80) { ++ xpad->pad_present = 1; ++ usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); ++ } else ++ xpad->pad_present = 0; ++ } ++ ++ /* Valid pad data */ ++ if (!(data[1] & 0x1)) ++ return; ++ ++ xpad360_process_packet(xpad, cmd, &data[4]); ++} ++ ++static void xpad_irq_in(struct urb *urb) ++{ ++ struct usb_xpad *xpad = urb->context; ++ struct device *dev = &xpad->intf->dev; ++ int retval, status; ++ ++ status = urb->status; ++ ++ switch (status) { ++ case 0: ++ /* success */ ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* this urb is terminated, clean up */ ++ dev_dbg(dev, "%s - urb shutting down with status: %d\n", ++ __func__, status); ++ return; ++ default: ++ dev_dbg(dev, "%s - nonzero urb status received: %d\n", ++ __func__, status); ++ goto exit; ++ } ++ ++ switch (xpad->xtype) { ++ case XTYPE_XBOX360: ++ xpad360_process_packet(xpad, 0, xpad->idata); ++ break; ++ case XTYPE_XBOX360W: ++ xpad360w_process_packet(xpad, 0, xpad->idata); ++ break; ++ default: ++ xpad_process_packet(xpad, 0, xpad->idata); ++ } ++ ++exit: ++ retval = usb_submit_urb(urb, GFP_ATOMIC); ++ if (retval) ++ dev_err(dev, "%s - usb_submit_urb failed with result %d\n", ++ __func__, retval); ++} ++ ++static void xpad_bulk_out(struct urb *urb) ++{ ++ struct usb_xpad *xpad = urb->context; ++ struct device *dev = &xpad->intf->dev; ++ ++ switch (urb->status) { ++ case 0: ++ /* success */ ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* this urb is terminated, clean up */ ++ dev_dbg(dev, "%s - urb shutting down with status: %d\n", ++ __func__, urb->status); ++ break; ++ default: ++ dev_dbg(dev, "%s - nonzero urb status received: %d\n", ++ __func__, urb->status); ++ } ++} ++ ++#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) ++static void xpad_irq_out(struct urb *urb) ++{ ++ struct usb_xpad *xpad = urb->context; ++ struct device *dev = &xpad->intf->dev; ++ int retval, status; ++ ++ status = urb->status; ++ ++ switch (status) { ++ case 0: ++ /* success */ ++ return; ++ ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* this urb is terminated, clean up */ ++ dev_dbg(dev, "%s - urb shutting down with status: %d\n", ++ __func__, status); ++ return; ++ ++ default: ++ dev_dbg(dev, "%s - nonzero urb status received: %d\n", ++ __func__, status); ++ goto exit; ++ } ++ ++exit: ++ retval = usb_submit_urb(urb, GFP_ATOMIC); ++ if (retval) ++ dev_err(dev, "%s - usb_submit_urb failed with result %d\n", ++ __func__, retval); ++} ++ ++static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) ++{ ++ struct usb_endpoint_descriptor *ep_irq_out; ++ int error; ++ ++ if (xpad->xtype == XTYPE_UNKNOWN) ++ return 0; ++ ++ xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, ++ GFP_KERNEL, &xpad->odata_dma); ++ if (!xpad->odata) { ++ error = -ENOMEM; ++ goto fail1; ++ } ++ ++ mutex_init(&xpad->odata_mutex); ++ ++ xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); ++ if (!xpad->irq_out) { ++ error = -ENOMEM; ++ goto fail2; ++ } ++ ++ ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; ++ usb_fill_int_urb(xpad->irq_out, xpad->udev, ++ usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), ++ xpad->odata, XPAD_PKT_LEN, ++ xpad_irq_out, xpad, ep_irq_out->bInterval); ++ xpad->irq_out->transfer_dma = xpad->odata_dma; ++ xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ return 0; ++ ++ fail2: usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); ++ fail1: return error; ++} ++ ++static void xpad_stop_output(struct usb_xpad *xpad) ++{ ++ if (xpad->xtype != XTYPE_UNKNOWN) ++ usb_kill_urb(xpad->irq_out); ++} ++ ++static void xpad_deinit_output(struct usb_xpad *xpad) ++{ ++ if (xpad->xtype != XTYPE_UNKNOWN) { ++ usb_free_urb(xpad->irq_out); ++ usb_free_coherent(xpad->udev, XPAD_PKT_LEN, ++ xpad->odata, xpad->odata_dma); ++ } ++} ++#else ++static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } ++static void xpad_deinit_output(struct usb_xpad *xpad) {} ++static void xpad_stop_output(struct usb_xpad *xpad) {} ++#endif ++ ++#ifdef CONFIG_JOYSTICK_XPAD_FF ++static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) ++{ ++ struct usb_xpad *xpad = input_get_drvdata(dev); ++ ++ if (effect->type == FF_RUMBLE) { ++ __u16 strong = effect->u.rumble.strong_magnitude; ++ __u16 weak = effect->u.rumble.weak_magnitude; ++ ++ switch (xpad->xtype) { ++ ++ case XTYPE_XBOX: ++ xpad->odata[0] = 0x00; ++ xpad->odata[1] = 0x06; ++ xpad->odata[2] = 0x00; ++ xpad->odata[3] = strong / 256; /* left actuator */ ++ xpad->odata[4] = 0x00; ++ xpad->odata[5] = weak / 256; /* right actuator */ ++ xpad->irq_out->transfer_buffer_length = 6; ++ ++ return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); ++ ++ case XTYPE_XBOX360: ++ xpad->odata[0] = 0x00; ++ xpad->odata[1] = 0x08; ++ xpad->odata[2] = 0x00; ++ xpad->odata[3] = strong / 256; /* left actuator? */ ++ xpad->odata[4] = weak / 256; /* right actuator? */ ++ xpad->odata[5] = 0x00; ++ xpad->odata[6] = 0x00; ++ xpad->odata[7] = 0x00; ++ xpad->irq_out->transfer_buffer_length = 8; ++ ++ return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); ++ ++ case XTYPE_XBOX360W: ++ xpad->odata[0] = 0x00; ++ xpad->odata[1] = 0x01; ++ xpad->odata[2] = 0x0F; ++ xpad->odata[3] = 0xC0; ++ xpad->odata[4] = 0x00; ++ xpad->odata[5] = strong / 256; ++ xpad->odata[6] = weak / 256; ++ xpad->odata[7] = 0x00; ++ xpad->odata[8] = 0x00; ++ xpad->odata[9] = 0x00; ++ xpad->odata[10] = 0x00; ++ xpad->odata[11] = 0x00; ++ xpad->irq_out->transfer_buffer_length = 12; ++ ++ return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); ++ ++ default: ++ dev_dbg(&xpad->dev->dev, ++ "%s - rumble command sent to unsupported xpad type: %d\n", ++ __func__, xpad->xtype); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int xpad_init_ff(struct usb_xpad *xpad) ++{ ++ if (xpad->xtype == XTYPE_UNKNOWN) ++ return 0; ++ ++ input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); ++ ++ return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); ++} ++ ++#else ++static int xpad_init_ff(struct usb_xpad *xpad) { return 0; } ++#endif ++ ++#if defined(CONFIG_JOYSTICK_XPAD_LEDS) ++#include ++ ++struct xpad_led { ++ char name[16]; ++ struct led_classdev led_cdev; ++ struct usb_xpad *xpad; ++}; ++ ++static void xpad_send_led_command(struct usb_xpad *xpad, int command) ++{ ++ if (command >= 0 && command < 14) { ++ mutex_lock(&xpad->odata_mutex); ++ xpad->odata[0] = 0x01; ++ xpad->odata[1] = 0x03; ++ xpad->odata[2] = command; ++ xpad->irq_out->transfer_buffer_length = 3; ++ usb_submit_urb(xpad->irq_out, GFP_KERNEL); ++ mutex_unlock(&xpad->odata_mutex); ++ } ++} ++ ++static void xpad_led_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ struct xpad_led *xpad_led = container_of(led_cdev, ++ struct xpad_led, led_cdev); ++ ++ xpad_send_led_command(xpad_led->xpad, value); ++} ++ ++static int xpad_led_probe(struct usb_xpad *xpad) ++{ ++ static atomic_t led_seq = ATOMIC_INIT(0); ++ long led_no; ++ struct xpad_led *led; ++ struct led_classdev *led_cdev; ++ int error; ++ ++ if (xpad->xtype != XTYPE_XBOX360) ++ return 0; ++ ++ xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); ++ if (!led) ++ return -ENOMEM; ++ ++ led_no = (long)atomic_inc_return(&led_seq) - 1; ++ ++ snprintf(led->name, sizeof(led->name), "xpad%ld", led_no); ++ led->xpad = xpad; ++ ++ led_cdev = &led->led_cdev; ++ led_cdev->name = led->name; ++ led_cdev->brightness_set = xpad_led_set; ++ ++ error = led_classdev_register(&xpad->udev->dev, led_cdev); ++ if (error) { ++ kfree(led); ++ xpad->led = NULL; ++ return error; ++ } ++ ++ /* ++ * Light up the segment corresponding to controller number ++ */ ++ xpad_send_led_command(xpad, (led_no % 4) + 2); ++ ++ return 0; ++} ++ ++static void xpad_led_disconnect(struct usb_xpad *xpad) ++{ ++ struct xpad_led *xpad_led = xpad->led; ++ ++ if (xpad_led) { ++ led_classdev_unregister(&xpad_led->led_cdev); ++ kfree(xpad_led); ++ } ++} ++#else ++static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } ++static void xpad_led_disconnect(struct usb_xpad *xpad) { } ++#endif ++ ++ ++static int xpad_open(struct input_dev *dev) ++{ ++ struct usb_xpad *xpad = input_get_drvdata(dev); ++ ++ /* URB was submitted in probe */ ++ if (xpad->xtype == XTYPE_XBOX360W) ++ return 0; ++ ++ xpad->irq_in->dev = xpad->udev; ++ if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) ++ return -EIO; ++ ++ return 0; ++} ++ ++static void xpad_close(struct input_dev *dev) ++{ ++ struct usb_xpad *xpad = input_get_drvdata(dev); ++ ++ if (xpad->xtype != XTYPE_XBOX360W) ++ usb_kill_urb(xpad->irq_in); ++ ++ xpad_stop_output(xpad); ++} ++ ++static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) ++{ ++ set_bit(abs, input_dev->absbit); ++ ++ switch (abs) { ++ case ABS_X: ++ case ABS_Y: ++ case ABS_RX: ++ case ABS_RY: /* the two sticks */ ++ input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128); ++ break; ++ case ABS_Z: ++ case ABS_RZ: /* the triggers (if mapped to axes) */ ++ input_set_abs_params(input_dev, abs, 0, 255, 0, 0); ++ break; ++ case ABS_HAT0X: ++ case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */ ++ input_set_abs_params(input_dev, abs, -1, 1, 0, 0); ++ break; ++ } ++} ++ ++static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) ++{ ++ struct usb_device *udev = interface_to_usbdev(intf); ++ struct usb_xpad *xpad; ++ struct input_dev *input_dev; ++ struct usb_endpoint_descriptor *ep_irq_in; ++ int i, error; ++ ++ for (i = 0; xpad_device[i].idVendor; i++) { ++ if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && ++ (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) ++ break; ++ } ++ ++ xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL); ++ input_dev = input_allocate_device(); ++ if (!xpad || !input_dev) { ++ error = -ENOMEM; ++ goto fail1; ++ } ++ ++ xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN, ++ GFP_KERNEL, &xpad->idata_dma); ++ if (!xpad->idata) { ++ error = -ENOMEM; ++ goto fail1; ++ } ++ ++ xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL); ++ if (!xpad->irq_in) { ++ error = -ENOMEM; ++ goto fail2; ++ } ++ ++ xpad->udev = udev; ++ xpad->intf = intf; ++ xpad->mapping = xpad_device[i].mapping; ++ xpad->xtype = xpad_device[i].xtype; ++ ++ if (xpad->xtype == XTYPE_UNKNOWN) { ++ if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { ++ if (intf->cur_altsetting->desc.bInterfaceProtocol == 129) ++ xpad->xtype = XTYPE_XBOX360W; ++ else ++ xpad->xtype = XTYPE_XBOX360; ++ } else ++ xpad->xtype = XTYPE_XBOX; ++ ++ if (dpad_to_buttons) ++ xpad->mapping |= MAP_DPAD_TO_BUTTONS; ++ if (triggers_to_buttons) ++ xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS; ++ if (sticks_to_null) ++ xpad->mapping |= MAP_STICKS_TO_NULL; ++ } ++ ++ xpad->dev = input_dev; ++ usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); ++ strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); ++ ++ input_dev->name = xpad_device[i].name; ++ input_dev->phys = xpad->phys; ++ usb_to_input_id(udev, &input_dev->id); ++ input_dev->dev.parent = &intf->dev; ++ ++ input_set_drvdata(input_dev, xpad); ++ ++ input_dev->open = xpad_open; ++ input_dev->close = xpad_close; ++ ++ input_dev->evbit[0] = BIT_MASK(EV_KEY); ++ ++ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { ++ input_dev->evbit[0] |= BIT_MASK(EV_ABS); ++ /* set up axes */ ++ for (i = 0; xpad_abs[i] >= 0; i++) ++ xpad_set_up_abs(input_dev, xpad_abs[i]); ++ } ++ ++ /* set up standard buttons */ ++ for (i = 0; xpad_common_btn[i] >= 0; i++) ++ __set_bit(xpad_common_btn[i], input_dev->keybit); ++ ++ /* set up model-specific ones */ ++ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) { ++ for (i = 0; xpad360_btn[i] >= 0; i++) ++ __set_bit(xpad360_btn[i], input_dev->keybit); ++ } else { ++ for (i = 0; xpad_btn[i] >= 0; i++) ++ __set_bit(xpad_btn[i], input_dev->keybit); ++ } ++ ++ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { ++ for (i = 0; xpad_btn_pad[i] >= 0; i++) ++ __set_bit(xpad_btn_pad[i], input_dev->keybit); ++ } else { ++ for (i = 0; xpad_abs_pad[i] >= 0; i++) ++ xpad_set_up_abs(input_dev, xpad_abs_pad[i]); ++ } ++ ++ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { ++ for (i = 0; xpad_btn_triggers[i] >= 0; i++) ++ __set_bit(xpad_btn_triggers[i], input_dev->keybit); ++ } else { ++ for (i = 0; xpad_abs_triggers[i] >= 0; i++) ++ xpad_set_up_abs(input_dev, xpad_abs_triggers[i]); ++ } ++ ++ error = xpad_init_output(intf, xpad); ++ if (error) ++ goto fail3; ++ ++ error = xpad_init_ff(xpad); ++ if (error) ++ goto fail4; ++ ++ error = xpad_led_probe(xpad); ++ if (error) ++ goto fail5; ++ ++ ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; ++ usb_fill_int_urb(xpad->irq_in, udev, ++ usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), ++ xpad->idata, XPAD_PKT_LEN, xpad_irq_in, ++ xpad, ep_irq_in->bInterval); ++ xpad->irq_in->transfer_dma = xpad->idata_dma; ++ xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ error = input_register_device(xpad->dev); ++ if (error) ++ goto fail6; ++ ++ usb_set_intfdata(intf, xpad); ++ ++ if (xpad->xtype == XTYPE_XBOX360W) { ++ /* ++ * Setup the message to set the LEDs on the ++ * controller when it shows up ++ */ ++ xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); ++ if (!xpad->bulk_out) { ++ error = -ENOMEM; ++ goto fail7; ++ } ++ ++ xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); ++ if (!xpad->bdata) { ++ error = -ENOMEM; ++ goto fail8; ++ } ++ ++ xpad->bdata[2] = 0x08; ++ switch (intf->cur_altsetting->desc.bInterfaceNumber) { ++ case 0: ++ xpad->bdata[3] = 0x42; ++ break; ++ case 2: ++ xpad->bdata[3] = 0x43; ++ break; ++ case 4: ++ xpad->bdata[3] = 0x44; ++ break; ++ case 6: ++ xpad->bdata[3] = 0x45; ++ } ++ ++ ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; ++ if (usb_endpoint_is_bulk_out(ep_irq_in)) { ++ usb_fill_bulk_urb(xpad->bulk_out, udev, ++ usb_sndbulkpipe(udev, ++ ep_irq_in->bEndpointAddress), ++ xpad->bdata, XPAD_PKT_LEN, ++ xpad_bulk_out, xpad); ++ } else { ++ usb_fill_int_urb(xpad->bulk_out, udev, ++ usb_sndintpipe(udev, ++ ep_irq_in->bEndpointAddress), ++ xpad->bdata, XPAD_PKT_LEN, ++ xpad_bulk_out, xpad, 0); ++ } ++ ++ /* ++ * Submit the int URB immediately rather than waiting for open ++ * because we get status messages from the device whether ++ * or not any controllers are attached. In fact, it's ++ * exactly the message that a controller has arrived that ++ * we're waiting for. ++ */ ++ xpad->irq_in->dev = xpad->udev; ++ error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); ++ if (error) ++ goto fail9; ++ } ++ ++ return 0; ++ ++ fail9: kfree(xpad->bdata); ++ fail8: usb_free_urb(xpad->bulk_out); ++ fail7: input_unregister_device(input_dev); ++ input_dev = NULL; ++ fail6: xpad_led_disconnect(xpad); ++ fail5: if (input_dev) ++ input_ff_destroy(input_dev); ++ fail4: xpad_deinit_output(xpad); ++ fail3: usb_free_urb(xpad->irq_in); ++ fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); ++ fail1: input_free_device(input_dev); ++ kfree(xpad); ++ return error; ++ ++} ++ ++static void xpad_disconnect(struct usb_interface *intf) ++{ ++ struct usb_xpad *xpad = usb_get_intfdata (intf); ++ ++ xpad_led_disconnect(xpad); ++ input_unregister_device(xpad->dev); ++ xpad_deinit_output(xpad); ++ ++ if (xpad->xtype == XTYPE_XBOX360W) { ++ usb_kill_urb(xpad->bulk_out); ++ usb_free_urb(xpad->bulk_out); ++ usb_kill_urb(xpad->irq_in); ++ } ++ ++ usb_free_urb(xpad->irq_in); ++ usb_free_coherent(xpad->udev, XPAD_PKT_LEN, ++ xpad->idata, xpad->idata_dma); ++ ++ kfree(xpad->bdata); ++ kfree(xpad); ++ ++ usb_set_intfdata(intf, NULL); ++} ++ ++static struct usb_driver xpad_driver = { ++ .name = "xpad", ++ .probe = xpad_probe, ++ .disconnect = xpad_disconnect, ++ .id_table = xpad_table, ++}; ++ ++module_usb_driver(xpad_driver); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/input/keyboard/gpio_keys.c linux-openelec/drivers/input/keyboard/gpio_keys.c +--- linux-3.14.36/drivers/input/keyboard/gpio_keys.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/input/keyboard/gpio_keys.c 2015-05-06 12:05:42.000000000 -0500 +@@ -3,6 +3,7 @@ + * + * Copyright 2005 Phil Blundell + * Copyright 2010, 2011 David Jander ++ * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * 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 +@@ -473,6 +474,8 @@ + + isr = gpio_keys_gpio_isr; + irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ if (bdata->button->wakeup) ++ irqflags |= IRQF_NO_SUSPEND; + + } else { + if (!button->irq) { +diff -Nur linux-3.14.36/drivers/input/keyboard/imx_keypad.c linux-openelec/drivers/input/keyboard/imx_keypad.c +--- linux-3.14.36/drivers/input/keyboard/imx_keypad.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/input/keyboard/imx_keypad.c 2015-05-06 12:05:42.000000000 -0500 +@@ -1,6 +1,7 @@ + /* + * Driver for the IMX keypad port. + * Copyright (C) 2009 Alberto Panizzo ++ * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * 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 +@@ -548,6 +549,8 @@ + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(kbd->irq); ++ else ++ pinctrl_pm_select_sleep_state(dev); + + return 0; + } +@@ -561,6 +564,8 @@ + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(kbd->irq); ++ else ++ pinctrl_pm_select_default_state(dev); + + mutex_lock(&input_dev->mutex); + +diff -Nur linux-3.14.36/drivers/input/misc/mma8450.c linux-openelec/drivers/input/misc/mma8450.c +--- linux-3.14.36/drivers/input/misc/mma8450.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/input/misc/mma8450.c 2015-05-06 12:05:42.000000000 -0500 +@@ -1,7 +1,7 @@ + /* + * Driver for Freescale's 3-Axis Accelerometer MMA8450 + * +- * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #define MMA8450_DRV_NAME "mma8450" + +@@ -51,11 +52,22 @@ + + #define MMA8450_CTRL_REG1 0x38 + #define MMA8450_CTRL_REG2 0x39 ++#define MMA8450_ID 0xC6 ++#define MMA8450_WHO_AM_I 0x0F ++ ++enum { ++ MODE_STANDBY = 0, ++ MODE_2G, ++ MODE_4G, ++ MODE_8G, ++}; + + /* mma8450 status */ + struct mma8450 { + struct i2c_client *client; + struct input_polled_dev *idev; ++ struct mutex mma8450_lock; ++ u8 mode; + }; + + static int mma8450_read(struct mma8450 *m, unsigned off) +@@ -112,16 +124,19 @@ + int ret; + u8 buf[6]; + +- ret = mma8450_read(m, MMA8450_STATUS); +- if (ret < 0) +- return; ++ mutex_lock(&m->mma8450_lock); + +- if (!(ret & MMA8450_STATUS_ZXYDR)) ++ ret = mma8450_read(m, MMA8450_STATUS); ++ if (ret < 0 || !(ret & MMA8450_STATUS_ZXYDR)) { ++ mutex_unlock(&m->mma8450_lock); + return; ++ } + + ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf)); +- if (ret < 0) ++ if (ret < 0) { ++ mutex_unlock(&m->mma8450_lock); + return; ++ } + + x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf); + y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf); +@@ -131,10 +146,12 @@ + input_report_abs(dev->input, ABS_Y, y); + input_report_abs(dev->input, ABS_Z, z); + input_sync(dev->input); ++ ++ mutex_unlock(&m->mma8450_lock); + } + + /* Initialize the MMA8450 chip */ +-static void mma8450_open(struct input_polled_dev *dev) ++static s32 mma8450_open(struct input_polled_dev *dev) + { + struct mma8450 *m = dev->private; + int err; +@@ -142,18 +159,20 @@ + /* enable all events from X/Y/Z, no FIFO */ + err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07); + if (err) +- return; ++ return err; + + /* + * Sleep mode poll rate - 50Hz + * System output data rate - 400Hz +- * Full scale selection - Active, +/- 2G ++ * Standby mode + */ +- err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01); +- if (err < 0) +- return; +- ++ err = mma8450_write(m, MMA8450_CTRL_REG1, MODE_STANDBY); ++ if (err) ++ return err; ++ m->mode = MODE_STANDBY; + msleep(MODE_CHANGE_DELAY_MS); ++ ++ return 0; + } + + static void mma8450_close(struct input_polled_dev *dev) +@@ -164,6 +183,76 @@ + mma8450_write(m, MMA8450_CTRL_REG2, 0x01); + } + ++static ssize_t mma8450_scalemode_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ int mode = 0; ++ struct mma8450 *m; ++ struct i2c_client *client = to_i2c_client(dev); ++ ++ m = i2c_get_clientdata(client); ++ ++ mutex_lock(&m->mma8450_lock); ++ mode = (int)m->mode; ++ mutex_unlock(&m->mma8450_lock); ++ ++ return sprintf(buf, "%d\n", mode); ++} ++ ++static ssize_t mma8450_scalemode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ unsigned long mode; ++ int ret; ++ struct mma8450 *m = NULL; ++ struct i2c_client *client = to_i2c_client(dev); ++ ++ ret = strict_strtoul(buf, 10, &mode); ++ if (ret) { ++ dev_err(dev, "string transform error\n"); ++ return ret; ++ } ++ ++ if (mode > MODE_8G) { ++ dev_warn(dev, "not supported mode %d\n", (int)mode); ++ return count; ++ } ++ ++ m = i2c_get_clientdata(client); ++ ++ mutex_lock(&m->mma8450_lock); ++ if (mode == m->mode) { ++ mutex_unlock(&m->mma8450_lock); ++ return count; ++ } ++ ++ ret = mma8450_write(m, MMA8450_CTRL_REG1, mode); ++ if (ret < 0) { ++ mutex_unlock(&m->mma8450_lock); ++ return ret; ++ } ++ ++ msleep(MODE_CHANGE_DELAY_MS); ++ m->mode = (u8)mode; ++ mutex_unlock(&m->mma8450_lock); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(scalemode, S_IWUSR | S_IRUGO, ++ mma8450_scalemode_show, mma8450_scalemode_store); ++ ++static struct attribute *mma8450_attributes[] = { ++ &dev_attr_scalemode.attr, ++ NULL ++}; ++ ++static const struct attribute_group mma8450_attr_group = { ++ .attrs = mma8450_attributes, ++}; ++ + /* + * I2C init/probing/exit functions + */ +@@ -172,7 +261,25 @@ + { + struct input_polled_dev *idev; + struct mma8450 *m; +- int err; ++ int err, client_id; ++ struct i2c_adapter *adapter = NULL; ++ ++ adapter = to_i2c_adapter(c->dev.parent); ++ err = i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA); ++ if (!err) ++ goto err_out; ++ ++ client_id = i2c_smbus_read_byte_data(c, MMA8450_WHO_AM_I); ++ ++ if (MMA8450_ID != client_id) { ++ dev_err(&c->dev, ++ "read chip ID 0x%x is not equal to 0x%x!\n", client_id, ++ MMA8450_ID); ++ err = -EINVAL; ++ goto err_out; ++ } + + m = kzalloc(sizeof(struct mma8450), GFP_KERNEL); + idev = input_allocate_polled_device(); +@@ -183,6 +290,7 @@ + + m->client = c; + m->idev = idev; ++ i2c_set_clientdata(c, m); + + idev->private = m; + idev->input->name = MMA8450_DRV_NAME; +@@ -190,8 +298,6 @@ + idev->poll = mma8450_poll; + idev->poll_interval = POLL_INTERVAL; + idev->poll_interval_max = POLL_INTERVAL_MAX; +- idev->open = mma8450_open; +- idev->close = mma8450_close; + + __set_bit(EV_ABS, idev->input->evbit); + input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32); +@@ -206,11 +312,32 @@ + + i2c_set_clientdata(c, m); + ++ mutex_init(&m->mma8450_lock); ++ ++ err = mma8450_open(idev); ++ if (err) { ++ dev_err(&c->dev, "failed to initialize mma8450\n"); ++ goto err_unreg_dev; ++ } ++ ++ err = sysfs_create_group(&c->dev.kobj, &mma8450_attr_group); ++ if (err) { ++ dev_err(&c->dev, "create device file failed!\n"); ++ err = -EINVAL; ++ goto err_close; ++ } ++ + return 0; + ++err_close: ++ mma8450_close(idev); ++err_unreg_dev: ++ mutex_destroy(&m->mma8450_lock); ++ input_unregister_polled_device(idev); + err_free_mem: + input_free_polled_device(idev); + kfree(m); ++err_out: + return err; + } + +@@ -219,6 +346,9 @@ + struct mma8450 *m = i2c_get_clientdata(c); + struct input_polled_dev *idev = m->idev; + ++ sysfs_remove_group(&c->dev.kobj, &mma8450_attr_group); ++ mma8450_close(idev); ++ mutex_destroy(&m->mma8450_lock); + input_unregister_polled_device(idev); + input_free_polled_device(idev); + kfree(m); +diff -Nur linux-3.14.36/drivers/input/sparse-keymap.c linux-openelec/drivers/input/sparse-keymap.c +--- linux-3.14.36/drivers/input/sparse-keymap.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/input/sparse-keymap.c 2015-05-06 12:05:42.000000000 -0500 +@@ -236,7 +236,7 @@ + * in an input device that was set up by sparse_keymap_setup(). + * NOTE: It is safe to cal this function while input device is + * still registered (however the drivers should care not to try to +- * use freed keymap and thus have to shut off interrups/polling ++ * use freed keymap and thus have to shut off interrupts/polling + * before freeing the keymap). + */ + void sparse_keymap_free(struct input_dev *dev) +diff -Nur linux-3.14.36/drivers/input/touchscreen/st1232.c linux-openelec/drivers/input/touchscreen/st1232.c +--- linux-3.14.36/drivers/input/touchscreen/st1232.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/input/touchscreen/st1232.c 2015-07-24 18:03:30.272842002 -0500 +@@ -1,12 +1,13 @@ + /* +- * ST1232 Touchscreen Controller Driver ++ * ST1232/ST1332 Touchscreen Controller Driver + * + * Copyright (C) 2010 Renesas Solutions Corp. +- * Tony SIM ++ * Tony SIM ++ * Copyright (C) 2015 Peter Vicman + * + * Using code from: + * - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c +- * Copyright (C) 2007 Google, Inc. ++ * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and +@@ -30,282 +31,383 @@ + #include + #include + #include ++#include + +-#define ST1232_TS_NAME "st1232-ts" +- +-#define MIN_X 0x00 +-#define MIN_Y 0x00 +-#define MAX_X 0x31f /* (800 - 1) */ +-#define MAX_Y 0x1df /* (480 - 1) */ +-#define MAX_AREA 0xff +-#define MAX_FINGERS 2 ++#define ST1232_TS_NAME "st1232-ts" ++#define MIN_X 0 ++#define MIN_Y 0 ++#define MAX_X (800 - 1) ++#define MAX_Y (480 - 1) ++#define MAX_AREA 0xff ++#define MAX_FINGERS 2 ++#define SAMPLE_DELAY 20 /* msecs */ ++#define REVERSE_X(x) if (reverse_x == true) { x = MAX_X - (x); } else {} ++#define REVERSE_Y(y) if (reverse_y == true) { y = MAX_Y - (y); } else {} + + struct st1232_ts_finger { +- u16 x; +- u16 y; +- u8 t; +- bool is_valid; ++ u16 x; ++ u16 y; ++ u8 t; ++ bool is_valid; + }; + + struct st1232_ts_data { +- struct i2c_client *client; +- struct input_dev *input_dev; +- struct st1232_ts_finger finger[MAX_FINGERS]; +- struct dev_pm_qos_request low_latency_req; +- int reset_gpio; ++ struct i2c_client *client; ++ struct input_dev *input_dev; ++ struct st1232_ts_finger finger[MAX_FINGERS]; ++ struct dev_pm_qos_request low_latency_req; ++ int reset_gpio; ++ struct delayed_work work; + }; + ++static bool multitouch = false; ++module_param(multitouch, bool, 0); ++MODULE_PARM_DESC(multitouch, " If multitouch is set to 1 ts acts as multitouch"); ++ ++static bool reverse_x = false; ++module_param(reverse_x, bool, 0600); ++MODULE_PARM_DESC(reverse_x, " If reverse_x is set to 1 x coordinates are reversed"); ++ ++static bool reverse_y = false; ++module_param(reverse_y, bool, 0600); ++MODULE_PARM_DESC(reverse_y, " If reverse_y is set to 1 y coordinates are reversed"); ++ ++static int offset_x = 0; ++module_param(offset_x, int, 0600); ++MODULE_PARM_DESC(offset_x, " Offset value for x axis"); ++ ++static int offset_y = 0; ++module_param(offset_y, int, 0600); ++MODULE_PARM_DESC(offset_y, " Offset value for y axis"); ++ + static int st1232_ts_read_data(struct st1232_ts_data *ts) + { +- struct st1232_ts_finger *finger = ts->finger; +- struct i2c_client *client = ts->client; +- struct i2c_msg msg[2]; +- int error; +- u8 start_reg; +- u8 buf[10]; +- +- /* read touchscreen data from ST1232 */ +- msg[0].addr = client->addr; +- msg[0].flags = 0; +- msg[0].len = 1; +- msg[0].buf = &start_reg; +- start_reg = 0x10; +- +- msg[1].addr = ts->client->addr; +- msg[1].flags = I2C_M_RD; +- msg[1].len = sizeof(buf); +- msg[1].buf = buf; +- +- error = i2c_transfer(client->adapter, msg, 2); +- if (error < 0) +- return error; +- +- /* get "valid" bits */ +- finger[0].is_valid = buf[2] >> 7; +- finger[1].is_valid = buf[5] >> 7; +- +- /* get xy coordinate */ +- if (finger[0].is_valid) { +- finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3]; +- finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4]; +- finger[0].t = buf[8]; +- } +- +- if (finger[1].is_valid) { +- finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6]; +- finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7]; +- finger[1].t = buf[9]; +- } ++ struct st1232_ts_finger *finger = ts->finger; ++ struct i2c_client *client = ts->client; ++ struct i2c_msg msg[2]; ++ int error; ++ u8 start_reg; ++ u8 buf[10]; ++ ++ /* read touchscreen data from ST1232 */ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 1; ++ msg[0].buf = &start_reg; ++ start_reg = 0x10; ++ ++ msg[1].addr = ts->client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = sizeof(buf); ++ msg[1].buf = buf; ++ ++ error = i2c_transfer(client->adapter, msg, 2); ++ if (error < 0) ++ return error; ++ ++ memset(finger, 0x0, sizeof(struct st1232_ts_finger) * MAX_FINGERS); ++ ++ /* get "valid" bits from fingers ++ and combine with "valid" bits from coordinates */ ++ finger[0].is_valid = (buf[0] & 0x07); /* only 3 bits on st1332 */ ++ finger[0].is_valid &= (buf[2] >> 7); ++ finger[1].is_valid = (buf[0] & 0x07); ++ finger[1].is_valid &= (buf[5] >> 7); ++ ++ /* get xy coordinates and strength */ ++ if (finger[0].is_valid) { ++ finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3]; ++ finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4]; ++ finger[0].t = buf[8]; ++ ++ REVERSE_X(finger[0].x) ++ REVERSE_Y(finger[0].y) ++ ++ finger[0].x += offset_x; ++ finger[0].y += offset_y; ++ } ++ ++ if (finger[1].is_valid) { ++ finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6]; ++ finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7]; ++ finger[1].t = buf[9]; ++ ++ REVERSE_X(finger[1].x) ++ REVERSE_Y(finger[1].y) ++ } + +- return 0; ++ return 0; + } + +-static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) ++static void st1232_ts_finger_released(struct work_struct *work) + { +- struct st1232_ts_data *ts = dev_id; +- struct st1232_ts_finger *finger = ts->finger; +- struct input_dev *input_dev = ts->input_dev; +- int count = 0; +- int i, ret; +- +- ret = st1232_ts_read_data(ts); +- if (ret < 0) +- goto end; +- +- /* multi touch protocol */ +- for (i = 0; i < MAX_FINGERS; i++) { +- if (!finger[i].is_valid) +- continue; +- +- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); +- input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); +- input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); +- input_mt_sync(input_dev); +- count++; +- } +- +- /* SYN_MT_REPORT only if no contact */ +- if (!count) { +- input_mt_sync(input_dev); +- if (ts->low_latency_req.dev) { +- dev_pm_qos_remove_request(&ts->low_latency_req); +- ts->low_latency_req.dev = NULL; +- } +- } else if (!ts->low_latency_req.dev) { +- /* First contact, request 100 us latency. */ +- dev_pm_qos_add_ancestor_request(&ts->client->dev, +- &ts->low_latency_req, 100); +- } ++ struct st1232_ts_data *ts = container_of(work, struct st1232_ts_data, work.work); ++ struct st1232_ts_finger *finger = ts->finger; ++ struct input_dev *input_dev = ts->input_dev; ++ int ret; ++ ++ ret = st1232_ts_read_data(ts); ++ if (ret < 0) ++ goto end; ++ ++ /* finger is a pointer to finger[0] */ ++ if (finger->is_valid) ++ goto end; /* finger (still) touched */ ++ ++ /* finger released */ ++ input_report_abs(input_dev, ABS_PRESSURE, 0); ++ input_report_key(input_dev, BTN_TOUCH, 0); ++ input_sync(input_dev); ++ ++end: ++ return; ++} + +- /* SYN_REPORT */ +- input_sync(input_dev); ++static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) ++{ ++ struct st1232_ts_data *ts = dev_id; ++ struct st1232_ts_finger *finger = ts->finger; ++ struct input_dev *input_dev = ts->input_dev; ++ int count = 0; ++ int i, ret; ++ ++ if (multitouch == false) { ++ /* ++ * Cancel scheduled polling for release if we have new value ++ * available. Wait if the polling is already running. ++ */ ++ cancel_delayed_work_sync(&ts->work); ++ } ++ ++ ret = st1232_ts_read_data(ts); ++ if (ret < 0) ++ goto end; ++ ++ if (multitouch == false) { ++ if (finger->is_valid) { ++ input_report_abs(input_dev, ABS_X, finger->x); ++ input_report_abs(input_dev, ABS_Y, finger->y); ++ input_report_abs(input_dev, ABS_PRESSURE, finger->t); ++ input_report_key(input_dev, BTN_TOUCH, 1); ++ input_sync(input_dev); ++ } ++ } else { ++ /* multi touch protocol */ ++ for (i = 0; i < MAX_FINGERS; i++) { ++ if (!finger[i].is_valid) ++ continue; ++ ++ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); ++ input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); ++ input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); ++ input_mt_sync(input_dev); ++ count++; ++ } ++ ++ /* SYN_MT_REPORT only if no contact */ ++ if (!count) { ++ input_mt_sync(input_dev); ++ if (ts->low_latency_req.dev) { ++ dev_pm_qos_remove_request(&ts->low_latency_req); ++ ts->low_latency_req.dev = NULL; ++ } ++ } else if (!ts->low_latency_req.dev) { ++ /* First contact, request 100 us latency. */ ++ dev_pm_qos_add_ancestor_request(&ts->client->dev, &ts->low_latency_req, 100); ++ } ++ ++ /* SYN_REPORT */ ++ input_sync(input_dev); ++ } + + end: +- return IRQ_HANDLED; ++ if (multitouch == false) { ++ /* start polling for st1232_ts_read_data to detect release */ ++ schedule_delayed_work(&ts->work, msecs_to_jiffies(SAMPLE_DELAY)); ++ } ++ ++ return IRQ_HANDLED; + } + + static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) + { +- if (gpio_is_valid(ts->reset_gpio)) +- gpio_direction_output(ts->reset_gpio, poweron); ++ if (gpio_is_valid(ts->reset_gpio)) ++ gpio_direction_output(ts->reset_gpio, poweron); + } + + static int st1232_ts_probe(struct i2c_client *client, +- const struct i2c_device_id *id) ++ const struct i2c_device_id *id) + { +- struct st1232_ts_data *ts; +- struct st1232_pdata *pdata = dev_get_platdata(&client->dev); +- struct input_dev *input_dev; +- int error; +- +- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +- dev_err(&client->dev, "need I2C_FUNC_I2C\n"); +- return -EIO; +- } +- +- if (!client->irq) { +- dev_err(&client->dev, "no IRQ?\n"); +- return -EINVAL; +- } +- +- ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); +- if (!ts) +- return -ENOMEM; +- +- input_dev = devm_input_allocate_device(&client->dev); +- if (!input_dev) +- return -ENOMEM; +- +- ts->client = client; +- ts->input_dev = input_dev; +- +- if (pdata) +- ts->reset_gpio = pdata->reset_gpio; +- else if (client->dev.of_node) +- ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); +- else +- ts->reset_gpio = -ENODEV; +- +- if (gpio_is_valid(ts->reset_gpio)) { +- error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL); +- if (error) { +- dev_err(&client->dev, +- "Unable to request GPIO pin %d.\n", +- ts->reset_gpio); +- return error; +- } +- } +- +- st1232_ts_power(ts, true); +- +- input_dev->name = "st1232-touchscreen"; +- input_dev->id.bustype = BUS_I2C; +- input_dev->dev.parent = &client->dev; +- +- __set_bit(EV_SYN, input_dev->evbit); +- __set_bit(EV_KEY, input_dev->evbit); +- __set_bit(EV_ABS, input_dev->evbit); +- +- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0); +- input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); +- input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); +- +- error = devm_request_threaded_irq(&client->dev, client->irq, +- NULL, st1232_ts_irq_handler, +- IRQF_ONESHOT, +- client->name, ts); +- if (error) { +- dev_err(&client->dev, "Failed to register interrupt\n"); +- return error; +- } +- +- error = input_register_device(ts->input_dev); +- if (error) { +- dev_err(&client->dev, "Unable to register %s input device\n", +- input_dev->name); +- return error; +- } ++ struct st1232_ts_data *ts; ++ struct st1232_pdata *pdata = dev_get_platdata(&client->dev); ++ struct input_dev *input_dev; ++ int error; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ dev_err(&client->dev, "need I2C_FUNC_I2C\n"); ++ return -EIO; ++ } ++ ++ if (!client->irq) { ++ dev_err(&client->dev, "no IRQ?\n"); ++ return -EINVAL; ++ } ++ ++ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); ++ if (!ts) ++ return -ENOMEM; ++ ++ input_dev = devm_input_allocate_device(&client->dev); ++ if (!input_dev) ++ return -ENOMEM; ++ ++ ts->client = client; ++ ts->input_dev = input_dev; ++ ++ if (pdata) ++ ts->reset_gpio = pdata->reset_gpio; ++ else if (client->dev.of_node) ++ ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); ++ else ++ ts->reset_gpio = -ENODEV; ++ ++ if (gpio_is_valid(ts->reset_gpio)) { ++ error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL); ++ if (error) { ++ dev_err(&client->dev, "Unable to request GPIO pin %d.\n", ts->reset_gpio); ++ return error; ++ } ++ } ++ ++ st1232_ts_power(ts, true); ++ ++ input_dev->name = ST1232_TS_NAME; ++ input_dev->id.bustype = BUS_I2C; ++ input_dev->dev.parent = &client->dev; ++ ++ if (multitouch == false) { ++ input_dev->phys = ST1232_TS_NAME"/input0"; ++ ++ __set_bit(BTN_TOUCH, input_dev->keybit); ++ __set_bit(EV_KEY, input_dev->evbit); ++ __set_bit(EV_ABS, input_dev->evbit); ++ __set_bit(ABS_X, input_dev->absbit); ++ __set_bit(ABS_Y, input_dev->absbit); ++ __set_bit(ABS_PRESSURE, input_dev->absbit); ++ ++ input_set_abs_params(input_dev, ABS_X, MIN_X, MAX_X, 0, 0); ++ input_set_abs_params(input_dev, ABS_Y, MIN_Y, MAX_Y, 0, 0); ++ input_set_abs_params(input_dev, ABS_PRESSURE, 0x0, 0xff, 0, 0); ++ ++ INIT_DELAYED_WORK(&ts->work, st1232_ts_finger_released); ++ } else { ++ __set_bit(EV_SYN, input_dev->evbit); ++ __set_bit(EV_KEY, input_dev->evbit); ++ __set_bit(EV_ABS, input_dev->evbit); ++ ++ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0); ++ input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); ++ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); ++ } ++ ++ error = devm_request_threaded_irq(&client->dev, client->irq, ++ NULL, st1232_ts_irq_handler, ++ IRQF_ONESHOT, ++ client->name, ts); ++ if (error) { ++ dev_err(&client->dev, "Failed to register interrupt\n"); ++ return error; ++ } ++ ++ error = input_register_device(ts->input_dev); ++ if (error) { ++ dev_err(&client->dev, "Unable to register %s input device\n", ++ input_dev->name); ++ return error; ++ } + +- i2c_set_clientdata(client, ts); +- device_init_wakeup(&client->dev, 1); ++ i2c_set_clientdata(client, ts); ++ device_init_wakeup(&client->dev, 1); + +- return 0; ++ return 0; + } + + static int st1232_ts_remove(struct i2c_client *client) + { +- struct st1232_ts_data *ts = i2c_get_clientdata(client); ++ struct st1232_ts_data *ts = i2c_get_clientdata(client); + +- device_init_wakeup(&client->dev, 0); +- st1232_ts_power(ts, false); ++ device_init_wakeup(&client->dev, 0); ++ st1232_ts_power(ts, false); ++ cancel_delayed_work_sync(&ts->work); + +- return 0; ++ return 0; + } + + #ifdef CONFIG_PM_SLEEP + static int st1232_ts_suspend(struct device *dev) + { +- struct i2c_client *client = to_i2c_client(dev); +- struct st1232_ts_data *ts = i2c_get_clientdata(client); ++ struct i2c_client *client = to_i2c_client(dev); ++ struct st1232_ts_data *ts = i2c_get_clientdata(client); + +- if (device_may_wakeup(&client->dev)) { +- enable_irq_wake(client->irq); +- } else { +- disable_irq(client->irq); +- st1232_ts_power(ts, false); +- } ++ if (device_may_wakeup(&client->dev)) { ++ enable_irq_wake(client->irq); ++ } else { ++ disable_irq(client->irq); ++ cancel_delayed_work_sync(&ts->work); ++ st1232_ts_power(ts, false); ++ } + +- return 0; ++ return 0; + } + + static int st1232_ts_resume(struct device *dev) + { +- struct i2c_client *client = to_i2c_client(dev); +- struct st1232_ts_data *ts = i2c_get_clientdata(client); ++ struct i2c_client *client = to_i2c_client(dev); ++ struct st1232_ts_data *ts = i2c_get_clientdata(client); + +- if (device_may_wakeup(&client->dev)) { +- disable_irq_wake(client->irq); +- } else { +- st1232_ts_power(ts, true); +- enable_irq(client->irq); +- } ++ if (device_may_wakeup(&client->dev)) { ++ disable_irq_wake(client->irq); ++ } else { ++ st1232_ts_power(ts, true); ++ schedule_delayed_work(&ts->work, HZ / 50); ++ enable_irq(client->irq); ++ } + +- return 0; ++ return 0; + } +- + #endif + + static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops, +- st1232_ts_suspend, st1232_ts_resume); ++ st1232_ts_suspend, st1232_ts_resume); + + static const struct i2c_device_id st1232_ts_id[] = { +- { ST1232_TS_NAME, 0 }, +- { } ++ { ST1232_TS_NAME, 0 }, ++ { } + }; + MODULE_DEVICE_TABLE(i2c, st1232_ts_id); + + #ifdef CONFIG_OF + static const struct of_device_id st1232_ts_dt_ids[] = { +- { .compatible = "sitronix,st1232", }, +- { } ++ { .compatible = "sitronix,st1232", }, ++ { } + }; + MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids); + #endif + + static struct i2c_driver st1232_ts_driver = { +- .probe = st1232_ts_probe, +- .remove = st1232_ts_remove, +- .id_table = st1232_ts_id, +- .driver = { +- .name = ST1232_TS_NAME, +- .owner = THIS_MODULE, +- .of_match_table = of_match_ptr(st1232_ts_dt_ids), +- .pm = &st1232_ts_pm_ops, +- }, ++ .probe = st1232_ts_probe, ++ .remove = st1232_ts_remove, ++ .id_table = st1232_ts_id, ++ .driver = { ++ .name = ST1232_TS_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(st1232_ts_dt_ids), ++ .pm = &st1232_ts_pm_ops, ++ }, + }; + + module_i2c_driver(st1232_ts_driver); + +-MODULE_AUTHOR("Tony SIM "); +-MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); ++MODULE_AUTHOR("Tony SIM , Peter Vicman "); ++MODULE_DESCRIPTION("SITRONIX ST1232/ST1332 Touchscreen Controller Driver"); + MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/Kconfig linux-openelec/drivers/Kconfig +--- linux-3.14.36/drivers/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -96,6 +96,8 @@ + + source "drivers/memstick/Kconfig" + ++source "drivers/mxc/Kconfig" ++ + source "drivers/leds/Kconfig" + + source "drivers/accessibility/Kconfig" +diff -Nur linux-3.14.36/drivers/leds/leds-gpio.c linux-openelec/drivers/leds/leds-gpio.c +--- linux-3.14.36/drivers/leds/leds-gpio.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/leds/leds-gpio.c 2015-05-06 12:05:42.000000000 -0500 +@@ -3,7 +3,7 @@ + * + * Copyright (C) 2007 8D Technologies inc. + * Raphael Assenat +- * Copyright (C) 2008 Freescale Semiconductor, Inc. ++ * Copyright (C) 2008, 2014 Freescale Semiconductor, Inc. + * + * 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 +@@ -203,6 +203,8 @@ + else + led.default_state = LEDS_GPIO_DEFSTATE_OFF; + } ++ if (of_get_property(child, "retain-state-suspended", NULL)) ++ led.retain_state_suspended = 1; + + ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], + &pdev->dev, NULL); +diff -Nur linux-3.14.36/drivers/leds/leds-pwm.c linux-openelec/drivers/leds/leds-pwm.c +--- linux-3.14.36/drivers/leds/leds-pwm.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/leds/leds-pwm.c 2015-05-06 12:05:42.000000000 -0500 +@@ -70,6 +70,10 @@ + + duty *= brightness; + do_div(duty, max); ++ ++ if (led_dat->active_low) ++ duty = led_dat->period - duty; ++ + led_dat->duty = duty; + + if (led_dat->can_sleep) +@@ -93,55 +97,75 @@ + } + } + +-static int led_pwm_create_of(struct platform_device *pdev, +- struct led_pwm_priv *priv) ++static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, ++ struct led_pwm *led, struct device_node *child) + { +- struct device_node *child; ++ struct led_pwm_data *led_data = &priv->leds[priv->num_leds]; + int ret; + +- for_each_child_of_node(pdev->dev.of_node, child) { +- struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; ++ led_data->active_low = led->active_low; ++ led_data->period = led->pwm_period_ns; ++ led_data->cdev.name = led->name; ++ led_data->cdev.default_trigger = led->default_trigger; ++ led_data->cdev.brightness_set = led_pwm_set; ++ led_data->cdev.brightness = LED_OFF; ++ led_data->cdev.max_brightness = led->max_brightness; ++ led_data->cdev.flags = LED_CORE_SUSPENDRESUME; ++ ++ if (child) ++ led_data->pwm = devm_of_pwm_get(dev, child, NULL); ++ else ++ led_data->pwm = devm_pwm_get(dev, led->name); ++ if (IS_ERR(led_data->pwm)) { ++ ret = PTR_ERR(led_data->pwm); ++ dev_err(dev, "unable to request PWM for %s: %d\n", ++ led->name, ret); ++ return ret; ++ } + +- led_dat->cdev.name = of_get_property(child, "label", +- NULL) ? : child->name; ++ if (child) ++ led_data->period = pwm_get_period(led_data->pwm); + +- led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL); +- if (IS_ERR(led_dat->pwm)) { +- dev_err(&pdev->dev, "unable to request PWM for %s\n", +- led_dat->cdev.name); +- ret = PTR_ERR(led_dat->pwm); +- goto err; +- } +- /* Get the period from PWM core when n*/ +- led_dat->period = pwm_get_period(led_dat->pwm); ++ led_data->can_sleep = pwm_can_sleep(led_data->pwm); ++ if (led_data->can_sleep) ++ INIT_WORK(&led_data->work, led_pwm_work); + +- led_dat->cdev.default_trigger = of_get_property(child, ++ ret = led_classdev_register(dev, &led_data->cdev); ++ if (ret == 0) { ++ priv->num_leds++; ++ } else { ++ dev_err(dev, "failed to register PWM led for %s: %d\n", ++ led->name, ret); ++ } ++ ++ return ret; ++} ++ ++static int led_pwm_create_of(struct device *dev, struct led_pwm_priv *priv) ++{ ++ struct device_node *child; ++ struct led_pwm led; ++ int ret = 0; ++ ++ memset(&led, 0, sizeof(led)); ++ ++ for_each_child_of_node(dev->of_node, child) { ++ led.name = of_get_property(child, "label", NULL) ? : ++ child->name; ++ ++ led.default_trigger = of_get_property(child, + "linux,default-trigger", NULL); ++ led.active_low = of_property_read_bool(child, "active-low"); + of_property_read_u32(child, "max-brightness", +- &led_dat->cdev.max_brightness); ++ &led.max_brightness); + +- led_dat->cdev.brightness_set = led_pwm_set; +- led_dat->cdev.brightness = LED_OFF; +- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; +- +- led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); +- if (led_dat->can_sleep) +- INIT_WORK(&led_dat->work, led_pwm_work); +- +- ret = led_classdev_register(&pdev->dev, &led_dat->cdev); +- if (ret < 0) { +- dev_err(&pdev->dev, "failed to register for %s\n", +- led_dat->cdev.name); ++ ret = led_pwm_add(dev, priv, &led, child); ++ if (ret) { + of_node_put(child); +- goto err; ++ break; + } +- priv->num_leds++; + } + +- return 0; +-err: +- led_pwm_cleanup(priv); +- + return ret; + } + +@@ -167,51 +191,23 @@ + + if (pdata) { + for (i = 0; i < count; i++) { +- struct led_pwm *cur_led = &pdata->leds[i]; +- struct led_pwm_data *led_dat = &priv->leds[i]; +- +- led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); +- if (IS_ERR(led_dat->pwm)) { +- ret = PTR_ERR(led_dat->pwm); +- dev_err(&pdev->dev, +- "unable to request PWM for %s\n", +- cur_led->name); +- goto err; +- } +- +- led_dat->cdev.name = cur_led->name; +- led_dat->cdev.default_trigger = cur_led->default_trigger; +- led_dat->active_low = cur_led->active_low; +- led_dat->period = cur_led->pwm_period_ns; +- led_dat->cdev.brightness_set = led_pwm_set; +- led_dat->cdev.brightness = LED_OFF; +- led_dat->cdev.max_brightness = cur_led->max_brightness; +- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; +- +- led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); +- if (led_dat->can_sleep) +- INIT_WORK(&led_dat->work, led_pwm_work); +- +- ret = led_classdev_register(&pdev->dev, &led_dat->cdev); +- if (ret < 0) +- goto err; ++ ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i], ++ NULL); ++ if (ret) ++ break; + } +- priv->num_leds = count; + } else { +- ret = led_pwm_create_of(pdev, priv); +- if (ret) +- return ret; ++ ret = led_pwm_create_of(&pdev->dev, priv); ++ } ++ ++ if (ret) { ++ led_pwm_cleanup(priv); ++ return ret; + } + + platform_set_drvdata(pdev, priv); + + return 0; +- +-err: +- priv->num_leds = i; +- led_pwm_cleanup(priv); +- +- return ret; + } + + static int led_pwm_remove(struct platform_device *pdev) +diff -Nur linux-3.14.36/drivers/mailbox/mailbox.c linux-openelec/drivers/mailbox/mailbox.c +--- linux-3.14.36/drivers/mailbox/mailbox.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/mailbox/mailbox.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,488 @@ ++/* ++ * Mailbox: Common code for Mailbox controllers and users ++ * ++ * Copyright (C) 2014 Linaro Ltd. ++ * Author: Jassi Brar ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TXDONE_BY_IRQ (1 << 0) /* controller has remote RTR irq */ ++#define TXDONE_BY_POLL (1 << 1) /* controller can read status of last TX */ ++#define TXDONE_BY_ACK (1 << 2) /* S/W ACK recevied by Client ticks the TX */ ++ ++static LIST_HEAD(mbox_cons); ++static DEFINE_MUTEX(con_mutex); ++ ++static int _add_to_rbuf(struct mbox_chan *chan, void *mssg) ++{ ++ int idx; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&chan->lock, flags); ++ ++ /* See if there is any space left */ ++ if (chan->msg_count == MBOX_TX_QUEUE_LEN) { ++ spin_unlock_irqrestore(&chan->lock, flags); ++ return -ENOMEM; ++ } ++ ++ idx = chan->msg_free; ++ chan->msg_data[idx] = mssg; ++ chan->msg_count++; ++ ++ if (idx == MBOX_TX_QUEUE_LEN - 1) ++ chan->msg_free = 0; ++ else ++ chan->msg_free++; ++ ++ spin_unlock_irqrestore(&chan->lock, flags); ++ ++ return idx; ++} ++ ++static void _msg_submit(struct mbox_chan *chan) ++{ ++ unsigned count, idx; ++ unsigned long flags; ++ void *data; ++ int err; ++ ++ spin_lock_irqsave(&chan->lock, flags); ++ ++ if (!chan->msg_count || chan->active_req) { ++ spin_unlock_irqrestore(&chan->lock, flags); ++ return; ++ } ++ ++ count = chan->msg_count; ++ idx = chan->msg_free; ++ if (idx >= count) ++ idx -= count; ++ else ++ idx += MBOX_TX_QUEUE_LEN - count; ++ ++ data = chan->msg_data[idx]; ++ ++ /* Try to submit a message to the MBOX controller */ ++ err = chan->mbox->ops->send_data(chan, data); ++ if (!err) { ++ chan->active_req = data; ++ chan->msg_count--; ++ } ++ ++ spin_unlock_irqrestore(&chan->lock, flags); ++} ++ ++static void tx_tick(struct mbox_chan *chan, int r) ++{ ++ unsigned long flags; ++ void *mssg; ++ ++ spin_lock_irqsave(&chan->lock, flags); ++ mssg = chan->active_req; ++ chan->active_req = NULL; ++ spin_unlock_irqrestore(&chan->lock, flags); ++ ++ /* Submit next message */ ++ _msg_submit(chan); ++ ++ /* Notify the client */ ++ if (chan->cl->tx_block) ++ complete(&chan->tx_complete); ++ else if (mssg && chan->cl->tx_done) ++ chan->cl->tx_done(chan->cl, mssg, r); ++} ++ ++static void poll_txdone(unsigned long data) ++{ ++ struct mbox_controller *mbox = (struct mbox_controller *)data; ++ bool txdone, resched = false; ++ int i; ++ ++ for (i = 0; i < mbox->num_chans; i++) { ++ struct mbox_chan *chan = &mbox->chans[i]; ++ ++ if (chan->active_req && chan->cl) { ++ resched = true; ++ txdone = chan->mbox->ops->last_tx_done(chan); ++ if (txdone) ++ tx_tick(chan, 0); ++ } ++ } ++ ++ if (resched) ++ mod_timer(&mbox->poll, ++ jiffies + msecs_to_jiffies(mbox->period)); ++} ++ ++/** ++ * mbox_chan_received_data - A way for controller driver to push data ++ * received from remote to the upper layer. ++ * @chan: Pointer to the mailbox channel on which RX happened. ++ * @data: Client specific message typecasted as void * ++ * ++ * After startup and before shutdown any data received on the chan ++ * is passed on to the API via atomic mbox_chan_received_data(). ++ * The controller should ACK the RX only after this call returns. ++ */ ++void mbox_chan_received_data(struct mbox_chan *chan, void *mssg) ++{ ++ /* No buffering the received data */ ++ if (chan->cl->rx_callback) ++ chan->cl->rx_callback(chan->cl, mssg); ++} ++EXPORT_SYMBOL_GPL(mbox_chan_received_data); ++ ++/** ++ * mbox_chan_txdone - A way for controller driver to notify the ++ * framework that the last TX has completed. ++ * @chan: Pointer to the mailbox chan on which TX happened. ++ * @r: Status of last TX - OK or ERROR ++ * ++ * The controller that has IRQ for TX ACK calls this atomic API ++ * to tick the TX state machine. It works only if txdone_irq ++ * is set by the controller. ++ */ ++void mbox_chan_txdone(struct mbox_chan *chan, int r) ++{ ++ if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) { ++ pr_err("Controller can't run the TX ticker\n"); ++ return; ++ } ++ ++ tx_tick(chan, r); ++} ++EXPORT_SYMBOL_GPL(mbox_chan_txdone); ++ ++/** ++ * mbox_client_txdone - The way for a client to run the TX state machine. ++ * @chan: Mailbox channel assigned to this client. ++ * @r: Success status of last transmission. ++ * ++ * The client/protocol had received some 'ACK' packet and it notifies ++ * the API that the last packet was sent successfully. This only works ++ * if the controller can't sense TX-Done. ++ */ ++void mbox_client_txdone(struct mbox_chan *chan, int r) ++{ ++ if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) { ++ pr_err("Client can't run the TX ticker\n"); ++ return; ++ } ++ ++ tx_tick(chan, r); ++} ++EXPORT_SYMBOL_GPL(mbox_client_txdone); ++ ++/** ++ * mbox_client_peek_data - A way for client driver to pull data ++ * received from remote by the controller. ++ * @chan: Mailbox channel assigned to this client. ++ * ++ * A poke to controller driver for any received data. ++ * The data is actually passed onto client via the ++ * mbox_chan_received_data() ++ * The call can be made from atomic context, so the controller's ++ * implementation of peek_data() must not sleep. ++ * ++ * Return: True, if controller has, and is going to push after this, ++ * some data. ++ * False, if controller doesn't have any data to be read. ++ */ ++bool mbox_client_peek_data(struct mbox_chan *chan) ++{ ++ if (chan->mbox->ops->peek_data) ++ return chan->mbox->ops->peek_data(chan); ++ ++ return false; ++} ++EXPORT_SYMBOL_GPL(mbox_client_peek_data); ++ ++/** ++ * mbox_send_message - For client to submit a message to be ++ * sent to the remote. ++ * @chan: Mailbox channel assigned to this client. ++ * @mssg: Client specific message typecasted. ++ * ++ * For client to submit data to the controller destined for a remote ++ * processor. If the client had set 'tx_block', the call will return ++ * either when the remote receives the data or when 'tx_tout' millisecs ++ * run out. ++ * In non-blocking mode, the requests are buffered by the API and a ++ * non-negative token is returned for each queued request. If the request ++ * is not queued, a negative token is returned. Upon failure or successful ++ * TX, the API calls 'tx_done' from atomic context, from which the client ++ * could submit yet another request. ++ * In blocking mode, 'tx_done' is not called, effectively making the ++ * queue length 1. ++ * The pointer to message should be preserved until it is sent ++ * over the chan, i.e, tx_done() is made. ++ * This function could be called from atomic context as it simply ++ * queues the data and returns a token against the request. ++ * ++ * Return: Non-negative integer for successful submission (non-blocking mode) ++ * or transmission over chan (blocking mode). ++ * Negative value denotes failure. ++ */ ++int mbox_send_message(struct mbox_chan *chan, void *mssg) ++{ ++ int t; ++ ++ if (!chan || !chan->cl) ++ return -EINVAL; ++ ++ t = _add_to_rbuf(chan, mssg); ++ if (t < 0) { ++ pr_err("Try increasing MBOX_TX_QUEUE_LEN\n"); ++ return t; ++ } ++ ++ _msg_submit(chan); ++ ++ reinit_completion(&chan->tx_complete); ++ ++ if (chan->txdone_method == TXDONE_BY_POLL) ++ poll_txdone((unsigned long)chan->mbox); ++ ++ if (chan->cl->tx_block && chan->active_req) { ++ unsigned long wait; ++ int ret; ++ ++ if (!chan->cl->tx_tout) /* wait for ever */ ++ wait = msecs_to_jiffies(3600000); ++ else ++ wait = msecs_to_jiffies(chan->cl->tx_tout); ++ ++ ret = wait_for_completion_timeout(&chan->tx_complete, wait); ++ if (ret == 0) { ++ t = -EIO; ++ tx_tick(chan, -EIO); ++ } ++ } ++ ++ return t; ++} ++EXPORT_SYMBOL_GPL(mbox_send_message); ++ ++/** ++ * mbox_request_channel - Request a mailbox channel. ++ * @cl: Identity of the client requesting the channel. ++ * ++ * The Client specifies its requirements and capabilities while asking for ++ * a mailbox channel. It can't be called from atomic context. ++ * The channel is exclusively allocated and can't be used by another ++ * client before the owner calls mbox_free_channel. ++ * After assignment, any packet received on this channel will be ++ * handed over to the client via the 'rx_callback'. ++ * The framework holds reference to the client, so the mbox_client ++ * structure shouldn't be modified until the mbox_free_channel returns. ++ * ++ * Return: Pointer to the channel assigned to the client if successful. ++ * ERR_PTR for request failure. ++ */ ++struct mbox_chan *mbox_request_channel(struct mbox_client *cl) ++{ ++ struct device *dev = cl->dev; ++ struct mbox_controller *mbox; ++ struct of_phandle_args spec; ++ struct mbox_chan *chan; ++ unsigned long flags; ++ int count, i, ret; ++ ++ if (!dev || !dev->of_node) { ++ pr_err("%s: No owner device node\n", __func__); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ count = of_property_count_strings(dev->of_node, "mbox-names"); ++ if (count < 0) { ++ pr_err("%s: mbox-names property of node '%s' missing\n", ++ __func__, dev->of_node->full_name); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ mutex_lock(&con_mutex); ++ ++ ret = -ENODEV; ++ for (i = 0; i < count; i++) { ++ const char *s; ++ ++ if (of_property_read_string_index(dev->of_node, ++ "mbox-names", i, &s)) ++ continue; ++ ++ if (strcmp(cl->chan_name, s)) ++ continue; ++ ++ if (of_parse_phandle_with_args(dev->of_node, ++ "mbox", "#mbox-cells", i, &spec)) ++ continue; ++ ++ chan = NULL; ++ list_for_each_entry(mbox, &mbox_cons, node) ++ if (mbox->dev->of_node == spec.np) { ++ chan = mbox->of_xlate(mbox, &spec); ++ break; ++ } ++ ++ of_node_put(spec.np); ++ ++ if (!chan) ++ continue; ++ ++ ret = -EBUSY; ++ if (!chan->cl && try_module_get(mbox->dev->driver->owner)) ++ break; ++ } ++ ++ if (i == count) { ++ mutex_unlock(&con_mutex); ++ return ERR_PTR(ret); ++ } ++ ++ spin_lock_irqsave(&chan->lock, flags); ++ chan->msg_free = 0; ++ chan->msg_count = 0; ++ chan->active_req = NULL; ++ chan->cl = cl; ++ init_completion(&chan->tx_complete); ++ ++ if (chan->txdone_method == TXDONE_BY_POLL ++ && cl->knows_txdone) ++ chan->txdone_method |= TXDONE_BY_ACK; ++ spin_unlock_irqrestore(&chan->lock, flags); ++ ++ ret = chan->mbox->ops->startup(chan); ++ if (ret) { ++ pr_err("Unable to startup the chan (%d)\n", ret); ++ mbox_free_channel(chan); ++ chan = ERR_PTR(ret); ++ } ++ ++ mutex_unlock(&con_mutex); ++ return chan; ++} ++EXPORT_SYMBOL_GPL(mbox_request_channel); ++ ++/** ++ * mbox_free_channel - The client relinquishes control of a mailbox ++ * channel by this call. ++ * @chan: The mailbox channel to be freed. ++ */ ++void mbox_free_channel(struct mbox_chan *chan) ++{ ++ unsigned long flags; ++ ++ if (!chan || !chan->cl) ++ return; ++ ++ chan->mbox->ops->shutdown(chan); ++ ++ /* The queued TX requests are simply aborted, no callbacks are made */ ++ spin_lock_irqsave(&chan->lock, flags); ++ chan->cl = NULL; ++ chan->active_req = NULL; ++ if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) ++ chan->txdone_method = TXDONE_BY_POLL; ++ ++ module_put(chan->mbox->dev->driver->owner); ++ spin_unlock_irqrestore(&chan->lock, flags); ++} ++EXPORT_SYMBOL_GPL(mbox_free_channel); ++ ++static struct mbox_chan * ++of_mbox_index_xlate(struct mbox_controller *mbox, ++ const struct of_phandle_args *sp) ++{ ++ int ind = sp->args[0]; ++ ++ if (ind >= mbox->num_chans) ++ return NULL; ++ ++ return &mbox->chans[ind]; ++} ++ ++/** ++ * mbox_controller_register - Register the mailbox controller ++ * @mbox: Pointer to the mailbox controller. ++ * ++ * The controller driver registers its communication chans ++ */ ++int mbox_controller_register(struct mbox_controller *mbox) ++{ ++ int i, txdone; ++ ++ /* Sanity check */ ++ if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans) ++ return -EINVAL; ++ ++ if (mbox->txdone_irq) ++ txdone = TXDONE_BY_IRQ; ++ else if (mbox->txdone_poll) ++ txdone = TXDONE_BY_POLL; ++ else /* It has to be ACK then */ ++ txdone = TXDONE_BY_ACK; ++ ++ if (txdone == TXDONE_BY_POLL) { ++ mbox->poll.function = &poll_txdone; ++ mbox->poll.data = (unsigned long)mbox; ++ init_timer(&mbox->poll); ++ } ++ ++ for (i = 0; i < mbox->num_chans; i++) { ++ struct mbox_chan *chan = &mbox->chans[i]; ++ chan->cl = NULL; ++ chan->mbox = mbox; ++ chan->txdone_method = txdone; ++ spin_lock_init(&chan->lock); ++ } ++ ++ if (!mbox->of_xlate) ++ mbox->of_xlate = of_mbox_index_xlate; ++ ++ mutex_lock(&con_mutex); ++ list_add_tail(&mbox->node, &mbox_cons); ++ mutex_unlock(&con_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mbox_controller_register); ++ ++/** ++ * mbox_controller_unregister - UnRegister the mailbox controller ++ * @mbox: Pointer to the mailbox controller. ++ */ ++void mbox_controller_unregister(struct mbox_controller *mbox) ++{ ++ int i; ++ ++ if (!mbox) ++ return; ++ ++ mutex_lock(&con_mutex); ++ ++ list_del(&mbox->node); ++ ++ for (i = 0; i < mbox->num_chans; i++) ++ mbox_free_channel(&mbox->chans[i]); ++ ++ if (mbox->txdone_poll) ++ del_timer_sync(&mbox->poll); ++ ++ mutex_unlock(&con_mutex); ++} ++EXPORT_SYMBOL_GPL(mbox_controller_unregister); +diff -Nur linux-3.14.36/drivers/mailbox/Makefile linux-openelec/drivers/mailbox/Makefile +--- linux-3.14.36/drivers/mailbox/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/mailbox/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -1,3 +1,7 @@ ++# Generic MAILBOX API ++ ++obj-$(CONFIG_MAILBOX) += mailbox.o ++ + obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o + + obj-$(CONFIG_OMAP_MBOX) += omap-mailbox.o +diff -Nur linux-3.14.36/drivers/mailbox/pl320-ipc.c linux-openelec/drivers/mailbox/pl320-ipc.c +--- linux-3.14.36/drivers/mailbox/pl320-ipc.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/mailbox/pl320-ipc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -26,7 +26,7 @@ + #include + #include + +-#include ++#include + + #define IPCMxSOURCE(m) ((m) * 0x40) + #define IPCMxDSET(m) (((m) * 0x40) + 0x004) +diff -Nur linux-3.14.36/drivers/Makefile linux-openelec/drivers/Makefile +--- linux-3.14.36/drivers/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -111,6 +111,7 @@ + obj-$(CONFIG_CPU_FREQ) += cpufreq/ + obj-$(CONFIG_CPU_IDLE) += cpuidle/ + obj-y += mmc/ ++obj-$(CONFIG_ARCH_MXC) += mxc/ + obj-$(CONFIG_MEMSTICK) += memstick/ + obj-y += leds/ + obj-$(CONFIG_INFINIBAND) += infiniband/ +diff -Nur linux-3.14.36/drivers/media/common/b2c2/flexcop-common.h linux-openelec/drivers/media/common/b2c2/flexcop-common.h +--- linux-3.14.36/drivers/media/common/b2c2/flexcop-common.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/common/b2c2/flexcop-common.h 2015-07-24 18:03:30.356842002 -0500 +@@ -91,6 +91,8 @@ + int feedcount; + int pid_filtering; + int fullts_streaming_state; ++ /* the stream will be activated by an externally (by the fe for example) */ ++ int need_external_stream_control; + + /* bus specific callbacks */ + flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *, +@@ -177,6 +179,8 @@ + struct dvb_demux_feed *dvbdmxfeed, int onoff); + void flexcop_hw_filter_init(struct flexcop_device *fc); + ++extern void flexcop_external_stream_control(struct dvb_frontend *fe, u8 onoff); ++ + void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff); + + void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]); +diff -Nur linux-3.14.36/drivers/media/common/b2c2/flexcop-fe-tuner.c linux-openelec/drivers/media/common/b2c2/flexcop-fe-tuner.c +--- linux-3.14.36/drivers/media/common/b2c2/flexcop-fe-tuner.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/common/b2c2/flexcop-fe-tuner.c 2015-07-24 18:03:30.356842002 -0500 +@@ -12,6 +12,7 @@ + #include "cx24113.h" + #include "cx24123.h" + #include "isl6421.h" ++#include "cx24120.h" + #include "mt352.h" + #include "bcm3510.h" + #include "nxt200x.h" +@@ -26,6 +27,15 @@ + #define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \ + (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE))) + ++#if FE_SUPPORTED(BCM3510) || FE_SUPPORTED(CX24120) ++static int flexcop_fe_request_firmware(struct dvb_frontend *fe, ++ const struct firmware **fw, char* name) ++{ ++ struct flexcop_device *fc = fe->dvb->priv; ++ return request_firmware(fw, name, fc->dev); ++} ++#endif ++ + /* lnb control */ + #if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299) + static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +@@ -445,13 +455,6 @@ + + /* AirStar ATSC 1st generation */ + #if FE_SUPPORTED(BCM3510) +-static int flexcop_fe_request_firmware(struct dvb_frontend *fe, +- const struct firmware **fw, char* name) +-{ +- struct flexcop_device *fc = fe->dvb->priv; +- return request_firmware(fw, name, fc->dev); +-} +- + static struct bcm3510_config air2pc_atsc_first_gen_config = { + .demod_address = 0x0f, + .request_firmware = flexcop_fe_request_firmware, +@@ -619,10 +622,40 @@ + #define cablestar2_attach NULL + #endif + ++/* SkyStar S2 PCI DVB-S/S2 card based on Conexant cx24120/cx24118 */ ++#if FE_SUPPORTED(CX24120) && FE_SUPPORTED(ISL6421) ++static const struct cx24120_config skystar2_rev3_3_cx24120_config = { ++ .i2c_addr = 0x55, ++ .request_firmware = flexcop_fe_request_firmware, ++}; ++ ++static int skystarS2_rev33_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) ++{ ++// struct dvb_frontend_ops *ops; ++ ++ fc->fe = dvb_attach(cx24120_attach, ++ &skystar2_rev3_3_cx24120_config, i2c); ++ if (fc->fe == NULL) return 0; ++ fc->dev_type = FC_SKYS2_REV33; ++ fc->fc_i2c_adap[2].no_base_addr = 1; ++ if ( (dvb_attach(isl6421_attach, fc->fe, ++ &fc->fc_i2c_adap[2].i2c_adap, 0x08, 0, 0, false) == NULL) ) { ++ err("ISL6421 could NOT be attached!"); ++ return 0; ++ } ++ info("ISL6421 successfully attached."); ++// ops = &fc->fe->ops; ++ return 1; ++} ++#else ++#define skystarS2_rev33_attach NULL ++#endif ++ + static struct { + flexcop_device_type_t type; + int (*attach)(struct flexcop_device *, struct i2c_adapter *); + } flexcop_frontends[] = { ++ { FC_SKYS2_REV33, skystarS2_rev33_attach }, + { FC_SKY_REV27, skystar2_rev27_attach }, + { FC_SKY_REV28, skystar2_rev28_attach }, + { FC_SKY_REV26, skystar2_rev26_attach }, +diff -Nur linux-3.14.36/drivers/media/common/b2c2/flexcop-hw-filter.c linux-openelec/drivers/media/common/b2c2/flexcop-hw-filter.c +--- linux-3.14.36/drivers/media/common/b2c2/flexcop-hw-filter.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/common/b2c2/flexcop-hw-filter.c 2015-07-24 18:03:30.356842002 -0500 +@@ -11,6 +11,12 @@ + deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off"); + } + ++void flexcop_external_stream_control(struct dvb_frontend *fe, u8 onoff) ++{ ++ struct flexcop_device *fc = fe->dvb->priv; ++ flexcop_rcv_data_ctrl(fc, onoff); ++} ++ + void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff) + { + flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff); +@@ -199,6 +205,7 @@ + + /* if it was the first or last feed request change the stream-status */ + if (fc->feedcount == onoff) { ++ if (!fc->need_external_stream_control) + flexcop_rcv_data_ctrl(fc, onoff); + if (fc->stream_control) /* device specific stream control */ + fc->stream_control(fc, onoff); +diff -Nur linux-3.14.36/drivers/media/common/b2c2/flexcop-misc.c linux-openelec/drivers/media/common/b2c2/flexcop-misc.c +--- linux-3.14.36/drivers/media/common/b2c2/flexcop-misc.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/common/b2c2/flexcop-misc.c 2015-07-24 18:03:30.356842002 -0500 +@@ -56,6 +56,7 @@ + [FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6", + [FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u", + [FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8", ++ [FC_SKYS2_REV33]= "Sky2PC/SkyStar S2 DVB-S/S2 rev 3.3", + }; + + static const char *flexcop_bus_names[] = { +diff -Nur linux-3.14.36/drivers/media/common/b2c2/flexcop-reg.h linux-openelec/drivers/media/common/b2c2/flexcop-reg.h +--- linux-3.14.36/drivers/media/common/b2c2/flexcop-reg.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/common/b2c2/flexcop-reg.h 2015-07-24 18:03:30.356842002 -0500 +@@ -24,6 +24,7 @@ + FC_SKY_REV26, + FC_SKY_REV27, + FC_SKY_REV28, ++ FC_SKYS2_REV33, + } flexcop_device_type_t; + + typedef enum { +diff -Nur linux-3.14.36/drivers/media/common/b2c2/Kconfig linux-openelec/drivers/media/common/b2c2/Kconfig +--- linux-3.14.36/drivers/media/common/b2c2/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/common/b2c2/Kconfig 2015-07-24 18:03:30.356842002 -0500 +@@ -3,6 +3,7 @@ + depends on DVB_CORE && I2C + depends on DVB_B2C2_FLEXCOP_PCI || DVB_B2C2_FLEXCOP_USB + default y ++ select DVB_CX24120 if !DVB_FE_CUSTOMISE + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT +diff -Nur linux-3.14.36/drivers/media/dvb-core/dvb-usb-ids.h linux-openelec/drivers/media/dvb-core/dvb-usb-ids.h +--- linux-3.14.36/drivers/media/dvb-core/dvb-usb-ids.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-core/dvb-usb-ids.h 2015-07-24 18:03:30.144842002 -0500 +@@ -285,6 +285,8 @@ + #define USB_PID_REALTEK_RTL2832U 0x2832 + #define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007 + #define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a ++#define USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI 0x3012 ++#define USB_PID_TECHNOTREND_TVSTICK_CT2_4400 0x3014 + #define USB_PID_NEBULA_DIGITV 0x0201 + #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 + #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/cx24120.c linux-openelec/drivers/media/dvb-frontends/cx24120.c +--- linux-3.14.36/drivers/media/dvb-frontends/cx24120.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/cx24120.c 2015-07-24 18:03:30.360842002 -0500 +@@ -0,0 +1,1053 @@ ++/* ++ Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver ++ Version 0.0.4a 03.04.2012 ++ ++ Copyright (C) 2009 Sergey Tyurin ++ Updated 2012 by Jannis Achstetter ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "cx24120.h" ++#include "cx24120_const.h" ++ ++//========================== ++#define dbginfo(args...) do { if(cx24120_debug) { printk(KERN_DEBUG "CX24120: %s: >>> ", __func__); \ ++ printk(args); } } while (0) ++#define info(args...) do { printk(KERN_INFO "CX24120: %s: -> ", __func__); \ ++ printk(args); } while (0) ++#define err(args...) do { printk(KERN_ERR "CX24120: %s: ### ERROR: ", __func__); \ ++ printk(args); } while (0) ++//========================== ++ ++static int cx24120_debug=0; ++static int reg_debug=0; ++MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24120/CX24118 hardware"); ++module_param(cx24120_debug, int, 0644); ++MODULE_PARM_DESC(cx24120_debug, "Activates frontend debugging (default:0)"); ++ ++// ############################## ++struct cx24120_state { ++ struct i2c_adapter *i2c; ++ const struct cx24120_config *config; ++ struct dvb_frontend frontend; ++ u8 need_set_mpeg_out; ++ u8 attached; ++ u8 dvb_s2_mode; ++ u8 cold_init; ++}; ++// ##################################### ++// #### Command message to firmware #### ++struct cx24120_cmd { // total size = 36 ++ u8 id; // [00] - message id ++ u8 arg[30]; // [04] - message first byte ++ u8 len; // [34] - message lengh or first registers to read ++ u8 reg; // [35] - number of registers to read ++}; ++ ++//=================================================================== ++static int cx24120_readreg(struct cx24120_state *state, u8 reg) ++{ ++ int ret; ++ u8 buf = 0; ++ struct i2c_msg msg[] = { ++ { .addr = state->config->i2c_addr, ++ .flags = 0, ++ .len = 1, ++ .buf = ® }, ++ ++ { .addr = state->config->i2c_addr, ++ .flags = I2C_M_RD, ++ .len = 1, ++ .buf = &buf } ++ }; ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret != 2) { ++ err("Read error: reg=0x%02x, ret=0x%02x)\n", reg, ret); ++ return ret; ++ } ++ if (reg_debug) dbginfo("reg=0x%02x; data=0x%02x\n", reg, buf); ++ return buf; ++} // end cx24120_readreg ++//=================================================================== ++static int cx24120_writereg(struct cx24120_state *state, u8 reg, u8 data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = state->config->i2c_addr, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 }; ++ int ret; ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) { ++ err("Write error: i2c_write error(err == %i, 0x%02x: 0x%02x)\n", ret, reg, data); ++ return ret; ++ } ++ if (reg_debug) dbginfo("reg=0x%02x; data=0x%02x\n", reg, data); ++ return 0; ++} // end cx24120_writereg ++//=================================================================== ++static int cx24120_writeregN(struct cx24120_state *state, u8 reg, const u8 *values, u16 len, u8 incr) ++{ ++ u8 buf[5]; /* maximum 4 data bytes at once - flexcop limitation (very limited i2c-interface this one) */ ++ struct i2c_msg msg = { ++ .addr = state->config->i2c_addr, ++ .flags = 0, ++ .buf = buf, ++ .len = len }; ++ int ret; ++ ++ do { ++ buf[0] = reg; ++ msg.len = len > 4 ? 4 : len; ++ memcpy(&buf[1], values, msg.len); ++ len -= msg.len; // data length revers counter ++ values += msg.len; // incr data pointer ++ if (incr) reg += msg.len; ++ msg.len++; /* don't forget the addr byte */ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) { ++ err("i2c_write error(err == %i, 0x%02x)\n", ret, reg); ++ return ret; ++ } ++ if (reg_debug) { ++ if( !(reg == 0xFA) && !(reg == 0x20) && !(reg == 0x21)) { // Exclude firmware upload & diseqc messages ++ dbginfo("reg=0x%02x; data=0x%02x,0x%02x,0x%02x,0x%02x\n", // from debug ++ reg, buf[1], buf[2], buf[3], buf[4]); ++ } ++ } ++ } while (len); ++ return 0; ++} // end cx24120_writeregN ++//=================================================================== ++static struct dvb_frontend_ops cx24120_ops; ++//=================================================================== ++struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, struct i2c_adapter *i2c) ++{ ++ struct cx24120_state *state = NULL; ++ int demod_rev; ++ ++ info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner\n"); ++ info("Driver version: 'SVT - 0.0.4a 03.04.2012'\n"); ++ state = kzalloc(sizeof(struct cx24120_state), ++ GFP_KERNEL); ++ if (state == NULL) { ++ err("### Unable to allocate memory for cx24120_state structure. :(\n"); ++ goto error; ++ } ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ /* check if the demod is present and has proper type */ ++ demod_rev = cx24120_readreg(state, CX24120_REG_REVISION); ++ switch (demod_rev) { ++ case 0x07: ++ info("Demod CX24120 rev. 0x07 detected.\n"); ++ break; ++ case 0x05: ++ info("Demod CX24120 rev. 0x05 detected.\n"); ++ break; ++ default: ++ err("### Unsupported demod revision: 0x%x detected. Exit.\n", demod_rev); ++ goto error; ++ } ++ /* create dvb_frontend */ ++ state->attached = 0x10; // set attached flag ++ state->cold_init=0; ++ memcpy(&state->frontend.ops, &cx24120_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner ATTACHED.\n"); ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(cx24120_attach); // end cx24120_attach ++//=================================================================== ++static int cx24120_test_rom(struct cx24120_state *state) ++{ ++ int err, ret; ++ err = cx24120_readreg(state, 0xFD); ++ if (err & 4 ) ++ { ++ ret = cx24120_readreg(state, 0xDF) & 0xFE; ++ err = cx24120_writereg(state, 0xDF, ret); ++ } ++ return err; ++} // end cx24120_test_rom ++//=================================================================== ++static int cx24120_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ ++ *snr = (cx24120_readreg(state, CX24120_REG_QUALITY_H)<<8) | ++ (cx24120_readreg(state, CX24120_REG_QUALITY_L)); ++ dbginfo("read SNR index = %d\n", *snr); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_read_snr); // end cx24120_read_snr ++//=================================================================== ++static int cx24120_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ ++ *ber = (cx24120_readreg(state, CX24120_REG_BER_HH) << 24) | // BER high byte of high word ++ (cx24120_readreg(state, CX24120_REG_BER_HL) << 16) | // BER low byte of high word ++ (cx24120_readreg(state, CX24120_REG_BER_LH) << 8) | // BER high byte of low word ++ cx24120_readreg(state, CX24120_REG_BER_LL); // BER low byte of low word ++ dbginfo("read BER index = %d\n", *ber); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_read_ber); // end cx24120_read_ber ++//=================================================================== ++static int cx24120_message_send(struct cx24120_state *state, struct cx24120_cmd *cmd); ++//=================================================================== ++static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state, u8 flag) ++{ ++ u8 tristate; ++ struct cx24120_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ ++ cmd.id = 0x13; // (19) message Enable/Disable mpeg output ??? ++ cmd.arg[0] = 1; ++ cmd.arg[1] = 0; ++ tristate = flag ? 0 : (u8)(-1); ++ cmd.arg[2] = tristate; ++ cmd.arg[3] = 1; ++ cmd.len = 4; ++ ++ if(flag) dbginfo("MPEG output DISABLED\n"); ++ else dbginfo("MPEG output ENABLED\n"); ++ ++ return cx24120_message_send(state, &cmd); ++} // end cx24120_msg_mpeg_output_global_config ++//=================================================================== ++static int cx24120_message_send(struct cx24120_state *state, struct cx24120_cmd *cmd) ++{ ++ u8 xxzz; ++ u32 msg_cmd_mask; ++ int ret, ficus; ++ ++ if(state->dvb_s2_mode & 0x02) { // is MPEG enabled? ++ // if yes: ++ xxzz = cmd->id - 0x11; // look for specific message id ++ if ( xxzz <= 0x13 ) { ++ msg_cmd_mask = 1 << xxzz; ++ //0x0F8021 // if cmd_id 17 or 22 or 33-36, 42, 47, 57-61 etc. disable mpeg output ++ if ( msg_cmd_mask & 0x0F8021 ) { // 000011111000000000100001b ++ cx24120_msg_mpeg_output_global_config(state, 0); ++ msleep(100); ++ state->dvb_s2_mode &= 0xFD; // reset mpeg out enable flag ++ } ++ } ++ } ++ ret = cx24120_writereg(state, 0x00 /* reg id*/, cmd->id /* value */); // message start & target ++ ret = cx24120_writeregN(state, 0x01 /* reg msg*/, &cmd->arg[0], cmd->len /* len*/, 1 /* incr */); // message data ++ ret = cx24120_writereg(state, 0x1F /* reg msg_end */, 0x01 /* value */); // message end ++ ++ ficus = 1000; ++ while ( cx24120_readreg(state, 0x1F)) { // is command done??? ++ msleep(1); ++ if( !(--ficus)) { ++ err("Too long waiting 'done' state from reg(0x1F). :(\n"); ++ return -EREMOTEIO; ++ } ++ } ++ dbginfo("Successfully send message 0x%02x\n", cmd->id); ++ ++ if ( cmd->reg > 30 ) { ++ err("Too much registers to read. cmd->reg = %d", cmd->reg); ++ return -EREMOTEIO; ++ } ++ ficus = 0; ++ if ( cmd->reg ) { // cmd->reg - qty consecutive regs to read ++ while ( ficus < cmd->reg ){ // starts from reg No cmd->len ++ // number of registers to read is cmd->reg ++ // and write results starts from cmd->arg[0]. ++ cmd->arg[ficus] = cx24120_readreg(state, (cmd->len+ficus+1)); ++ ++ficus; ++ } ++ } ++ return 0; ++} // end cx24120_message_send ++//=================================================================== ++static int cx24120_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ u32 srate, freq; ++ fe_code_rate_t fec; ++ fe_spectral_inversion_t inversion; ++ u8 smbr1, smbr2; ++ int ret; ++ ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ ++ cmd.id = CMD_TUNEREQUEST; // 0x11 set tuner parametrs ++ cmd.len = 15; ++ ++ freq = p->frequency; ++ srate = p->symbol_rate; ++ fec = p->fec_inner; ++ inversion = p->inversion; ++ ++ // check symbol rate ++ if ( srate > 31000000 ) { // if symbol rate > 31 000 ++ smbr1 = (-(srate < 31000001) & 3) + 2; // ebp ++ smbr2 = (-(srate < 31000001) & 6) + 4; // edi ++ } else { ++ smbr1 = 3; ++ smbr2 = 6; ++ } ++ ++ ret = cx24120_writereg(state, 0xE6, smbr1); ++ ret = cx24120_readreg(state, 0xF0); ++ ret &= 0xFFFFFFF0; ++ ret |= smbr2; ++ ret = cx24120_writereg(state, 0xF0, ret); ++ ++ cmd.arg[0] = 0; // CMD_TUNER_REQUEST ++ ++ // Frequency ++ cmd.arg[1] = (freq & 0xFF0000) >> 16; /* intermediate frequency in kHz */ ++ cmd.arg[2] = (freq & 0x00FF00) >> 8; ++ cmd.arg[3] = (freq & 0x0000FF); ++ ++ // Symbol Rate ++ cmd.arg[4] = ((srate/1000) & 0xFF00) >> 8; ++ cmd.arg[5] = ((srate/1000) & 0x00FF); ++ ++ // Inversion ++ if ( inversion ) { ++ if ( inversion == 1 ) cmd.arg[6] = 4; ++ else cmd.arg[6] = 0x0C; ++ } else { ++ cmd.arg[6] = 0; ++ } ++ ++ // FEC ++ switch ( fec ) // fec = p->u.qpsk.fec_inner ++ { ++ case 1: // FEC_1_2 ++ cmd.arg[7] = 0x2E; break; // [11] = 0 by memset ++ case 2: // FEC_2_3 ++ cmd.arg[7] = 0x2F; break; ++ case 3: // FEC_3_4 ++ cmd.arg[7] = 0x30; break; ++ case 5: // FEC_5_6 ++ cmd.arg[7] = 0x31; break; ++ case 7: // FEC_7_8 ++ cmd.arg[7] = 0x32; break; ++ default: // FEC_NONE, FEC_4_5, FEC_6_7, FEC_8_9, ++ // FEC_AUTO, FEC_3_5, FEC_9_10 ++ if ( state->dvb_s2_mode & 1 ) { // if DVB-S2 mode ++ cmd.arg[7] = 0; ++ cmd.arg[11] = 0; ++ } else { ++ cmd.arg[7] = 0x2E; ++ cmd.arg[11] = 0xAC; ++ } ++ break; ++ } ++ cmd.arg[8] = 0x13; ++ cmd.arg[9] = 0x88; ++ cmd.arg[10] = 0; ++ cmd.arg[12] = smbr2; ++ cmd.arg[13] = smbr1; ++ cmd.arg[14] = 0; ++ ++ state->need_set_mpeg_out |= 0x01; // after tune we need restart mpeg out ????? ++ ++ return cx24120_message_send(state, &cmd); ++ ++} ++EXPORT_SYMBOL(cx24120_set_frontend); // end cx24120_set_frontend ++//=================================================================== ++void cx24120_message_fill(struct cx24120_cmd *cmd, ++ u8 msg_id, ++ u8 *msg_addr, ++ u8 msg_len, ++ u8 num_regs) ++{ ++ cmd->id = msg_id; ++ memcpy(&cmd->arg[0], msg_addr, msg_len); ++ cmd->len = msg_len; ++ cmd->reg = num_regs; ++} // end cx24120_message_fill ++//=================================================================== ++static int cx24120_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ int result, sigstr_h, sigstr_l; ++ ++ cx24120_message_fill(&cmd, 0x1A/*msg_id*/, &cx24120_msg_read_sigstr[0], 1/*msg_len*/, 0/*num_regs*/); ++ ++ if( !(cx24120_message_send(state, &cmd)) ) { ++ sigstr_h = (cx24120_readreg(state, CX24120_REG_SIGSTR_H) >> 6) << 8; ++ sigstr_l = cx24120_readreg(state, CX24120_REG_SIGSTR_L ); ++ dbginfo("Signal strength from firmware= 0x%x\n", (sigstr_h | sigstr_l)); ++ *signal_strength = ((sigstr_h | sigstr_l) << 5) & 0x0000FFFF; ++ dbginfo("Signal strength= 0x%x\n", *signal_strength); ++ result = 0; ++ } else { ++ err("error reading signal strength\n"); ++ result = -EREMOTEIO; ++ } ++ return result; ++} ++EXPORT_SYMBOL(cx24120_read_signal_strength); // end cx24120_read_signal_strength ++//=================================================================== ++static int cx24120_msg_mpeg_output_config(struct cx24120_state *state, u8 num, ++ struct cx24120_skystar2_mpeg_config *config_msg) ++{ ++ struct cx24120_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ ++ cmd.id = CMD_MPEG_INIT; // cmd->id=20 - message id ++ cmd.len = 7; ++ cmd.arg[0] = num; // sequental number - can be 0,1,2 ++ cmd.arg[1] = ((config_msg->x1 & 0x01) << 1) | ++ ((config_msg->x1 >> 1) & 0x01); ++ cmd.arg[2] = 0x05; ++ cmd.arg[3] = 0x02; ++ cmd.arg[4] = ((config_msg->x2 >> 1) & 0x01); ++ cmd.arg[5] = (config_msg->x2 & 0xF0) | (config_msg->x3 & 0x0F); ++ cmd.arg[6] = state->attached; /* 0x10 if succesfully attached */ ++ ++ return cx24120_message_send(state, &cmd); ++} // end cx24120_msg_mpeg_output_config ++//=================================================================== ++static int cx24120_diseqc_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ ++ cmd.id = CMD_DISEQC_BURST; ++ cmd.arg[0] = 0x00; ++ if (burst) ++ cmd.arg[1] = 0x01; ++ dbginfo("burst sent.\n"); ++ ++ return cx24120_message_send(state, &cmd); ++} ++EXPORT_SYMBOL(cx24120_diseqc_send_burst); // end cx24120_diseqc_send_burst ++//=================================================================== ++static int cx24120_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ ++ dbginfo("cmd(0x23,4) - tone = %d\n", tone); ++ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { ++ err("Invalid tone=%d\n", tone); ++ return -EINVAL; ++ } ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ cmd.id = CMD_SETTONE; // 0x23 ++ cmd.len = 4; ++ if (!tone) ++ cmd.arg[3] = 0x01; ++ return cx24120_message_send(state, &cmd); ++} ++EXPORT_SYMBOL(cx24120_set_tone); // end cx24120_set_tone ++//=================================================================== ++static int cx24120_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ cmd.id = CMD_SETVOLTAGE; // ++ cmd.len = 2; ++ if (!(voltage - 1)) ++ cmd.arg[1] = 0x01; ++ return cx24120_message_send(state, &cmd); ++} ++EXPORT_SYMBOL(cx24120_set_voltage); // end cx24120_set_voltage ++//=================================================================== ++static int cx24120_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *d) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ int back_count; ++ ++ dbginfo("Start sending diseqc sequence===============\n"); ++ ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ ++ cmd.id = CMD_DISEQC_MSG1; // 0x20 ++ cmd.len = 11; ++ cmd.arg[0] = 0x00; ++ cmd.arg[1] = 0x00; ++ cmd.arg[2] = 0x03; ++ cmd.arg[3] = 0x16; ++ cmd.arg[4] = 0x28; ++ cmd.arg[5] = 0x01; ++ cmd.arg[6] = 0x01; ++ cmd.arg[7] = 0x14; ++ cmd.arg[8] = 0x19; ++ cmd.arg[9] = 0x14; ++ cmd.arg[10] = 0x1E; ++ if ( cx24120_message_send(state, &cmd) ) { ++ err("send 1st message(0x%x) filed==========\n", cmd.id); ++ return -EREMOTEIO; ++ } ++ cmd.id = CMD_DISEQC_MSG2; // 0x21 ++ cmd.len = d->msg_len + 6; ++ cmd.arg[0] = 0x00; ++ cmd.arg[1] = 0x01; ++ cmd.arg[2] = 0x02; ++ cmd.arg[3] = 0x00; ++ cmd.arg[4] = 0x00; ++ cmd.arg[5] = d->msg_len; ++ ++ memcpy(&cmd.arg[6], &d->msg, d->msg_len); ++ ++ if ( cx24120_message_send(state, &cmd) ) { ++ err("send 2d message(0x%x) filed========\n", cmd.id); ++ return -EREMOTEIO; ++ } ++ back_count = 100; ++ do { ++ if ( !(cx24120_readreg(state, 0x93) & 0x01) ) { ++ dbginfo("diseqc sequence sent success==========.\n"); ++ return 0; ++ } ++ msleep(5); ++ --back_count; ++ } while ( back_count ); ++ err("Too long waiting for diseqc.=============\n"); ++ return -ETIMEDOUT; ++} ++EXPORT_SYMBOL(cx24120_send_diseqc_msg); // end cx24120_send_diseqc_msg ++//=================================================================== ++static int cx24120_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ int ret, clock_seq_num, GettedFEC; ++ u8 mode_code, mode_8PSK_flag, attached_flag, clock_id; ++ ++ ret = cx24120_readreg(state, CX24120_REG_STATUS); //0x3A ++ dbginfo("status = 0x%x\n", ret); ++ *status = 0; ++ if ( ret & CX24120_HAS_SIGNAL ) *status = FE_HAS_SIGNAL; ++ if ( ret & CX24120_HAS_CARRIER) *status |= FE_HAS_CARRIER; ++ if ( ret & CX24120_HAS_VITERBI) *status |= (FE_HAS_VITERBI + FE_HAS_SYNC); ++ ++ if ( ret & CX24120_HAS_LOCK ) { // 0x08 ++ *status |= FE_HAS_LOCK; ++ if ( state->need_set_mpeg_out & 1 ) { // just tuned??? ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ cmd.id = CMD_CLOCK_READ; ++ cmd.arg[0] = 0x00; ++ cmd.len = 1; // cmd.reg != 0, so it is first register to read ++ cmd.reg = 6; // number of registers to read (0x01-0x06) ++ if ( !cx24120_message_send(state, &cmd) ) { // in cmd[0]-[5] - result ++ // 0x02-0x07 ++ ret = cx24120_readreg(state, CX24120_REG_FECMODE) & 0x3F; // ntv - 0x8E(142) & 3F = 14 ++ GettedFEC = ret; // 0x0d= 13 ++ dbginfo("Get FEC: %d\n", ret); ++ if ( state->dvb_s2_mode & 0x01 ) { // is DVB-S2? ++ switch (ret-4) { ++ case 0: ++ mode_code = 0x01; goto mode_QPSK; // FEC_1_2 - qpsk only ++ case 1: ++ case 8: ++ mode_code = 0x64; goto mode_8PSK; // FEC_3_5 (10)- 8PSK only ++ case 2: ++ case 9: ++ mode_code = 0x02; goto mode_8PSK; // FEC_2_3 ++ case 3: ++ case 10: ++ mode_code = 0x03; goto mode_8PSK; // FEC_3_4 // 14-4=10 - ntv+ ++ case 4: ++ mode_code = 0x04; goto mode_QPSK; // FEC_4_5 - qpsk only ++ case 5: ++ case 11: ++ mode_code = 0x05; goto mode_8PSK; // FEC_5_6 ++ case 6: ++ case 12: ++ mode_code = 0x08; goto mode_8PSK; // FEC_8_9 ++ case 7: ++ case 13: ++ mode_code = 0x65; goto mode_8PSK; // FEC_9_10 (11)- 8PSK only ++ default: ++ info("Unknown DVB-S2 modefec (not QPSK or 8PSK): %d\n", ret-4); ++ mode_code = 0x01; // set like for mode 0 ++ mode_8PSK: ++ if ( ret > 11 ) { // 14 ++ mode_8PSK_flag = 0x63; // DVB-S2-8PSK flag ++ dbginfo("DVB-S2: 8PSK mode: %d, mode_code= 0x%x\n", ret-4, mode_code); ++ } else { ++ mode_QPSK: ++ mode_8PSK_flag = 0x00; ++ dbginfo("DVB-S2: QPSK mode: %d\n", ret-4); ++ } ++ break; ++ } // end switch ++ } // end if dvb_s2_mode // dvb-s2 ++ else { // state->dvb_s2_mode & 1 = 0 -> #### DVB-S ++ switch ( ret - 2 ) { ++ case 0: ++ mode_code = 2; break; // FEC_2_3 ++ case 1: ++ mode_code = 3; break; // FEC_3_4 ++ case 2: ++ mode_code = 4; break; // FEC_4_5 ++ case 3: ++ mode_code = 5; break; // FEC_5_6 ++ case 4: ++ mode_code = 6; break; // FEC_6_7 ++ case 5: ++ mode_code = 7; break; // FEC_7_8 ++ default: ++ mode_code = 1;break; // FEC_1_2 ++ } ++ mode_8PSK_flag = 0; ++ } // end of switch for dvb-s ++ ++ attached_flag = 0x10; ++ if (state->attached == 0x10) // must be 0x10 if successfully attached in flexcop_fe_tuner ++ attached_flag = 0; ++ ret = 0; ++ if ( state->dvb_s2_mode & 0x01 ) // if dvb-s2 ++ ret = (cx24120_readreg(state, CX24120_REG_FECMODE) >> 7) & 0x01; // QPSK or 8PSK ??? ++ // bit 4 bit 5 bit 0 bit 3 ++ clock_id = (ret << 3) | attached_flag | (state->dvb_s2_mode & 1) | 4; // possible id: 4, 5, 13. 12-impossible, ++ // ntv S2 = 0x8E -> 8 | 1 | 4 = 13 // because 7th bit of ret - is S2 flag ++ // 1/2 S2 = 0x0d -> 0 | 1 | 4 = 5 ++ dbginfo("Check clock table for: clock_id=0x%x, 8PSK_mask=0x%x, mode_code=0x%x\n", ++ clock_id, mode_8PSK_flag, mode_code); ++ ++ clock_seq_num = 0; ++ while ( (clock_ratios_table[clock_seq_num].ratio_id != clock_id) || ++ (clock_ratios_table[clock_seq_num].mode_xPSK != mode_8PSK_flag) || ++ (clock_ratios_table[clock_seq_num].fec_mode != mode_code) ) ++ { ++ /* dbginfo("Check table string(%d): clock_id=%d, 8PSK_flag=%d, mode_code=%d\n", clock_seq_num, ++ * clock_ratios_table[clock_seq_num].ratio_id, ++ * clock_ratios_table[clock_seq_num].mode_xPSK, ++ * clock_ratios_table[clock_seq_num].fec_mode); ++ */ ++ ++clock_seq_num; ++ if ( clock_seq_num == ARRAY_SIZE(clock_ratios_table) ) { ++ info("Check in clock table filed: unsupported modulation tuned - data reception in danger. :(\n"); ++ goto settings_end; ++ } ++ } ++ //############################### ++ dbginfo("Check succesful: GetFEC: %d; post lock: m=%d, n=%d; clock_seq_idx: %d m=%d, n=%d, rate=%d\n", ++ GettedFEC, ++ cmd.arg[2] | (cmd.arg[1] << 8) | (cmd.arg[0] << 16), // registers was readed early ++ cmd.arg[5] | (cmd.arg[4] << 8) | (cmd.arg[3] << 16), // in message with id = 0x16 ++ clock_seq_num, ++ clock_ratios_table[clock_seq_num].m_rat, ++ clock_ratios_table[clock_seq_num].n_rat, ++ clock_ratios_table[clock_seq_num].rate); ++ //############################### ++ cmd.id = CMD_CLOCK_SET; ++ cmd.len = 10; ++ cmd.reg = 0; ++ cmd.arg[0] = 0; ++ cmd.arg[1] = state->attached; // must be 0x10 if successfully attached in flexcop_fe_tuner ++ ++ cmd.arg[2] = (clock_ratios_table[clock_seq_num].m_rat >> 16) & 0xFF; ++ cmd.arg[3] = (clock_ratios_table[clock_seq_num].m_rat >> 8) & 0xFF; ++ cmd.arg[4] = (clock_ratios_table[clock_seq_num].m_rat >> 0) & 0xFF; ++ ++ cmd.arg[5] = (clock_ratios_table[clock_seq_num].n_rat >> 16) & 0xFF; ++ cmd.arg[6] = (clock_ratios_table[clock_seq_num].n_rat >> 8) & 0xFF; ++ cmd.arg[7] = (clock_ratios_table[clock_seq_num].n_rat >> 0) & 0xFF; ++ ++ cmd.arg[8] = (clock_ratios_table[clock_seq_num].rate >> 8) & 0xFF; ++ cmd.arg[9] = (clock_ratios_table[clock_seq_num].rate >> 0) & 0xFF; ++ ++ cx24120_message_send(state, &cmd); ++ ++ settings_end: ++ msleep(200); ++ cx24120_msg_mpeg_output_global_config(state, 1); ++ state->dvb_s2_mode |= 0x02; // set mpeg flag ++ state->need_set_mpeg_out &= 0xFE; // clocks set done -> clear flag ++ } ++ } ++ } ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_read_status); // end cx24120_read_status ++//=================================================================== ++int cx24120_init(struct dvb_frontend *fe) ++{ ++ const struct firmware *fw; ++ struct cx24120_state *state = fe->demodulator_priv; ++ struct cx24120_cmd cmd; ++ u8 ret, ret_EA, reg1, fL, fH; ++ u32 vco, xtal_khz; ++ u64 inv_vco, res, xxyyzz; ++ int reset_result; ++ ++ if( state->cold_init ) return 0; ++ ++ ret = cx24120_writereg(state, 0xEA, 0x00); ++ ret = cx24120_test_rom(state); ++ ret = cx24120_readreg(state, 0xFB) & 0xFE; ++ ret = cx24120_writereg(state, 0xFB, ret); ++ ret = cx24120_readreg(state, 0xFC) & 0xFE; ++ ret = cx24120_writereg(state, 0xFC, ret); ++ ret = cx24120_writereg(state, 0xC3, 0x04); ++ ret = cx24120_writereg(state, 0xC4, 0x04); ++ ret = cx24120_writereg(state, 0xCE, 0x00); ++ ret = cx24120_writereg(state, 0xCF, 0x00); ++ ret_EA = cx24120_readreg(state, 0xEA) & 0xFE; ++ ret = cx24120_writereg(state, 0xEA, ret_EA); ++ ret = cx24120_writereg(state, 0xEB, 0x0C); ++ ret = cx24120_writereg(state, 0xEC, 0x06); ++ ret = cx24120_writereg(state, 0xED, 0x05); ++ ret = cx24120_writereg(state, 0xEE, 0x03); ++ ret = cx24120_writereg(state, 0xEF, 0x05); ++ ret = cx24120_writereg(state, 0xF3, 0x03); ++ ret = cx24120_writereg(state, 0xF4, 0x44); ++ ++ reg1 = 0xF0; ++ do { ++ cx24120_writereg(state, reg1, 0x04); ++ cx24120_writereg(state, reg1 - 10, 0x02); ++ ++reg1; ++ } while ( reg1 != 0xF3 ); ++ ++ ret = cx24120_writereg(state, 0xEA, (ret_EA | 0x01)); ++ reg1 = 0xC5; ++ do { ++ ret = cx24120_writereg(state, reg1, 0x00); ++ ret = cx24120_writereg(state, reg1 + 1, 0x00); ++ reg1 += 2; ++ } while ( reg1 != 0xCB ); ++ ++ ret = cx24120_writereg(state, 0xE4, 0x03); ++ ret = cx24120_writereg(state, 0xEB, 0x0A); ++ ++ dbginfo("Requesting firmware (%s) to download...\n", CX24120_FIRMWARE); ++ ret = state->config->request_firmware(fe, &fw, CX24120_FIRMWARE); ++ if (ret) { ++ err("Could not load firmware (%s): %d\n", CX24120_FIRMWARE, ret); ++ return ret; ++ } ++ dbginfo("Firmware found and it size is %d bytes (%02x %02x .. %02x %02x)\n", ++ (int)fw->size, // firmware_size in bytes u32* ++ fw->data[0], // fw 1st byte ++ fw->data[1], // fw 2d byte ++ fw->data[fw->size - 2], // fw before last byte ++ fw->data[fw->size - 1]); // fw last byte ++ ++ ret = cx24120_test_rom(state); ++ ret = cx24120_readreg(state, 0xFB) & 0xFE; ++ ret = cx24120_writereg(state, 0xFB, ret); ++ ret = cx24120_writereg(state, 0xE0, 0x76); ++ ret = cx24120_writereg(state, 0xF7, 0x81); ++ ret = cx24120_writereg(state, 0xF8, 0x00); ++ ret = cx24120_writereg(state, 0xF9, 0x00); ++ ret = cx24120_writeregN(state, 0xFA, fw->data, (fw->size - 1), 0x00); ++ ret = cx24120_writereg(state, 0xF7, 0xC0); ++ ret = cx24120_writereg(state, 0xE0, 0x00); ++ ret = (fw->size - 2) & 0x00FF; ++ ret = cx24120_writereg(state, 0xF8, ret); // ret now is 0x7a ++ ret = ((fw->size - 2) >> 8) & 0x00FF; ++ ret = cx24120_writereg(state, 0xF9, ret); // ret now is 0xaf ++ ret = cx24120_writereg(state, 0xF7, 0x00); ++ ret = cx24120_writereg(state, 0xDC, 0x00); ++ ret = cx24120_writereg(state, 0xDC, 0x07); ++ msleep(500); ++ ++ ret = cx24120_readreg(state, 0xE1); // now is 0xd5 - last byte of the firmware ++ if ( ret == fw->data[fw->size - 1] ) { ++ dbginfo("Firmware uploaded successfully\n"); ++ reset_result = 0; ++ } else { ++ err("Firmware upload failed. Last byte returned=0x%x\n", ret ); ++ reset_result = -EREMOTEIO; ++ } ++ ret = cx24120_writereg(state, 0xDC, 0x00); ++ release_firmware(fw); ++ if (reset_result) ++ return reset_result; ++ ++ //================== Start tuner ++ cx24120_message_fill(&cmd, CMD_START_TUNER, &cx24120_msg_tuner_init[0], 3, 0); // 0x1B ++ if(cx24120_message_send(state, &cmd)) { ++ err("Error tuner start! :(\n"); ++ return -EREMOTEIO; ++ } ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ ++ cmd.id = CMD_VCO_SET; // 0x10 ++ cmd.len = 12; ++ ++ // ###################### ++ // Calc VCO ++ xtal_khz = 10111; ++ xxyyzz = 0x400000000ULL; // 17179869184 ++ vco = xtal_khz * 10 * 4; // 404440 ++ inv_vco = xxyyzz / vco; // 42478 = 0x00A5EE ++ res = xxyyzz % vco; // 66864 = 0x010530 ++ ++ if( inv_vco > xtal_khz * 10 * 2) ++inv_vco; ++ ++ fH = (inv_vco >> 8) & 0xFF; ++ fL = (inv_vco) & 0xFF; ++ dbginfo("vco= %d, inv_vco= %lld, res= %lld, fL= 0x%x, fH= 0x%x\n", vco, inv_vco, res, fL, fH); ++ // ###################### ++ ++ cmd.arg[0] = 0x06; ++ cmd.arg[1] = 0x2B; ++ cmd.arg[2] = 0xD8; ++ cmd.arg[3] = fH; // 0xA5 ++ cmd.arg[4] = fL; // 0xEE ++ cmd.arg[5] = 0x03; ++ cmd.arg[6] = 0x9D; ++ cmd.arg[7] = 0xFC; ++ cmd.arg[8] = 0x06; ++ cmd.arg[9] = 0x03; ++ cmd.arg[10] = 0x27; ++ cmd.arg[11] = 0x7F; ++ ++ if(cx24120_message_send(state, &cmd)) { ++ err("Error set VCO! :(\n"); ++ return -EREMOTEIO; ++ } ++ memset(&cmd, 0, sizeof(struct cx24120_cmd)); ++ // set bandwidth ++ cmd.id = CMD_BANDWIDTH; // 0x15 ++ cmd.len = 12; ++ cmd.arg[0] = 0x00; ++ cmd.arg[1] = 0x00; ++ cmd.arg[2] = 0x00; ++ cmd.arg[3] = 0x00; ++ cmd.arg[4] = 0x05; ++ cmd.arg[5] = 0x02; ++ cmd.arg[6] = 0x02; ++ cmd.arg[7] = 0x00; ++ cmd.arg[8] = 0x05; ++ cmd.arg[9] = 0x02; ++ cmd.arg[10] = 0x02; ++ cmd.arg[11] = 0x00; ++ ++ if ( cx24120_message_send(state, &cmd) ) { ++ err("Error set bandwidth! :(\n"); ++ return -EREMOTEIO; ++ } ++ ret = cx24120_readreg(state, 0xBA); ++ if ( ret > 3) { ++ dbginfo("Reset-readreg 0xBA: %x\n", ret); ++ err("Error intitilizing tuner! :(\n"); ++ return -EREMOTEIO; ++ } ++ dbginfo("Tuner initialized correctly.\n"); ++ ++ ret = cx24120_writereg(state, 0xEB, 0x0A); ++ if (cx24120_msg_mpeg_output_global_config(state, 0) || ++ cx24120_msg_mpeg_output_config(state, 0, &initial_mpeg_config) || ++ cx24120_msg_mpeg_output_config(state, 1, &initial_mpeg_config) || ++ cx24120_msg_mpeg_output_config(state, 2, &initial_mpeg_config) ) ++ { ++ err("Error initilizing mpeg output. :(\n"); ++ return -EREMOTEIO; ++ } else { ++ cmd.id = 0x3C; // 60 ++ cmd.len = 0x03; ++ cmd.arg[0] = 0x00; ++ cmd.arg[1] = 0x10; ++ cmd.arg[2] = 0x10; ++ if(cx24120_message_send(state, &cmd)) { ++ err("Error sending final init message. :(\n"); ++ return -EREMOTEIO; ++ } ++ } ++ state->cold_init=1; ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_init); // end cx24120_reset ++//=================================================================== ++static int cx24120_tune(struct dvb_frontend *fe, bool re_tune, ++ unsigned int mode_flags, unsigned int *delay, fe_status_t *p_status) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct cx24120_state *state = fe->demodulator_priv; ++ int delay_cnt, sd_idx = 0; ++ fe_status_t status; ++ ++ if (re_tune) { ++ ++// dbginfo("Compare symrate with table: symrate= %d, in table= %d\n", ++// p->u.qpsk.symbol_rate, symrates_pairs[sd_idx].symrate); ++ ++ while ( p->symbol_rate > symrates_pairs[sd_idx].symrate ) { ++ ++sd_idx; ++ } ++ dbginfo("Found symrate delay = %d\n", symrates_pairs[sd_idx].delay); ++ state->dvb_s2_mode &= 0xFE; // clear bit -> try not DVB-S2 ++ dbginfo("trying DVB-S =================\n"); ++ cx24120_set_frontend(fe); ++ ++ delay_cnt = symrates_pairs[sd_idx].delay; ++ dbginfo("Wait for LOCK for DVB-S =================\n"); ++ while (delay_cnt >= 0) { ++ cx24120_read_status(fe, &status); ++ if (status & FE_HAS_LOCK) { ++ dbginfo("DVB-S LOCKED================\n"); ++ break; ++ } ++ msleep(100); ++ delay_cnt -=100; ++ } ++ dbginfo("Waiting finished - NO lock for DVB-S =================\n"); ++ ++ cx24120_read_status(fe, &status); ++ if ( !(status & FE_HAS_LOCK) ) { // if no lock on S ++ dbginfo("trying DVB-S2 ++++++++++++++++++++++++++\n"); ++ state->dvb_s2_mode |= 0x01; // may be it locked on S2 ? ++ p->fec_inner = FEC_AUTO; ++ cx24120_set_frontend(fe); ++ delay_cnt = symrates_pairs[sd_idx].delay; ++ dbginfo("Wait for LOCK for DVB-S2 ++++++++++++++++\n"); ++ while (delay_cnt >= 0) { ++ cx24120_read_status(fe, &status); ++ if (status & FE_HAS_LOCK) { ++ dbginfo("DVB-S2 LOCKED++++++++++++++++\n"); ++ break; ++ } ++ msleep(100); ++ delay_cnt -=100; ++ } ++ dbginfo("Waiting finished - NO lock for DVB-S2 ++++++++++++++++\n"); ++ } ++ } ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_tune); // end of cx24120_tune ++//=================================================================== ++static int cx24120_get_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_HW; ++} ++EXPORT_SYMBOL(cx24120_get_algo); ++//=================================================================== ++static int cx24120_sleep(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_sleep); ++//=================================================================== ++/*static int cx24120_wakeup(struct dvb_frontend *fe) ++ * { ++ * return 0; ++ * } ++ * EXPORT_SYMBOL(cx24120_wakeup); ++ */ ++//=================================================================== ++static int cx24120_get_frontend(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_get_frontend); ++//=================================================================== ++static void cx24120_release(struct dvb_frontend *fe) ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ dbginfo("Clear state structure\n"); ++ kfree(state); ++} ++EXPORT_SYMBOL(cx24120_release); ++//=================================================================== ++static int cx24120_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) // UNCORRECTED_BLOCKS ++{ ++ struct cx24120_state *state = fe->demodulator_priv; ++ ++ *ucblocks = (cx24120_readreg(state, CX24120_REG_UCB_H) << 8) | ++ cx24120_readreg(state, CX24120_REG_UCB_L); ++ dbginfo("Blocks = %d\n", *ucblocks); ++ return 0; ++} ++EXPORT_SYMBOL(cx24120_read_ucblocks); ++// ######################################################################################## ++static struct dvb_frontend_ops cx24120_ops = { ++ ++ .delsys = { SYS_DVBS2 }, ++ .info = { ++ .name = "Conexant CX24120/CX24118", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1011, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 5000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = // 0x500006ff ++ FE_CAN_INVERSION_AUTO | //0x00 000 001 ++ FE_CAN_FEC_1_2 | //0x00 000 002 ++ FE_CAN_FEC_2_3 | //0x00 000 004 ++ FE_CAN_FEC_3_4 | //0x00 000 008 ++ FE_CAN_FEC_4_5 | //0x00 000 010 ++ FE_CAN_FEC_5_6 | //0x00 000 020 ++ FE_CAN_FEC_6_7 | //0x00 000 040 ++ FE_CAN_FEC_7_8 | //0x00 000 080 ++ FE_CAN_FEC_AUTO | //0x00 000 200 ++ FE_CAN_QPSK | //0x00 000 400 ++//??? FE_HAS_EXTENDED_CAPS | //0x00 800 000 /* We need more bitspace for newer APIs, indicate this. */ ++ FE_CAN_2G_MODULATION | //0x10 000 000 /* frontend supports "2nd generation modulation" (DVB-S2) */ ++ FE_CAN_RECOVER //0x40 000 000 /* frontend can recover from a cable unplug automatically */ ++ }, //sum=50 000 6FF ++ .release = cx24120_release, ++ ++ .init = cx24120_init, ++ .sleep = cx24120_sleep, ++ ++ .tune = cx24120_tune, ++ .get_frontend_algo = cx24120_get_algo, ++ .set_frontend = cx24120_set_frontend, ++ ++ .get_frontend = cx24120_get_frontend, ++ .read_status = cx24120_read_status, ++ .read_ber = cx24120_read_ber, ++ .read_signal_strength = cx24120_read_signal_strength, ++ .read_snr = cx24120_read_snr, ++ .read_ucblocks = cx24120_read_ucblocks, ++ ++ .diseqc_send_master_cmd = cx24120_send_diseqc_msg, ++ ++ .diseqc_send_burst = cx24120_diseqc_send_burst, ++ .set_tone = cx24120_set_tone, ++ .set_voltage = cx24120_set_voltage, ++}; ++//=================================================================== ++MODULE_PARM_DESC(cx24120_debug, "prints some verbose debugging information (default:0)"); ++MODULE_AUTHOR("Sergey Tyurin"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/cx24120_const.h linux-openelec/drivers/media/dvb-frontends/cx24120_const.h +--- linux-3.14.36/drivers/media/dvb-frontends/cx24120_const.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/cx24120_const.h 2015-07-24 18:03:30.360842002 -0500 +@@ -0,0 +1,259 @@ ++/* ++ * Conexant CX24120/CX24118 - DVB-S/S2 demod/tuner driver ++ * DVBS/S2 Satellite demod/tuner driver static definitins ++ * ++ * Copyright (C) 2009 Sergey Tyurin ++ * Updated 2012 by Jannis Achstetter ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#define CX24120_FIRMWARE "dvb-fe-cx24120-1.20.58.2.fw" ++ ++// ############################## ++// ### cx24120 i2c registers ### ++#define CX24120_REG_CMD_START (0x00) // write cmd_id, and then start write args to next register: ++#define CX24120_REG_CMD_ARGS (0x01) // write command arguments, max 4 at once, then next 4, etc. ++#define CX24120_REG_CMD_END (0x1F) // write 0x01 for end, and read it for command result ++ ++#define CX24120_REG_FECMODE (0x39) // FEC status ++#define CX24120_REG_STATUS (0x3A) // Tuner status - signal, carrier, sync, lock ... ++#define CX24120_REG_QUALITY_H (0x40) // SNR high byte ++#define CX24120_REG_QUALITY_L (0x41) // SNR low byte ++ ++#define CX24120_REG_BER_HH (0x47) // BER high byte of high word ++#define CX24120_REG_BER_HL (0x48) // BER low byte of high word ++#define CX24120_REG_BER_LH (0x49) // BER high byte of low word ++#define CX24120_REG_BER_LL (0x4A) // BER low byte of low word ++ ++#define CX24120_REG_SIGSTR_H (0x3A) // Signal strength high byte & ??? status register ??? ++#define CX24120_REG_SIGSTR_L (0x3B) // Signal strength low byte ++ ++#define CX24120_REG_UCB_H (0x50) // UCB high byte ++#define CX24120_REG_UCB_L (0x51) // UCB low byte ++ ++#define CX24120_REG_REVISION (0xFF) // Chip revision (ro). Must be 0x7 or 0x5 ++ ++// ############################## ++/* Command messages */ ++enum command_message_id { ++ CMD_VCO_SET = 0x10, // cmdlen = 12; ++ CMD_TUNEREQUEST = 0x11, // cmd.len = 15; ++ ++ CMD_MPEG_ONOFF = 0x13, // cmd.len = 4; ++ CMD_MPEG_INIT = 0x14, // cmd.len = 7; ++ CMD_BANDWIDTH = 0x15, // cmd.len = 12; ++ CMD_CLOCK_READ = 0x16, // read clock from registers 0x01-0x06 ++ CMD_CLOCK_SET = 0x17, // cmd.len = 10; ++ ++ CMD_DISEQC_MSG1 = 0x20, // cmd.len = 11; ++ CMD_DISEQC_MSG2 = 0x21, // cmd.len = d->msg_len + 6; ++ CMD_SETVOLTAGE = 0x22, // cmd.len = 2; ++ CMD_SETTONE = 0x23, // cmd.len = 4; ++ CMD_DISEQC_BURST = 0x24, // cmd.len not used !!! ++ ++ CMD_READ_SNR = 0x1A, // Read signal strength ++ CMD_START_TUNER = 0x1B, // ??? ++ ++ CMD_TUNER_INIT = 0x3C, // cmd.len = 0x03; ++}; ++// ############################## ++/* signal status */ ++#define CX24120_HAS_SIGNAL (0x01) ++#define CX24120_HAS_CARRIER (0x02) ++#define CX24120_HAS_VITERBI (0x04) ++#define CX24120_HAS_LOCK (0x08) ++#define CX24120_HAS_UNK1 (0x10) ++#define CX24120_HAS_UNK2 (0x20) ++#define CX24120_STATUS_MASK (0x0f) ++#define CX24120_SIGNAL_MASK (0xc0) ++ ++static u8 cx24120_msg_tuner_init[] = { 0,0,0,0,0,0 }; ++static u8 cx24120_msg_read_sigstr[] = {0,0}; ++ ++static struct cx24120_skystar2_mpeg_config { ++ u8 x1; ++ u8 x2; ++ u8 x3; ++} initial_mpeg_config = { ++ 0xA1, // 10100001 ++ 0x76, // 01110110 ++ 0x07, // 00000111 ++}; ++ ++static struct cx24120_symrate_delay { ++ u32 symrate; ++ u32 delay; ++} symrates_pairs[] = { ++ { 3000000, 15000 }, ++ { 6000000, 10000 }, ++ { 8000000, 5000 }, ++ { 10000000, 2000 }, ++ {0x0FFFFFFFF, 400 }, ++}; ++ ++static struct cx24120_clock_ratios_table { ++ u32 ratio_id; ++ u32 mode_xPSK; ++ u32 fec_mode; ++ u32 m_rat; ++ u32 n_rat; ++ u32 rate; ++} clock_ratios_table[] = { ++{ 21 , 0 , 1 , 770068 , 763515 , 258 }, ++{ 21 , 0 , 100 , 97409 , 80370 , 310 }, ++{ 21 , 0 , 2 , 137293 , 101802 , 345 }, ++{ 21 , 0 , 3 , 4633447 , 3054060 , 388 }, ++{ 21 , 0 , 4 , 2472041 , 1527030 , 414 }, ++{ 21 , 0 , 5 , 85904 , 50901 , 432 }, ++{ 21 , 0 , 8 , 2751229 , 1527030 , 461 }, ++{ 21 , 0 , 101 , 1392872 , 763515 , 467 }, ++{ 21 , 99 , 100 , 1850771 , 1019430 , 464 }, ++{ 21 , 99 , 2 , 137293 , 67962 , 517 }, ++{ 21 , 99 , 3 , 4633447 , 2038860 , 581 }, // was 4 - ERRORR! FEC_4_5 not in DVB-S2 ++{ 21 , 99 , 5 , 85904 , 33981 , 647 }, ++{ 21 , 99 , 8 , 2751229 , 1019430 , 690 }, ++{ 21 , 99 , 101 , 1392872 , 509715 , 699 }, ++{ 29 , 0 , 1 , 770068 , 782127 , 252 }, ++{ 29 , 0 , 100 , 1850771 , 1564254 , 302 }, ++{ 29 , 0 , 2 , 686465 , 521418 , 337 }, ++{ 29 , 0 , 3 , 4633447 , 3128508 , 379 }, ++{ 29 , 0 , 4 , 2472041 , 1564254 , 404 }, ++{ 29 , 0 , 5 , 429520 , 260709 , 421 }, ++{ 29 , 0 , 8 , 2751229 , 1564254 , 450 }, ++{ 29 , 0 , 101 , 1392872 , 782127 , 455 }, ++{ 29 , 99 , 100 , 1850771 , 1043118 , 454 }, ++{ 29 , 99 , 2 , 686465 , 347706 , 505 }, ++{ 29 , 99 , 3 , 4633447 , 2086236 , 568 }, // was 4 - ERRORR! FEC_4_5 not in DVB-S2 ++{ 29 , 99 , 5 , 429520 , 173853 , 632 }, ++{ 29 , 99 , 8 , 2751229 , 1043118 , 675 }, ++{ 29 , 99 , 101 , 1392872 , 521559 , 683 }, ++{ 17 , 0 , 1 , 766052 , 763515 , 256 }, ++{ 17 , 0 , 100 , 96901 , 80370 , 308 }, ++{ 17 , 0 , 2 , 136577 , 101802 , 343 }, ++{ 17 , 0 , 3 , 4609283 , 3054060 , 386 }, ++{ 17 , 0 , 4 , 2459149 , 1527030 , 412 }, ++{ 17 , 0 , 5 , 85456 , 50901 , 429 }, ++{ 17 , 0 , 8 , 2736881 , 1527030 , 458 }, ++{ 17 , 0 , 101 , 1385608 , 763515 , 464 }, ++{ 17 , 99 , 100 , 1841119 , 1019430 , 462 }, ++{ 17 , 99 , 2 , 136577 , 67962 , 514 }, ++{ 17 , 99 , 3 , 4609283 , 2038860 , 578 }, // was 4 - ERRORR! FEC_4_5 not in DVB-S2 ++{ 17 , 99 , 5 , 85456 , 33981 , 643 }, ++{ 17 , 99 , 8 , 2736881 , 1019430 , 687 }, ++{ 17 , 99 , 101 , 1385608 , 509715 , 695 }, ++{ 25 , 0 , 1 , 766052 , 782127 , 250 }, ++{ 25 , 0 , 100 , 1841119 , 1564254 , 301 }, ++{ 25 , 0 , 2 , 682885 , 521418 , 335 }, ++{ 25 , 0 , 3 , 4609283 , 3128508 , 377 }, ++{ 25 , 0 , 4 , 2459149 , 1564254 , 402 }, ++{ 25 , 0 , 5 , 427280 , 260709 , 419 }, ++{ 25 , 0 , 8 , 2736881 , 1564254 , 447 }, ++{ 25 , 0 , 101 , 1385608 , 782127 , 453 }, ++{ 25 , 99 , 100 , 1841119 , 1043118 , 451 }, ++{ 25 , 99 , 2 , 682885 , 347706 , 502 }, ++{ 25 , 99 , 3 , 4609283 , 2086236 , 565 }, // was 4 - ERRORR! FEC_4_5 not in DVB-S2 ++{ 25 , 99 , 5 , 427280 , 173853 , 629 }, ++{ 25 , 99 , 8 , 2736881 , 1043118 , 671 }, ++{ 25 , 99 , 101 , 1385608 , 521559 , 680 }, ++{ 5 , 0 , 1 , 273088 , 254505 , 274 }, ++{ 5 , 0 , 100 , 17272 , 13395 , 330 }, ++{ 5 , 0 , 2 , 24344 , 16967 , 367 }, ++{ 5 , 0 , 3 , 410788 , 254505 , 413 }, ++{ 5 , 0 , 4 , 438328 , 254505 , 440 }, ++{ 5 , 0 , 5 , 30464 , 16967 , 459 }, ++{ 5 , 0 , 8 , 487832 , 254505 , 490 }, ++{ 5 , 0 , 101 , 493952 , 254505 , 496 }, ++{ 5 , 99 , 100 , 328168 , 169905 , 494 }, ++{ 5 , 99 , 2 , 24344 , 11327 , 550 }, // work for 0x0d - 11278V - DVB-S2 - 8PSK MPEG-4/HD ++{ 5 , 99 , 3 , 410788 , 169905 , 618 }, // 0x0e S2 8psk // was 4 - ERRORR! FEC_4_5 not in DVB-S2 ++{ 5 , 99 , 5 , 30464 , 11327 , 688 }, ++{ 5 , 99 , 8 , 487832 , 169905 , 735 }, ++{ 5 , 99 , 101 , 493952 , 169905 , 744 }, ++{ 13 , 0 , 1 , 273088 , 260709 , 268 }, ++{ 13 , 0 , 100 , 328168 , 260709 , 322 }, ++{ 13 , 0 , 2 , 121720 , 86903 , 358 }, ++{ 13 , 0 , 3 , 410788 , 260709 , 403 }, ++{ 13 , 0 , 4 , 438328 , 260709 , 430 }, ++{ 13 , 0 , 5 , 152320 , 86903 , 448 }, ++{ 13 , 0 , 8 , 487832 , 260709 , 479 }, ++{ 13 , 0 , 101 , 493952 , 260709 , 485 }, ++{ 13 , 99 , 100 , 328168 , 173853 , 483 }, ++{ 13 , 99 , 2 , 121720 , 57951 , 537 }, // work for 0x8d - dvb-s2 8psk ++{ 13 , 99 , 3 , 410788 , 173853 , 604 }, // was 4 - ERRORR! FEC_4_5 not in DVB-S2 ++{ 13 , 99 , 5 , 152320 , 57951 , 672 }, ++{ 13 , 99 , 8 , 487832 , 173853 , 718 }, ++{ 13 , 99 , 101 , 493952 , 173853 , 727 }, ++{ 1 , 0 , 1 , 815248 , 763515 , 273 }, ++{ 1 , 0 , 100 , 51562 , 40185 , 328 }, ++{ 1 , 0 , 2 , 72674 , 50901 , 365 }, ++{ 1 , 0 , 3 , 1226323 , 763515 , 411 }, ++{ 1 , 0 , 4 , 1308538 , 763515 , 438 }, ++{ 1 , 0 , 5 , 90944 , 50901 , 457 }, ++{ 1 , 0 , 8 , 1456322 , 763515 , 488 }, ++{ 1 , 0 , 101 , 1474592 , 763515 , 494 }, ++{ 1 , 99 , 100 , 979678 , 509715 , 492 }, ++{ 1 , 99 , 2 , 72674 , 33981 , 547 }, ++{ 1 , 99 , 3 , 1226323 , 509715 , 615 }, // was 4 - ERRORR!? FEC_4_5 not in DVB-S2 ++{ 1 , 99 , 5 , 90944 , 33981 , 685 }, ++{ 1 , 99 , 8 , 1456322 , 509715 , 731 }, ++{ 1 , 99 , 101 , 1474592 , 509715 , 740 }, ++{ 9 , 0 , 1 , 815248 , 782127 , 266 }, ++{ 9 , 0 , 100 , 979678 , 782127 , 320 }, ++{ 9 , 0 , 2 , 363370 , 260709 , 356 }, ++{ 9 , 0 , 3 , 1226323 , 782127 , 401 }, ++{ 9 , 0 , 4 , 1308538 , 782127 , 428 }, ++{ 9 , 0 , 5 , 454720 , 260709 , 446 }, ++{ 9 , 0 , 8 , 1456322 , 782127 , 476 }, ++{ 9 , 0 , 101 , 1474592 , 782127 , 482 }, ++{ 9 , 99 , 100 , 979678 , 521559 , 480 }, ++{ 9 , 99 , 2 , 363370 , 173853 , 535 }, ++{ 9 , 99 , 3 , 1226323 , 521559 , 601 }, // was 4 - ERRORR! FEC_4_5 not in DVB-S2 ++{ 9 , 99 , 5 , 454720 , 173853 , 669 }, ++{ 9 , 99 , 8 , 1456322 , 521559 , 714 }, ++{ 9 , 99 , 101 , 1474592 , 521559 , 723 }, ++{ 18 , 0 , 1 , 535 , 588 , 233 }, ++{ 18 , 0 , 2 , 1070 , 882 , 311 }, ++{ 18 , 0 , 6 , 3210 , 2058 , 399 }, ++{ 16 , 0 , 1 , 763 , 816 , 239 }, ++{ 16 , 0 , 2 , 1526 , 1224 , 319 }, ++{ 16 , 0 , 3 , 2289 , 1632 , 359 }, ++{ 16 , 0 , 5 , 3815 , 2448 , 399 }, ++{ 16 , 0 , 7 , 5341 , 3264 , 419 }, ++{ 22 , 0 , 1 , 535 , 588 , 233 }, ++{ 22 , 0 , 2 , 1070 , 882 , 311 }, ++{ 22 , 0 , 6 , 3210 , 2058 , 399 }, ++{ 20 , 0 , 1 , 143429 , 152592 , 241 }, ++{ 20 , 0 , 2 , 286858 , 228888 , 321 }, ++{ 20 , 0 , 3 , 430287 , 305184 , 361 }, ++{ 20 , 0 , 5 , 717145 , 457776 , 401 }, ++{ 20 , 0 , 7 , 1004003 , 610368 , 421 }, ++{ 2 , 0 , 1 , 584 , 588 , 254 }, ++{ 2 , 0 , 2 , 1169 , 882 , 339 }, ++{ 2 , 0 , 6 , 3507 , 2058 , 436 }, ++{ 0 , 0 , 1 , 812 , 816 , 255 }, ++{ 0 , 0 , 2 , 1624 , 1224 , 340 }, ++{ 0 , 0 , 3 , 2436 , 1632 , 382 }, ++{ 0 , 0 , 5 , 4060 , 2448 , 425 }, ++{ 0 , 0 , 7 , 5684 , 3264 , 446 }, ++{ 6 , 0 , 1 , 584 , 588 , 254 }, ++{ 6 , 0 , 2 , 1168 , 882 , 339 }, ++{ 6 , 0 , 6 , 3504 , 2058 , 436 }, ++{ 4 , 0 , 1 , 152592 , 152592 , 256 }, ++{ 4 , 0 , 2 , 305184 , 228888 , 341 }, ++{ 4 , 0 , 3 , 457776 , 305184 , 384 }, ++{ 4 , 0 , 5 , 762960 , 457776 , 427 }, ++{ 4 , 0 , 7 , 1068144 , 610368 , 448 }, ++}; +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/cx24120.h linux-openelec/drivers/media/dvb-frontends/cx24120.h +--- linux-3.14.36/drivers/media/dvb-frontends/cx24120.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/cx24120.h 2015-07-24 18:03:30.360842002 -0500 +@@ -0,0 +1,59 @@ ++/* ++ * Conexant CX24120/CX24118 - DVB-S/S2 demod/tuner driver ++ * ++ * Copyright (C) 2008 Patrick Boettcher ++ * Copyright (C) 2009 Sergey Tyurin ++ * Updated 2012 by Jannis Achstetter ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef CX24120_H ++#define CX24120_H ++ ++#include ++ ++struct firmware; ++struct dvb_frontend; ++struct i2c_adapter; ++ ++struct cx24120_config ++{ ++ u8 i2c_addr; ++ int (*request_firmware)(struct dvb_frontend *fe, const struct firmware **fw, char *name); ++ void (*stream_control)(struct dvb_frontend *fe, u8 onoff); ++}; ++ ++#if defined(CONFIG_DVB_CX24120) || \ ++ (defined(CONFIG_DVB_CX24120_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, ++ struct i2c_adapter *i2c); ++extern int cx24120_reset(struct dvb_frontend *fe); ++#else ++static inline ++struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static inline int cx24120_reset(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++#endif ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/cxd2820r_c.c linux-openelec/drivers/media/dvb-frontends/cxd2820r_c.c +--- linux-3.14.36/drivers/media/dvb-frontends/cxd2820r_c.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/cxd2820r_c.c 2015-07-24 18:03:30.140842002 -0500 +@@ -45,6 +45,7 @@ + { 0x1008b, 0x07, 0xff }, + { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 }, + { 0x10070, priv->cfg.ts_mode, 0xff }, ++ { 0x10071, !priv->cfg.ts_clock_inv << 4, 0x10 }, + }; + + dev_dbg(&priv->i2c->dev, "%s: frequency=%d symbol_rate=%d\n", __func__, +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/cxd2820r.h linux-openelec/drivers/media/dvb-frontends/cxd2820r.h +--- linux-3.14.36/drivers/media/dvb-frontends/cxd2820r.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/cxd2820r.h 2015-07-24 18:03:30.140842002 -0500 +@@ -52,6 +52,12 @@ + */ + u8 ts_mode; + ++ /* TS clock inverted. ++ * Default: 0 ++ * Values: 0, 1 ++ */ ++ bool ts_clock_inv; ++ + /* IF AGC polarity. + * Default: 0 + * Values: 0, 1 +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/cxd2820r_t2.c linux-openelec/drivers/media/dvb-frontends/cxd2820r_t2.c +--- linux-3.14.36/drivers/media/dvb-frontends/cxd2820r_t2.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/cxd2820r_t2.c 2015-07-24 18:03:30.140842002 -0500 +@@ -47,6 +47,7 @@ + { 0x02083, 0x0a, 0xff }, + { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 }, + { 0x02070, priv->cfg.ts_mode, 0xff }, ++ { 0x02071, !priv->cfg.ts_clock_inv << 6, 0x40 }, + { 0x020b5, priv->cfg.spec_inv << 4, 0x10 }, + { 0x02567, 0x07, 0x0f }, + { 0x02569, 0x03, 0x03 }, +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/cxd2820r_t.c linux-openelec/drivers/media/dvb-frontends/cxd2820r_t.c +--- linux-3.14.36/drivers/media/dvb-frontends/cxd2820r_t.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/cxd2820r_t.c 2015-07-24 18:03:30.140842002 -0500 +@@ -46,6 +46,7 @@ + { 0x00088, 0x01, 0xff }, + + { 0x00070, priv->cfg.ts_mode, 0xff }, ++ { 0x00071, !priv->cfg.ts_clock_inv << 4, 0x10 }, + { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 }, + { 0x000a5, 0x00, 0x01 }, + { 0x00082, 0x20, 0x60 }, +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88dc2800.c linux-openelec/drivers/media/dvb-frontends/dvbsky_m88dc2800.c +--- linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88dc2800.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/dvbsky_m88dc2800.c 2015-07-24 18:03:30.116842002 -0500 +@@ -0,0 +1,2124 @@ ++/* ++ M88DC2800/M88TC2800 - DVB-C demodulator and tuner from Montage ++ ++ Copyright (C) 2012 Max nibble ++ Copyright (C) 2011 Montage Technology / www.montage-tech.com ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "dvbsky_m88dc2800.h" ++ ++struct dvbsky_m88dc2800_state { ++ struct i2c_adapter *i2c; ++ const struct dvbsky_m88dc2800_config *config; ++ struct dvb_frontend frontend; ++ u32 freq; ++ u32 ber; ++ u32 sym; ++ u16 qam; ++ u8 inverted; ++ u32 xtal; ++ /* tuner state */ ++ u8 tuner_init_OK; /* Tuner initialize status */ ++ u8 tuner_dev_addr; /* Tuner device address */ ++ u32 tuner_freq; /* RF frequency to be set, unit: KHz */ ++ u16 tuner_qam; /* Reserved */ ++ u16 tuner_mode; ++ u8 tuner_bandwidth; /* Bandwidth of the channel, unit: MHz, 6/7/8 */ ++ u8 tuner_loopthrough; /* Tuner loop through switch, 0/1 */ ++ u32 tuner_crystal; /* Tuner crystal frequency, unit: KHz */ ++ u32 tuner_dac; /* Tuner DAC frequency, unit: KHz */ ++ u16 tuner_mtt; /* Tuner chip version, D1: 0x0d, E0: 0x0e, E1: 0x8e */ ++ u16 tuner_custom_cfg; ++ u32 tuner_version; /* Tuner driver version number */ ++ u32 tuner_time; ++}; ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_INFO "m88dc2800: " args); \ ++ } while (0) ++ ++ ++static int dvbsky_m88dc2800_i2c_write(struct dvbsky_m88dc2800_state *state, u8 addr, ++ u8 * p_data, u8 len) ++{ ++ struct i2c_msg msg = { .flags = 0 }; ++ ++ msg.addr = addr; ++ msg.buf = p_data; ++ msg.len = len; ++ ++ return i2c_transfer(state->i2c, &msg, 1); ++} ++ ++static int dvbsky_m88dc2800_i2c_read(struct dvbsky_m88dc2800_state *state, u8 addr, ++ u8 * p_data, u8 len) ++{ ++ struct i2c_msg msg = { .flags = I2C_M_RD }; ++ ++ msg.addr = addr; ++ msg.buf = p_data; ++ msg.len = len; ++ ++ return i2c_transfer(state->i2c, &msg, 1); ++} ++ ++/*demod register operations.*/ ++static int WriteReg(struct dvbsky_m88dc2800_state *state, u8 reg, u8 data) ++{ ++ u8 buf[] = { reg, data }; ++ u8 addr = state->config->demod_address; ++ int err; ++ ++ dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); ++ ++ err = dvbsky_m88dc2800_i2c_write(state, addr, buf, 2); ++ ++ if (err != 1) { ++ printk(KERN_ERR ++ "%s: writereg error(err == %i, reg == 0x%02x," ++ " value == 0x%02x)\n", __func__, err, reg, data); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int ReadReg(struct dvbsky_m88dc2800_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ u8 addr = state->config->demod_address; ++ ++ ret = dvbsky_m88dc2800_i2c_write(state, addr, b0, 1); ++ ++ if (ret != 1) { ++ printk(KERN_ERR "%s: reg=0x%x (error=%d)\n", ++ __func__, reg, ret); ++ return -EIO; ++ } ++ ++ ret = dvbsky_m88dc2800_i2c_read(state, addr, b1, 1); ++ ++ dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); ++ return b1[0]; ++} ++ ++static int _mt_fe_tn_set_reg(struct dvbsky_m88dc2800_state *state, u8 reg, ++ u8 data) ++{ ++ int ret; ++ u8 buf[2]; ++ u8 addr = state->tuner_dev_addr; ++ ++ buf[1] = ReadReg(state, 0x86); ++ buf[1] |= 0x80; ++ ret = WriteReg(state, 0x86, buf[1]); ++ ++ buf[0] = reg; ++ buf[1] = data; ++ ++ ret = dvbsky_m88dc2800_i2c_write(state, addr, buf, 2); ++ if (ret != 1) ++ return -EIO; ++ return 0; ++} ++ ++static int _mt_fe_tn_get_reg(struct dvbsky_m88dc2800_state *state, u8 reg, ++ u8 * p_data) ++{ ++ int ret; ++ u8 buf[2]; ++ u8 addr = state->tuner_dev_addr; ++ ++ buf[1] = ReadReg(state, 0x86); ++ buf[1] |= 0x80; ++ ret = WriteReg(state, 0x86, buf[1]); ++ ++ buf[0] = reg; ++ ret = dvbsky_m88dc2800_i2c_write(state, addr, buf, 1); ++ ++ msleep(1); ++ ++ buf[1] = ReadReg(state, 0x86); ++ buf[1] |= 0x80; ++ ret = WriteReg(state, 0x86, buf[1]); ++ ++ return dvbsky_m88dc2800_i2c_read(state, addr, p_data, 1); ++} ++ ++/* Tuner operation functions.*/ ++static int _mt_fe_tn_set_RF_front_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ u32 freq_KHz = state->tuner_freq; ++ u8 a, b, c; ++ if (state->tuner_mtt == 0xD1) { /* D1 */ ++ if (freq_KHz <= 123000) { ++ if (freq_KHz <= 56000) { ++ a = 0x00; b = 0x00; c = 0x00; ++ } else if (freq_KHz <= 64000) { ++ a = 0x10; b = 0x01; c = 0x08; ++ } else if (freq_KHz <= 72000) { ++ a = 0x20; b = 0x02; c = 0x10; ++ } else if (freq_KHz <= 80000) { ++ a = 0x30; b = 0x03; c = 0x18; ++ } else if (freq_KHz <= 88000) { ++ a = 0x40; b = 0x04; c = 0x20; ++ } else if (freq_KHz <= 96000) { ++ a = 0x50; b = 0x05; c = 0x28; ++ } else if (freq_KHz <= 104000) { ++ a = 0x60; b = 0x06; c = 0x30; ++ } else { ++ a = 0x70; b = 0x07; c = 0x38; ++ } ++ _mt_fe_tn_set_reg(state, 0x58, 0x9b); ++ _mt_fe_tn_set_reg(state, 0x59, a); ++ _mt_fe_tn_set_reg(state, 0x5d, b); ++ _mt_fe_tn_set_reg(state, 0x5e, c); ++ _mt_fe_tn_set_reg(state, 0x5a, 0x75); ++ _mt_fe_tn_set_reg(state, 0x73, 0x0c); ++ } else { /* if (freq_KHz > 112000) */ ++ _mt_fe_tn_set_reg(state, 0x58, 0x7b); ++ if (freq_KHz <= 304000) { ++ if (freq_KHz <= 136000) { ++ _mt_fe_tn_set_reg(state, 0x5e, 0x40); ++ } else if (freq_KHz <= 160000) { ++ _mt_fe_tn_set_reg(state, 0x5e, 0x48); ++ } else if (freq_KHz <= 184000) { ++ _mt_fe_tn_set_reg(state, 0x5e, 0x50); ++ } else if (freq_KHz <= 208000) { ++ _mt_fe_tn_set_reg(state, 0x5e, 0x58); ++ } else if (freq_KHz <= 232000) { ++ _mt_fe_tn_set_reg(state, 0x5e, 0x60); ++ } else if (freq_KHz <= 256000) { ++ _mt_fe_tn_set_reg(state, 0x5e, 0x68); ++ } else if (freq_KHz <= 280000) { ++ _mt_fe_tn_set_reg(state, 0x5e, 0x70); ++ } else { /* if (freq_KHz <= 304000) */ ++ _mt_fe_tn_set_reg(state, 0x5e, 0x78); ++ } ++ if (freq_KHz <= 171000) { ++ _mt_fe_tn_set_reg(state, 0x73, 0x08); ++ } else if (freq_KHz <= 211000) { ++ _mt_fe_tn_set_reg(state, 0x73, 0x0a); ++ } else { ++ _mt_fe_tn_set_reg(state, 0x73, 0x0e); ++ } ++ } else { /* if (freq_KHz > 304000) */ ++ _mt_fe_tn_set_reg(state, 0x5e, 0x88); ++ if (freq_KHz <= 400000) { ++ _mt_fe_tn_set_reg(state, 0x73, 0x0c); ++ } else if (freq_KHz <= 450000) { ++ _mt_fe_tn_set_reg(state, 0x73, 0x09); ++ } else if (freq_KHz <= 550000) { ++ _mt_fe_tn_set_reg(state, 0x73, 0x0e); ++ } else if (freq_KHz <= 650000) { ++ _mt_fe_tn_set_reg(state, 0x73, 0x0d); ++ } else { /*if (freq_KHz > 650000) */ ++ _mt_fe_tn_set_reg(state, 0x73, 0x0e); ++ } ++ } ++ } ++ if (freq_KHz > 800000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x24); ++ else if (freq_KHz > 700000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x34); ++ else if (freq_KHz > 500000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x44); ++ else if (freq_KHz > 300000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x43); ++ else if (freq_KHz > 220000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ else if (freq_KHz > 110000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x14); ++ else ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ if (freq_KHz > 600000) ++ _mt_fe_tn_set_reg(state, 0x6a, 0x53); ++ else if (freq_KHz > 500000) ++ _mt_fe_tn_set_reg(state, 0x6a, 0x57); ++ else ++ _mt_fe_tn_set_reg(state, 0x6a, 0x59); ++ if (freq_KHz < 200000) { ++ _mt_fe_tn_set_reg(state, 0x20, 0x5d); ++ } else if (freq_KHz < 500000) { ++ _mt_fe_tn_set_reg(state, 0x20, 0x7d); ++ } else { ++ _mt_fe_tn_set_reg(state, 0x20, 0xfd); ++ } /* end of 0xD1 */ ++ } else if (state->tuner_mtt == 0xE1) { /* E1 */ ++ if (freq_KHz <= 112000) { /* 123MHz */ ++ if (freq_KHz <= 56000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x01); ++ } else if (freq_KHz <= 64000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x09); ++ } else if (freq_KHz <= 72000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x11); ++ } else if (freq_KHz <= 80000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x19); ++ } else if (freq_KHz <= 88000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x21); ++ } else if (freq_KHz <= 96000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x29); ++ } else if (freq_KHz <= 104000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x31); ++ } else { /* if (freq_KHz <= 112000) */ ++ _mt_fe_tn_set_reg(state, 0x5c, 0x39); ++ } ++ _mt_fe_tn_set_reg(state, 0x5b, 0x30); ++ } else { /* if (freq_KHz > 112000) */ ++ if (freq_KHz <= 304000) { ++ if (freq_KHz <= 136000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x41); ++ } else if (freq_KHz <= 160000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x49); ++ } else if (freq_KHz <= 184000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x51); ++ } else if (freq_KHz <= 208000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x59); ++ } else if (freq_KHz <= 232000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x61); ++ } else if (freq_KHz <= 256000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x69); ++ } else if (freq_KHz <= 280000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x71); ++ } else { /* if (freq_KHz <= 304000) */ ++ _mt_fe_tn_set_reg(state, 0x5c, 0x79); ++ } ++ if (freq_KHz <= 150000) { ++ _mt_fe_tn_set_reg(state, 0x5b, 0x28); ++ } else if (freq_KHz <= 256000) { ++ _mt_fe_tn_set_reg(state, 0x5b, 0x29); ++ } else { ++ _mt_fe_tn_set_reg(state, 0x5b, 0x2a); ++ } ++ } else { /* if (freq_KHz > 304000) */ ++ if (freq_KHz <= 400000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x89); ++ } else if (freq_KHz <= 450000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x91); ++ } else if (freq_KHz <= 650000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0x98); ++ } else if (freq_KHz <= 850000) { ++ _mt_fe_tn_set_reg(state, 0x5c, 0xa0); ++ } else { ++ _mt_fe_tn_set_reg(state, 0x5c, 0xa8); ++ } ++ _mt_fe_tn_set_reg(state, 0x5b, 0x08); ++ } ++ } ++ } /* end of 0xE1 */ ++ return 0; ++} ++ ++static int _mt_fe_tn_cali_PLL_tc2800(struct dvbsky_m88dc2800_state *state, ++ u32 freq_KHz, ++ u32 cali_freq_thres_div2, ++ u32 cali_freq_thres_div3r, ++ u32 cali_freq_thres_div3) ++{ ++ s32 N, F, MUL; ++ u8 buf, tmp, tmp2; ++ s32 M; ++ const s32 crystal_KHz = state->tuner_crystal; ++ if (state->tuner_mtt == 0xD1) { ++ M = state->tuner_crystal / 4000; ++ if (freq_KHz > cali_freq_thres_div2) { ++ MUL = 4; ++ tmp = 2; ++ } else if (freq_KHz > 300000) { ++ MUL = 8; ++ tmp = 3; ++ } else if (freq_KHz > (cali_freq_thres_div2 / 2)) { ++ MUL = 8; ++ tmp = 4; ++ } else if (freq_KHz > (cali_freq_thres_div2 / 4)) { ++ MUL = 16; ++ tmp = 5; ++ } else if (freq_KHz > (cali_freq_thres_div2 / 8)) { ++ MUL = 32; ++ tmp = 6; ++ } else if (freq_KHz > (cali_freq_thres_div2 / 16)) { ++ MUL = 64; ++ tmp = 7; ++ } else { /* invalid */ ++ MUL = 0; ++ tmp = 0; ++ return 1; ++ } ++ } else if (state->tuner_mtt == 0xE1) { ++ M = state->tuner_crystal / 1000; ++ _mt_fe_tn_set_reg(state, 0x30, 0xff); ++ _mt_fe_tn_set_reg(state, 0x32, 0xe0); ++ _mt_fe_tn_set_reg(state, 0x33, 0x86); ++ _mt_fe_tn_set_reg(state, 0x37, 0x70); ++ _mt_fe_tn_set_reg(state, 0x38, 0x20); ++ _mt_fe_tn_set_reg(state, 0x39, 0x18); ++ _mt_fe_tn_set_reg(state, 0x89, 0x83); ++ if (freq_KHz > cali_freq_thres_div2) { ++ M = M / 4; ++ MUL = 4; ++ tmp = 2; ++ tmp2 = M + 16; /* 48 */ ++ } else if (freq_KHz > cali_freq_thres_div3r) { ++ M = M / 3; ++ MUL = 6; ++ tmp = 2; ++ tmp2 = M + 32; /* 32 */ ++ } else if (freq_KHz > cali_freq_thres_div3) { ++ M = M / 3; ++ MUL = 6; ++ tmp = 2; ++ tmp2 = M; /* 16 */ ++ } else if (freq_KHz > 304000) { ++ M = M / 4; ++ MUL = 8; ++ tmp = 3; ++ tmp2 = M + 16; /* 48 */ ++ } else if (freq_KHz > (cali_freq_thres_div2 / 2)) { ++ M = M / 4; ++ MUL = 8; ++ tmp = 4; ++ tmp2 = M + 16; /* 48 */ ++ } else if (freq_KHz > (cali_freq_thres_div3r / 2)) { ++ M = M / 3; ++ MUL = 12; ++ tmp = 4; ++ tmp2 = M + 32; /* 32 */ ++ } else if (freq_KHz > (cali_freq_thres_div3 / 2)) { ++ M = M / 3; ++ MUL = 12; ++ tmp = 4; ++ tmp2 = M; /* 16 */ ++ } else if (freq_KHz > (cali_freq_thres_div2 / 4)) { ++ M = M / 4; ++ MUL = 16; ++ tmp = 5; ++ tmp2 = M + 16; /* 48 */ ++ } else if (freq_KHz > (cali_freq_thres_div3r / 4)) { ++ M = M / 3; ++ MUL = 24; ++ tmp = 5; ++ tmp2 = M + 32; /* 32 */ ++ } else if (freq_KHz > (cali_freq_thres_div3 / 4)) { ++ M = M / 3; ++ MUL = 24; ++ tmp = 5; ++ tmp2 = M; /* 16 */ ++ } else if (freq_KHz > (cali_freq_thres_div2 / 8)) { ++ M = M / 4; ++ MUL = 32; ++ tmp = 6; ++ tmp2 = M + 16; /* 48 */ ++ } else if (freq_KHz > (cali_freq_thres_div3r / 8)) { ++ M = M / 3; ++ MUL = 48; ++ tmp = 6; ++ tmp2 = M + 32; /* 32 */ ++ } else if (freq_KHz > (cali_freq_thres_div3 / 8)) { ++ M = M / 3; ++ MUL = 48; ++ tmp = 6; ++ tmp2 = M; /* 16 */ ++ } else if (freq_KHz > (cali_freq_thres_div2 / 16)) { ++ M = M / 4; ++ MUL = 64; ++ tmp = 7; ++ tmp2 = M + 16; /* 48 */ ++ } else if (freq_KHz > (cali_freq_thres_div3r / 16)) { ++ M = M / 3; ++ MUL = 96; ++ tmp = 7; ++ tmp2 = M + 32; /* 32 */ ++ } else if (freq_KHz > (cali_freq_thres_div3 / 16)) { ++ M = M / 3; ++ MUL = 96; ++ tmp = 7; ++ tmp2 = M; /* 16 */ ++ } else { /* invalid */ ++ M = M / 4; ++ MUL = 0; ++ tmp = 0; ++ tmp2 = 48; ++ return 1; ++ } ++ if (freq_KHz == 291000) { ++ M = state->tuner_crystal / 1000 / 3; ++ MUL = 12; ++ tmp = 4; ++ tmp2 = M + 32; /* 32 */ ++ } ++ /* ++ if (freq_KHz == 578000) { ++ M = state->tuner_crystal / 1000 / 4; ++ MUL = 4; ++ tmp = 2; ++ tmp2 = M + 16; // 48 ++ } ++ */ ++ if (freq_KHz == 690000) { ++ M = state->tuner_crystal / 1000 / 3; ++ MUL = 4; ++ tmp = 2; ++ tmp2 = M + 16; /* 48 */ ++ } ++ _mt_fe_tn_get_reg(state, 0x33, &buf); ++ buf &= 0xc0; ++ buf += tmp2; ++ _mt_fe_tn_set_reg(state, 0x33, buf); ++ } else { ++ return 1; ++ } ++ _mt_fe_tn_get_reg(state, 0x39, &buf); ++ buf &= 0xf8; ++ buf += tmp; ++ _mt_fe_tn_set_reg(state, 0x39, buf); ++ N = (freq_KHz * MUL * M / crystal_KHz) / 2 * 2 - 256; ++ buf = (N >> 8) & 0xcf; ++ if (state->tuner_mtt == 0xE1) { ++ buf |= 0x30; ++ } ++ _mt_fe_tn_set_reg(state, 0x34, buf); ++ buf = N & 0xff; ++ _mt_fe_tn_set_reg(state, 0x35, buf); ++ F = ((freq_KHz * MUL * M / (crystal_KHz / 1000) / 2) - ++ (freq_KHz * MUL * M / crystal_KHz / 2 * 1000)) * 64 / 1000; ++ buf = F & 0xff; ++ _mt_fe_tn_set_reg(state, 0x36, buf); ++ if (F == 0) { ++ if (state->tuner_mtt == 0xD1) { ++ _mt_fe_tn_set_reg(state, 0x3d, 0xca); ++ } else if (state->tuner_mtt == 0xE1) { ++ _mt_fe_tn_set_reg(state, 0x3d, 0xfe); ++ } else { ++ return 1; ++ } ++ _mt_fe_tn_set_reg(state, 0x3e, 0x9c); ++ _mt_fe_tn_set_reg(state, 0x3f, 0x34); ++ } ++ if (F > 0) { ++ if (state->tuner_mtt == 0xD1) { ++ if ((F == 32) || (F == 16) || (F == 48)) { ++ _mt_fe_tn_set_reg(state, 0x3e, 0xa4); ++ _mt_fe_tn_set_reg(state, 0x3d, 0x4a); ++ _mt_fe_tn_set_reg(state, 0x3f, 0x36); ++ } else { ++ _mt_fe_tn_set_reg(state, 0x3e, 0xa4); ++ _mt_fe_tn_set_reg(state, 0x3d, 0x4a); ++ _mt_fe_tn_set_reg(state, 0x3f, 0x36); ++ } ++ } else if (state->tuner_mtt == 0xE1) { ++ _mt_fe_tn_set_reg(state, 0x3e, 0xa4); ++ _mt_fe_tn_set_reg(state, 0x3d, 0x7e); ++ _mt_fe_tn_set_reg(state, 0x3f, 0x36); ++ _mt_fe_tn_set_reg(state, 0x89, 0x84); ++ _mt_fe_tn_get_reg(state, 0x39, &buf); ++ buf = buf & 0x1f; ++ _mt_fe_tn_set_reg(state, 0x39, buf); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = buf | 0x02; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ } else { ++ return 1; ++ } ++ } ++ _mt_fe_tn_set_reg(state, 0x41, 0x00); ++ if (state->tuner_mtt == 0xD1) { ++ msleep(5); ++ } else if (state->tuner_mtt == 0xE1) { ++ msleep(2); ++ } else { ++ return 1; ++ } ++ _mt_fe_tn_set_reg(state, 0x41, 0x02); ++ _mt_fe_tn_set_reg(state, 0x30, 0x7f); ++ _mt_fe_tn_set_reg(state, 0x30, 0xff); ++ _mt_fe_tn_set_reg(state, 0x31, 0x80); ++ _mt_fe_tn_set_reg(state, 0x31, 0x00); ++ ++ return 0; ++} ++ ++static int _mt_fe_tn_set_PLL_freq_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ u8 buf, buf1; ++ u32 freq_thres_div2_KHz, freq_thres_div3r_KHz, ++ freq_thres_div3_KHz; ++ const u32 freq_KHz = state->tuner_freq; ++ if (state->tuner_mtt == 0xD1) { ++ _mt_fe_tn_set_reg(state, 0x32, 0xe1); ++ _mt_fe_tn_set_reg(state, 0x33, 0xa6); ++ _mt_fe_tn_set_reg(state, 0x37, 0x7f); ++ _mt_fe_tn_set_reg(state, 0x38, 0x20); ++ _mt_fe_tn_set_reg(state, 0x39, 0x18); ++ _mt_fe_tn_set_reg(state, 0x40, 0x40); ++ freq_thres_div2_KHz = 520000; ++ _mt_fe_tn_cali_PLL_tc2800(state, freq_KHz, ++ freq_thres_div2_KHz, 0, 0); ++ msleep(5); ++ _mt_fe_tn_get_reg(state, 0x3a, &buf); ++ buf1 = buf; ++ buf = buf & 0x03; ++ buf1 = buf1 & 0x01; ++ if ((buf1 == 0) || (buf == 3)) { ++ freq_thres_div2_KHz = 420000; ++ _mt_fe_tn_cali_PLL_tc2800(state, freq_KHz, ++ freq_thres_div2_KHz, 0, ++ 0); ++ msleep(5); ++ _mt_fe_tn_get_reg(state, 0x3a, &buf); ++ buf = buf & 0x07; ++ if (buf == 5) { ++ freq_thres_div2_KHz = 520000; ++ _mt_fe_tn_cali_PLL_tc2800(state, freq_KHz, ++ freq_thres_div2_KHz, ++ 0, 0); ++ msleep(5); ++ } ++ } ++ _mt_fe_tn_get_reg(state, 0x38, &buf); ++ _mt_fe_tn_set_reg(state, 0x38, buf); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = buf | 0x10; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ _mt_fe_tn_set_reg(state, 0x30, 0x7f); ++ _mt_fe_tn_set_reg(state, 0x30, 0xff); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = buf & 0xdf; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ _mt_fe_tn_set_reg(state, 0x40, 0x0); ++ _mt_fe_tn_set_reg(state, 0x30, 0x7f); ++ _mt_fe_tn_set_reg(state, 0x30, 0xff); ++ _mt_fe_tn_set_reg(state, 0x31, 0x80); ++ _mt_fe_tn_set_reg(state, 0x31, 0x00); ++ msleep(5); ++ _mt_fe_tn_get_reg(state, 0x39, &buf); ++ buf = buf >> 5; ++ if (buf < 5) { ++ _mt_fe_tn_get_reg(state, 0x39, &buf); ++ buf = buf | 0xa0; ++ buf = buf & 0xbf; ++ _mt_fe_tn_set_reg(state, 0x39, buf); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = buf | 0x02; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ } ++ _mt_fe_tn_get_reg(state, 0x37, &buf); ++ if (buf > 0x70) { ++ buf = 0x7f; ++ _mt_fe_tn_set_reg(state, 0x40, 0x40); ++ } ++ _mt_fe_tn_set_reg(state, 0x37, buf); ++ _mt_fe_tn_get_reg(state, 0x38, &buf); ++ if (buf < 0x0f) { ++ buf = (buf & 0x0f) << 2; ++ buf = buf + 0x0f; ++ _mt_fe_tn_set_reg(state, 0x37, buf); ++ } else if (buf < 0x1f) { ++ buf = buf + 0x0f; ++ _mt_fe_tn_set_reg(state, 0x37, buf); ++ } ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = (buf | 0x20) & 0xef; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ _mt_fe_tn_set_reg(state, 0x41, 0x00); ++ msleep(5); ++ _mt_fe_tn_set_reg(state, 0x41, 0x02); ++ } else if (state->tuner_mtt == 0xE1) { ++ freq_thres_div2_KHz = 580000; ++ freq_thres_div3r_KHz = 500000; ++ freq_thres_div3_KHz = 440000; ++ _mt_fe_tn_cali_PLL_tc2800(state, freq_KHz, ++ freq_thres_div2_KHz, ++ freq_thres_div3r_KHz, ++ freq_thres_div3_KHz); ++ msleep(3); ++ _mt_fe_tn_get_reg(state, 0x38, &buf); ++ _mt_fe_tn_set_reg(state, 0x38, buf); ++ _mt_fe_tn_set_reg(state, 0x30, 0x7f); ++ _mt_fe_tn_set_reg(state, 0x30, 0xff); ++ _mt_fe_tn_set_reg(state, 0x31, 0x80); ++ _mt_fe_tn_set_reg(state, 0x31, 0x00); ++ msleep(3); ++ _mt_fe_tn_get_reg(state, 0x38, &buf); ++ _mt_fe_tn_set_reg(state, 0x38, buf); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = buf | 0x10; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ _mt_fe_tn_set_reg(state, 0x30, 0x7f); ++ _mt_fe_tn_set_reg(state, 0x30, 0xff); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = buf & 0xdf; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ _mt_fe_tn_set_reg(state, 0x31, 0x80); ++ _mt_fe_tn_set_reg(state, 0x31, 0x00); ++ msleep(3); ++ _mt_fe_tn_get_reg(state, 0x37, &buf); ++ _mt_fe_tn_set_reg(state, 0x37, buf); ++ /* ++ if ((freq_KHz == 802000) || (freq_KHz == 826000)) { ++ _mt_fe_tn_set_reg(state, 0x37, 0x5e); ++ } ++ */ ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = (buf & 0xef) | 0x30; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ _mt_fe_tn_set_reg(state, 0x41, 0x00); ++ msleep(2); ++ _mt_fe_tn_set_reg(state, 0x41, 0x02); ++ } else { ++ return 1; ++ } ++ return 0; ++} ++ ++static int _mt_fe_tn_set_BB_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ return 0; ++} ++ ++ static int _mt_fe_tn_set_appendix_tc2800(struct dvbsky_m88dc2800_state *state) ++ ++{ ++ u8 buf; ++ const u32 freq_KHz = state->tuner_freq; ++ if (state->tuner_mtt == 0xD1) { ++ if ((freq_KHz == 123000) || (freq_KHz == 147000) || ++ (freq_KHz == 171000) || (freq_KHz == 195000)) { ++ _mt_fe_tn_set_reg(state, 0x20, 0x1b); ++ } ++ if ((freq_KHz == 371000) || (freq_KHz == 419000) || ++ (freq_KHz == 610000) || (freq_KHz == 730000) || ++ (freq_KHz == 754000) || (freq_KHz == 826000)) { ++ _mt_fe_tn_get_reg(state, 0x0d, &buf); ++ _mt_fe_tn_set_reg(state, 0x0d, (u8) (buf + 1)); ++ } ++ if ((freq_KHz == 522000) || (freq_KHz == 578000) || ++ (freq_KHz == 634000) || (freq_KHz == 690000) || ++ (freq_KHz == 834000)) { ++ _mt_fe_tn_get_reg(state, 0x0d, &buf); ++ _mt_fe_tn_set_reg(state, 0x0d, (u8) (buf - 1)); ++ } ++ } else if (state->tuner_mtt == 0xE1) { ++ _mt_fe_tn_set_reg(state, 0x20, 0xfc); ++ if (freq_KHz == 123000 || freq_KHz == 147000 || ++ freq_KHz == 171000 || freq_KHz == 195000 || ++ freq_KHz == 219000 || freq_KHz == 267000 || ++ freq_KHz == 291000 || freq_KHz == 339000 || ++ freq_KHz == 387000 || freq_KHz == 435000 || ++ freq_KHz == 482000 || freq_KHz == 530000 || ++ freq_KHz == 722000 || ++ (state->tuner_custom_cfg == 1 && freq_KHz == 315000)) { ++ _mt_fe_tn_set_reg(state, 0x20, 0x5c); ++ } ++ } ++ return 0; ++} ++ ++ static int _mt_fe_tn_set_DAC_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ u8 buf, tempnumber; ++ s32 N; ++ s32 f1f2number, f1, f2, delta1, Totalnum1; ++ s32 cntT, cntin, NCOI, z0, z1, z2, tmp; ++ u32 fc, fadc, fsd, f2d; ++ u32 FreqTrue108_Hz; ++ s32 M = state->tuner_crystal / 4000; ++ /* const u8 bandwidth = state->tuner_bandwidth; */ ++ const u16 DAC_fre = 108; ++ const u32 crystal_KHz = state->tuner_crystal; ++ const u32 DACFreq_KHz = state->tuner_dac; ++ const u32 freq_KHz = state->tuner_freq; ++ ++ if (state->tuner_mtt == 0xE1) { ++ _mt_fe_tn_get_reg(state, 0x33, &buf); ++ M = buf & 0x0f; ++ if (M == 0) ++ M = 6; ++ } ++ _mt_fe_tn_get_reg(state, 0x34, &buf); ++ N = buf & 0x07; ++ _mt_fe_tn_get_reg(state, 0x35, &buf); ++ N = (N << 8) + buf; ++ buf = ((N + 256) * crystal_KHz / M / DAC_fre + 500) / 1000; ++ if (state->tuner_mtt == 0xE1) { ++ _mt_fe_tn_set_appendix_tc2800(state); ++ if (freq_KHz == 187000 || freq_KHz == 195000 || ++ freq_KHz == 131000 || freq_KHz == 211000 || ++ freq_KHz == 219000 || freq_KHz == 227000 || ++ freq_KHz == 267000 || freq_KHz == 299000 || ++ freq_KHz == 347000 || freq_KHz == 363000 || ++ freq_KHz == 395000 || freq_KHz == 403000 || ++ freq_KHz == 435000 || freq_KHz == 482000 || ++ freq_KHz == 474000 || freq_KHz == 490000 || ++ freq_KHz == 610000 || freq_KHz == 642000 || ++ freq_KHz == 666000 || freq_KHz == 722000 || ++ freq_KHz == 754000 || ++ ((freq_KHz == 379000 || freq_KHz == 467000 || ++ freq_KHz == 762000) && state->tuner_custom_cfg != 1)) { ++ buf = buf + 1; ++ } ++ if (freq_KHz == 123000 || freq_KHz == 139000 || ++ freq_KHz == 147000 || freq_KHz == 171000 || ++ freq_KHz == 179000 || freq_KHz == 203000 || ++ freq_KHz == 235000 || freq_KHz == 251000 || ++ freq_KHz == 259000 || freq_KHz == 283000 || ++ freq_KHz == 331000 || freq_KHz == 363000 || ++ freq_KHz == 371000 || freq_KHz == 387000 || ++ freq_KHz == 411000 || freq_KHz == 427000 || ++ freq_KHz == 443000 || freq_KHz == 451000 || ++ freq_KHz == 459000 || freq_KHz == 506000 || ++ freq_KHz == 514000 || freq_KHz == 538000 || ++ freq_KHz == 546000 || freq_KHz == 554000 || ++ freq_KHz == 562000 || freq_KHz == 570000 || ++ freq_KHz == 578000 || freq_KHz == 602000 || ++ freq_KHz == 626000 || freq_KHz == 658000 || ++ freq_KHz == 690000 || freq_KHz == 714000 || ++ freq_KHz == 746000 || freq_KHz == 522000 || ++ freq_KHz == 826000 || freq_KHz == 155000 || ++ freq_KHz == 530000 || ++ ((freq_KHz == 275000 || freq_KHz == 355000) && ++ state->tuner_custom_cfg != 1) || ++ ((freq_KHz == 467000 || freq_KHz == 762000 || ++ freq_KHz == 778000 || freq_KHz == 818000) && ++ state->tuner_custom_cfg == 1)) { ++ buf = buf - 1; ++ } ++ } ++ _mt_fe_tn_set_reg(state, 0x0e, buf); ++ _mt_fe_tn_set_reg(state, 0x0d, buf); ++ f1f2number = ++ (((DACFreq_KHz * M * buf) / crystal_KHz) << 16) / (N + 256) + ++ (((DACFreq_KHz * M * buf) % crystal_KHz) << 16) / ((N + 256) * ++ crystal_KHz); ++ _mt_fe_tn_set_reg(state, 0xf1, (f1f2number & 0xff00) >> 8); ++ _mt_fe_tn_set_reg(state, 0xf2, f1f2number & 0x00ff); ++ FreqTrue108_Hz = ++ (N + 256) * crystal_KHz / (M * buf) * 1000 + ++ (((N + 256) * crystal_KHz) % (M * buf)) * 1000 / (M * buf); ++ f1 = 4096; ++ fc = FreqTrue108_Hz; ++ fadc = fc / 4; ++ fsd = 27000000; ++ f2d = state->tuner_bandwidth * 1000 / 2 - 150; ++ f2 = (fsd / 250) * f2d / ((fc + 500) / 1000); ++ delta1 = ((f1 - f2) << 15) / f2; ++ Totalnum1 = ((f1 - f2) << 15) - delta1 * f2; ++ cntT = f2; ++ cntin = Totalnum1; ++ NCOI = delta1; ++ z0 = cntin; ++ z1 = cntT; ++ z2 = NCOI; ++ tempnumber = (z0 & 0xff00) >> 8; ++ _mt_fe_tn_set_reg(state, 0xc9, (u8) (tempnumber & 0x0f)); ++ tempnumber = (z0 & 0xff); ++ _mt_fe_tn_set_reg(state, 0xca, tempnumber); ++ tempnumber = (z1 & 0xff00) >> 8; ++ _mt_fe_tn_set_reg(state, 0xcb, tempnumber); ++ tempnumber = (z1 & 0xff); ++ _mt_fe_tn_set_reg(state, 0xcc, tempnumber); ++ tempnumber = (z2 & 0xff00) >> 8; ++ _mt_fe_tn_set_reg(state, 0xcd, tempnumber); ++ tempnumber = (z2 & 0xff); ++ _mt_fe_tn_set_reg(state, 0xce, tempnumber); ++ tmp = f1; ++ f1 = f2; ++ f2 = tmp / 2; ++ delta1 = ((f1 - f2) << 15) / f2; ++ Totalnum1 = ((f1 - f2) << 15) - delta1 * f2; ++ NCOI = (f1 << 15) / f2 - (1 << 15); ++ cntT = f2; ++ cntin = Totalnum1; ++ z0 = cntin; ++ z1 = cntT; ++ z2 = NCOI; ++ tempnumber = (z0 & 0xff00) >> 8; ++ _mt_fe_tn_set_reg(state, 0xd9, (u8) (tempnumber & 0x0f)); ++ tempnumber = (z0 & 0xff); ++ _mt_fe_tn_set_reg(state, 0xda, tempnumber); ++ tempnumber = (z1 & 0xff00) >> 8; ++ _mt_fe_tn_set_reg(state, 0xdb, tempnumber); ++ tempnumber = (z1 & 0xff); ++ _mt_fe_tn_set_reg(state, 0xdc, tempnumber); ++ tempnumber = (z2 & 0xff00) >> 8; ++ _mt_fe_tn_set_reg(state, 0xdd, tempnumber); ++ tempnumber = (z2 & 0xff); ++ _mt_fe_tn_set_reg(state, 0xde, tempnumber); ++ ++ return 0; ++} ++ ++static int _mt_fe_tn_preset_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ if (state->tuner_mtt == 0xD1) { ++ _mt_fe_tn_set_reg(state, 0x19, 0x4a); ++ _mt_fe_tn_set_reg(state, 0x1b, 0x4b); ++ _mt_fe_tn_set_reg(state, 0x04, 0x04); ++ _mt_fe_tn_set_reg(state, 0x17, 0x0d); ++ _mt_fe_tn_set_reg(state, 0x62, 0x6c); ++ _mt_fe_tn_set_reg(state, 0x63, 0xf4); ++ _mt_fe_tn_set_reg(state, 0x1f, 0x0e); ++ _mt_fe_tn_set_reg(state, 0x6b, 0xf4); ++ _mt_fe_tn_set_reg(state, 0x14, 0x01); ++ _mt_fe_tn_set_reg(state, 0x5a, 0x75); ++ _mt_fe_tn_set_reg(state, 0x66, 0x74); ++ _mt_fe_tn_set_reg(state, 0x72, 0xe0); ++ _mt_fe_tn_set_reg(state, 0x70, 0x07); ++ _mt_fe_tn_set_reg(state, 0x15, 0x7b); ++ _mt_fe_tn_set_reg(state, 0x55, 0x71); ++ _mt_fe_tn_set_reg(state, 0x75, 0x55); ++ _mt_fe_tn_set_reg(state, 0x76, 0xac); ++ _mt_fe_tn_set_reg(state, 0x77, 0x6c); ++ _mt_fe_tn_set_reg(state, 0x78, 0x8b); ++ _mt_fe_tn_set_reg(state, 0x79, 0x42); ++ _mt_fe_tn_set_reg(state, 0x7a, 0xd2); ++ _mt_fe_tn_set_reg(state, 0x81, 0x01); ++ _mt_fe_tn_set_reg(state, 0x82, 0x00); ++ _mt_fe_tn_set_reg(state, 0x82, 0x02); ++ _mt_fe_tn_set_reg(state, 0x82, 0x04); ++ _mt_fe_tn_set_reg(state, 0x82, 0x06); ++ _mt_fe_tn_set_reg(state, 0x82, 0x08); ++ _mt_fe_tn_set_reg(state, 0x82, 0x09); ++ _mt_fe_tn_set_reg(state, 0x82, 0x29); ++ _mt_fe_tn_set_reg(state, 0x82, 0x49); ++ _mt_fe_tn_set_reg(state, 0x82, 0x58); ++ _mt_fe_tn_set_reg(state, 0x82, 0x59); ++ _mt_fe_tn_set_reg(state, 0x82, 0x98); ++ _mt_fe_tn_set_reg(state, 0x82, 0x99); ++ _mt_fe_tn_set_reg(state, 0x10, 0x05); ++ _mt_fe_tn_set_reg(state, 0x10, 0x0d); ++ _mt_fe_tn_set_reg(state, 0x11, 0x95); ++ _mt_fe_tn_set_reg(state, 0x11, 0x9d); ++ if (state->tuner_loopthrough != 0) { ++ _mt_fe_tn_set_reg(state, 0x67, 0x25); ++ } else { ++ _mt_fe_tn_set_reg(state, 0x67, 0x05); ++ } ++ } else if (state->tuner_mtt == 0xE1) { ++ _mt_fe_tn_set_reg(state, 0x1b, 0x47); ++ if (state->tuner_mode == 0) { /* DVB-C */ ++ _mt_fe_tn_set_reg(state, 0x66, 0x74); ++ _mt_fe_tn_set_reg(state, 0x62, 0x2c); ++ _mt_fe_tn_set_reg(state, 0x63, 0x54); ++ _mt_fe_tn_set_reg(state, 0x68, 0x0b); ++ _mt_fe_tn_set_reg(state, 0x14, 0x00); ++ } else { /* CTTB */ ++ _mt_fe_tn_set_reg(state, 0x66, 0x74); ++ _mt_fe_tn_set_reg(state, 0x62, 0x0c); ++ _mt_fe_tn_set_reg(state, 0x63, 0x54); ++ _mt_fe_tn_set_reg(state, 0x68, 0x0b); ++ _mt_fe_tn_set_reg(state, 0x14, 0x05); ++ } ++ _mt_fe_tn_set_reg(state, 0x6f, 0x00); ++ _mt_fe_tn_set_reg(state, 0x84, 0x04); ++ _mt_fe_tn_set_reg(state, 0x5e, 0xbe); ++ _mt_fe_tn_set_reg(state, 0x87, 0x07); ++ _mt_fe_tn_set_reg(state, 0x8a, 0x1f); ++ _mt_fe_tn_set_reg(state, 0x8b, 0x1f); ++ _mt_fe_tn_set_reg(state, 0x88, 0x30); ++ _mt_fe_tn_set_reg(state, 0x58, 0x34); ++ _mt_fe_tn_set_reg(state, 0x61, 0x8c); ++ _mt_fe_tn_set_reg(state, 0x6a, 0x42); ++ } ++ return 0; ++} ++ ++static int mt_fe_tn_wakeup_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ _mt_fe_tn_set_reg(state, 0x16, 0xb1); ++ _mt_fe_tn_set_reg(state, 0x09, 0x7d); ++ return 0; ++} ++ ++ static int mt_fe_tn_sleep_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ _mt_fe_tn_set_reg(state, 0x16, 0xb0); ++ _mt_fe_tn_set_reg(state, 0x09, 0x6d); ++ return 0; ++} ++ ++ static int mt_fe_tn_init_tc2800(struct dvbsky_m88dc2800_state *state) ++{ ++ if (state->tuner_init_OK != 1) { ++ state->tuner_dev_addr = 0x61; /* TUNER_I2C_ADDR_TC2800 */ ++ state->tuner_freq = 650000; ++ state->tuner_qam = 0; ++ state->tuner_mode = 0; // 0: DVB-C, 1: CTTB ++ state->tuner_bandwidth = 8; ++ state->tuner_loopthrough = 0; ++ state->tuner_crystal = 24000; ++ state->tuner_dac = 7200; ++ state->tuner_mtt = 0x00; ++ state->tuner_custom_cfg = 0; ++ state->tuner_version = 30022; /* Driver version number */ ++ state->tuner_time = 12092611; ++ state->tuner_init_OK = 1; ++ } ++ _mt_fe_tn_set_reg(state, 0x2b, 0x46); ++ _mt_fe_tn_set_reg(state, 0x2c, 0x75); ++ if (state->tuner_mtt == 0x00) { ++ u8 tmp = 0; ++ _mt_fe_tn_get_reg(state, 0x01, &tmp); ++ printk(KERN_INFO "m88dc2800: tuner id = 0x%02x ", tmp); ++ switch (tmp) { ++ case 0x0d: ++ state->tuner_mtt = 0xD1; ++ break; ++ case 0x8e: ++ default: ++ state->tuner_mtt = 0xE1; ++ break; ++ } ++ } ++ return 0; ++} ++ ++ static int mt_fe_tn_set_freq_tc2800(struct dvbsky_m88dc2800_state *state, ++ u32 freq_KHz) ++{ ++ u8 buf; ++ u8 buf1; ++ ++ mt_fe_tn_init_tc2800(state); ++ state->tuner_freq = freq_KHz; ++ _mt_fe_tn_set_reg(state, 0x21, freq_KHz > 500000 ? 0xb9 : 0x99); ++ mt_fe_tn_wakeup_tc2800(state); ++ _mt_fe_tn_set_reg(state, 0x05, 0x7f); ++ _mt_fe_tn_set_reg(state, 0x06, 0xf8); ++ _mt_fe_tn_set_RF_front_tc2800(state); ++ _mt_fe_tn_set_PLL_freq_tc2800(state); ++ _mt_fe_tn_set_DAC_tc2800(state); ++ _mt_fe_tn_set_BB_tc2800(state); ++ _mt_fe_tn_preset_tc2800(state); ++ _mt_fe_tn_set_reg(state, 0x05, 0x00); ++ _mt_fe_tn_set_reg(state, 0x06, 0x00); ++ if (state->tuner_mtt == 0xD1) { ++ _mt_fe_tn_set_reg(state, 0x00, 0x01); ++ _mt_fe_tn_set_reg(state, 0x00, 0x00); ++ msleep(5); ++ _mt_fe_tn_set_reg(state, 0x41, 0x00); ++ msleep(5); ++ _mt_fe_tn_set_reg(state, 0x41, 0x02); ++ ++ _mt_fe_tn_get_reg(state, 0x69, &buf1); ++ buf1 = buf1 & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x61, &buf); ++ buf = buf & 0x0f; ++ if (buf == 0x0c) ++ _mt_fe_tn_set_reg(state, 0x6a, 0x59); ++ if (buf1 > 0x02) { ++ if (freq_KHz > 600000) ++ _mt_fe_tn_set_reg(state, 0x66, 0x44); ++ else if (freq_KHz > 500000) ++ _mt_fe_tn_set_reg(state, 0x66, 0x64); ++ else ++ _mt_fe_tn_set_reg(state, 0x66, 0x74); ++ } ++ if (buf1 < 0x03) { ++ if (freq_KHz > 800000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x64); ++ else if (freq_KHz > 600000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ else if (freq_KHz > 500000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ else if (freq_KHz > 300000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x43); ++ else if (freq_KHz > 220000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ else if (freq_KHz > 110000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x14); ++ else ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ msleep(5); ++ } else if (buf < 0x0c) { ++ if (freq_KHz > 800000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x14); ++ else if (freq_KHz > 600000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x14); ++ else if (freq_KHz > 500000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x34); ++ else if (freq_KHz > 300000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x43); ++ else if (freq_KHz > 220000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ else if (freq_KHz > 110000) ++ _mt_fe_tn_set_reg(state, 0x87, 0x14); ++ else ++ _mt_fe_tn_set_reg(state, 0x87, 0x54); ++ msleep(5); ++ } ++ } else if ((state->tuner_mtt == 0xE1)) { ++ _mt_fe_tn_set_reg(state, 0x00, 0x01); ++ _mt_fe_tn_set_reg(state, 0x00, 0x00); ++ msleep(20); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = (buf & 0xef) | 0x28; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ msleep(50); ++ _mt_fe_tn_get_reg(state, 0x38, &buf); ++ _mt_fe_tn_set_reg(state, 0x38, buf); ++ _mt_fe_tn_get_reg(state, 0x32, &buf); ++ buf = (buf & 0xf7) | 0x10; ++ _mt_fe_tn_set_reg(state, 0x32, buf); ++ msleep(10); ++ _mt_fe_tn_get_reg(state, 0x69, &buf); ++ buf = buf & 0x03; ++ _mt_fe_tn_set_reg(state, 0x2a, buf); ++ if (buf > 0) { ++ msleep(20); ++ _mt_fe_tn_get_reg(state, 0x84, &buf); ++ buf = buf & 0x1f; ++ _mt_fe_tn_set_reg(state, 0x68, 0x0a); ++ _mt_fe_tn_get_reg(state, 0x88, &buf1); ++ buf1 = buf1 & 0x1f; ++ if (buf <= buf1) ++ _mt_fe_tn_set_reg(state, 0x66, 0x44); ++ else ++ _mt_fe_tn_set_reg(state, 0x66, 0x74); ++ } else { ++ if (freq_KHz <= 600000) ++ _mt_fe_tn_set_reg(state, 0x68, 0x0c); ++ else ++ _mt_fe_tn_set_reg(state, 0x68, 0x0e); ++ _mt_fe_tn_set_reg(state, 0x30, 0xfb); ++ _mt_fe_tn_set_reg(state, 0x30, 0xff); ++ _mt_fe_tn_set_reg(state, 0x31, 0x04); ++ _mt_fe_tn_set_reg(state, 0x31, 0x00); ++ } ++ if (state->tuner_loopthrough != 0) { ++ _mt_fe_tn_get_reg(state, 0x28, &buf); ++ if (buf == 0) { ++ _mt_fe_tn_set_reg(state, 0x28, 0xff); ++ _mt_fe_tn_get_reg(state, 0x61, &buf); ++ buf = buf & 0x0f; ++ if (buf > 9) ++ _mt_fe_tn_set_reg(state, 0x67, 0x74); ++ else if (buf > 6) ++ _mt_fe_tn_set_reg(state, 0x67, 0x64); ++ else if (buf > 3) ++ _mt_fe_tn_set_reg(state, 0x67, 0x54); ++ else ++ _mt_fe_tn_set_reg(state, 0x67, 0x44); ++ } ++ } else { ++ _mt_fe_tn_set_reg(state, 0x67, 0x34); ++ } ++ } else { ++ return 1; ++ } ++ return 0; ++} ++ ++ ++/* ++static int mt_fe_tn_set_BB_filter_band_tc2800(struct dvbsky_m88dc2800_state *state, ++ u8 bandwidth) ++{ ++ u8 buf, tmp; ++ ++ _mt_fe_tn_get_reg(state, 0x53, &tmp); ++ ++ if (bandwidth == 6) ++ buf = 0x01 << 1; ++ else if (bandwidth == 7) ++ buf = 0x02 << 1; ++ else if (bandwidth == 8) ++ buf = 0x04 << 1; ++ else ++ buf = 0x04 << 1; ++ ++ tmp &= 0xf1; ++ tmp |= buf; ++ _mt_fe_tn_set_reg(state, 0x53, tmp); ++ state->tuner_bandwidth = bandwidth; ++ return 0; ++} ++*/ ++ ++static s32 mt_fe_tn_get_signal_strength_tc2800(struct dvbsky_m88dc2800_state ++ *state) ++{ ++ s32 level = -107; ++ s32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; ++ s32 val1, val2, val; ++ s32 result2, result3, result4, result5, result6; ++ s32 append; ++ u8 tmp; ++ s32 freq_KHz = (s32) state->tuner_freq; ++ if (state->tuner_mtt == 0xD1) { ++ _mt_fe_tn_get_reg(state, 0x61, &tmp); ++ tmp1 = tmp & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x69, &tmp); ++ tmp2 = tmp & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x73, &tmp); ++ tmp3 = tmp & 0x07; ++ _mt_fe_tn_get_reg(state, 0x7c, &tmp); ++ tmp4 = (tmp >> 4) & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x7b, &tmp); ++ tmp5 = tmp & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x7f, &tmp); ++ tmp6 = (tmp >> 5) & 0x01; ++ if (tmp1 > 6) { ++ val1 = 0; ++ if (freq_KHz <= 200000) { ++ val2 = (tmp1 - 6) * 267; ++ } else if (freq_KHz <= 600000) { ++ val2 = (tmp1 - 6) * 280; ++ } else { ++ val2 = (tmp1 - 6) * 290; ++ } ++ val = val1 + val2; ++ } else { ++ if (tmp1 == 0) { ++ val1 = -550; ++ } else { ++ val1 = 0; ++ } ++ if ((tmp1 < 4) && (freq_KHz >= 506000)) { ++ val1 = -850; ++ } ++ val2 = 0; ++ val = val1 + val2; ++ } ++ if (freq_KHz <= 95000) { ++ result2 = tmp2 * 289; ++ } else if (freq_KHz <= 155000) { ++ result2 = tmp2 * 278; ++ } else if (freq_KHz <= 245000) { ++ result2 = tmp2 * 267; ++ } else if (freq_KHz <= 305000) { ++ result2 = tmp2 * 256; ++ } else if (freq_KHz <= 335000) { ++ result2 = tmp2 * 244; ++ } else if (freq_KHz <= 425000) { ++ result2 = tmp2 * 233; ++ } else if (freq_KHz <= 575000) { ++ result2 = tmp2 * 222; ++ } else if (freq_KHz <= 665000) { ++ result2 = tmp2 * 211; ++ } else { ++ result2 = tmp2 * 200; ++ } ++ result3 = (6 - tmp3) * 100; ++ result4 = 300 * tmp4; ++ result5 = 50 * tmp5; ++ result6 = 300 * tmp6; ++ if (freq_KHz < 105000) { ++ append = -450; ++ } else if (freq_KHz <= 227000) { ++ append = -4 * (freq_KHz / 1000 - 100) + 150; ++ } else if (freq_KHz <= 305000) { ++ append = -4 * (freq_KHz / 1000 - 100); ++ } else if (freq_KHz <= 419000) { ++ append = 500 - 40 * (freq_KHz / 1000 - 300) / 17 + 130; ++ } else if (freq_KHz <= 640000) { ++ append = 500 - 40 * (freq_KHz / 1000 - 300) / 17; ++ } else { ++ append = -500; ++ } ++ level = append - (val + result2 + result3 + result4 + ++ result5 + result6); ++ level /= 100; ++ } else if (state->tuner_mtt == 0xE1) { ++ _mt_fe_tn_get_reg(state, 0x61, &tmp); ++ tmp1 = tmp & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x84, &tmp); ++ tmp2 = tmp & 0x1f; ++ _mt_fe_tn_get_reg(state, 0x69, &tmp); ++ tmp3 = tmp & 0x03; ++ _mt_fe_tn_get_reg(state, 0x73, &tmp); ++ tmp4 = tmp & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x7c, &tmp); ++ tmp5 = (tmp >> 4) & 0x0f; ++ _mt_fe_tn_get_reg(state, 0x7b, &tmp); ++ tmp6 = tmp & 0x0f; ++ if (freq_KHz < 151000) { ++ result2 = (1150 - freq_KHz / 100) * 163 / 33 + 4230; ++ result3 = (1150 - freq_KHz / 100) * 115 / 33 + 1850; ++ result4 = -3676 * (freq_KHz / 1000) / 100 + 6115; ++ } else if (freq_KHz < 257000) { ++ result2 = (1540 - freq_KHz / 100) * 11 / 4 + 3870; ++ result3 = (1540 - freq_KHz / 100) * 205 / 96 + 2100; ++ result4 = -21 * freq_KHz / 1000 + 5084; ++ } else if (freq_KHz < 305000) { ++ result2 = (2620 - freq_KHz / 100) * 5 / 3 + 2770; ++ result3 = (2620 - freq_KHz / 100) * 10 / 7 + 1700; ++ result4 = 650; ++ } else if (freq_KHz < 449000) { ++ result2 = (307 - freq_KHz / 1000) * 82 / 27 + 11270; ++ result3 = (3100 - freq_KHz / 100) * 5 / 3 + 10000; ++ result4 = 134 * freq_KHz / 10000 + 11875; ++ } else { ++ result2 = (307 - freq_KHz / 1000) * 82 / 27 + 11270; ++ result3 = 8400; ++ result4 = 5300; ++ } ++ if (tmp1 > 6) { ++ val1 = result2; ++ val2 = 2900; ++ val = 500; ++ } else if (tmp1 > 0) { ++ val1 = result3; ++ val2 = 2700; ++ val = 500; ++ } else { ++ val1 = result4; ++ val2 = 2700; ++ val = 400; ++ } ++ level = val1 - (val2 * tmp1 + 500 * tmp2 + 3000 * tmp3 - ++ 500 * tmp4 + 3000 * tmp5 + val * tmp6) - 1000; ++ level /= 1000; ++ } ++ return level; ++} ++ ++ ++/* m88dc2800 operation functions */ ++u8 M88DC2000GetLock(struct dvbsky_m88dc2800_state * state) ++{ ++ u8 u8ret = 0; ++ if (ReadReg(state, 0x80) < 0x06) { ++ if ((ReadReg(state, 0xdf) & 0x80) == 0x80 ++ &&(ReadReg(state, 0x91) & 0x23) == 0x03 ++ &&(ReadReg(state, 0x43) & 0x08) == 0x08) ++ u8ret = 1; ++ else ++ u8ret = 0; ++ } else { ++ if ((ReadReg(state, 0x85) & 0x08) == 0x08) ++ u8ret = 1; ++ else ++ u8ret = 0; ++ } ++ dprintk("%s, lock=%d\n", __func__, u8ret); ++ return u8ret; ++} ++ ++static int M88DC2000SetTsType(struct dvbsky_m88dc2800_state *state, u8 type) ++{ ++ u8 regC2H; ++ ++ if (type == 3) { ++ WriteReg(state, 0x84, 0x6A); ++ WriteReg(state, 0xC0, 0x43); ++ WriteReg(state, 0xE2, 0x06); ++ regC2H = ReadReg(state, 0xC2); ++ regC2H &= 0xC0; ++ regC2H |= 0x1B; ++ WriteReg(state, 0xC2, regC2H); ++ WriteReg(state, 0xC1, 0x60); /* common interface */ ++ } else if (type == 1) { ++ WriteReg(state, 0x84, 0x6A); ++ WriteReg(state, 0xC0, 0x47); /* serial format */ ++ WriteReg(state, 0xE2, 0x02); ++ regC2H = ReadReg(state, 0xC2); ++ regC2H &= 0xC7; ++ WriteReg(state, 0xC2, regC2H); ++ WriteReg(state, 0xC1, 0x00); ++ } else { ++ WriteReg(state, 0x84, 0x6C); ++ WriteReg(state, 0xC0, 0x43); /* parallel format */ ++ WriteReg(state, 0xE2, 0x06); ++ regC2H = ReadReg(state, 0xC2); ++ regC2H &= 0xC7; ++ WriteReg(state, 0xC2, regC2H); ++ WriteReg(state, 0xC1, 0x00); ++ } ++ return 0; ++} ++ ++static int M88DC2000RegInitial_TC2800(struct dvbsky_m88dc2800_state *state) ++{ ++ u8 RegE3H, RegE4H; ++ ++ WriteReg(state, 0x00, 0x48); ++ WriteReg(state, 0x01, 0x09); ++ WriteReg(state, 0xFB, 0x0A); ++ WriteReg(state, 0xFC, 0x0B); ++ WriteReg(state, 0x02, 0x0B); ++ WriteReg(state, 0x03, 0x18); ++ WriteReg(state, 0x05, 0x0D); ++ WriteReg(state, 0x36, 0x80); ++ WriteReg(state, 0x43, 0x40); ++ WriteReg(state, 0x55, 0x7A); ++ WriteReg(state, 0x56, 0xD9); ++ WriteReg(state, 0x57, 0xDF); ++ WriteReg(state, 0x58, 0x39); ++ WriteReg(state, 0x5A, 0x00); ++ WriteReg(state, 0x5C, 0x71); ++ WriteReg(state, 0x5D, 0x23); ++ WriteReg(state, 0x86, 0x40); ++ WriteReg(state, 0xF9, 0x08); ++ WriteReg(state, 0x61, 0x40); ++ WriteReg(state, 0x62, 0x0A); ++ WriteReg(state, 0x90, 0x06); ++ WriteReg(state, 0xDE, 0x00); ++ WriteReg(state, 0xA0, 0x03); ++ WriteReg(state, 0xDF, 0x81); ++ WriteReg(state, 0xFA, 0x40); ++ WriteReg(state, 0x37, 0x10); ++ WriteReg(state, 0xF0, 0x40); ++ WriteReg(state, 0xF2, 0x9C); ++ WriteReg(state, 0xF3, 0x40); ++ RegE3H = ReadReg(state, 0xE3); ++ RegE4H = ReadReg(state, 0xE4); ++ if (((RegE3H & 0xC0) == 0x00) && ((RegE4H & 0xC0) == 0x00)) { ++ WriteReg(state, 0x30, 0xFF); ++ WriteReg(state, 0x31, 0x00); ++ WriteReg(state, 0x32, 0x00); ++ WriteReg(state, 0x33, 0x00); ++ WriteReg(state, 0x35, 0x32); ++ WriteReg(state, 0x40, 0x00); ++ WriteReg(state, 0x41, 0x10); ++ WriteReg(state, 0xF1, 0x02); ++ WriteReg(state, 0xF4, 0x04); ++ WriteReg(state, 0xF5, 0x00); ++ WriteReg(state, 0x42, 0x14); ++ WriteReg(state, 0xE1, 0x25); ++ } else if (((RegE3H & 0xC0) == 0x80) && ((RegE4H & 0xC0) == 0x40)) { ++ WriteReg(state, 0x30, 0xFF); ++ WriteReg(state, 0x31, 0x00); ++ WriteReg(state, 0x32, 0x00); ++ WriteReg(state, 0x33, 0x00); ++ WriteReg(state, 0x35, 0x32); ++ WriteReg(state, 0x39, 0x00); ++ WriteReg(state, 0x3A, 0x00); ++ WriteReg(state, 0x40, 0x00); ++ WriteReg(state, 0x41, 0x10); ++ WriteReg(state, 0xF1, 0x00); ++ WriteReg(state, 0xF4, 0x00); ++ WriteReg(state, 0xF5, 0x40); ++ WriteReg(state, 0x42, 0x14); ++ WriteReg(state, 0xE1, 0x25); ++ } else if ((RegE3H == 0x80 || RegE3H == 0x81) ++ && (RegE4H == 0x80 || RegE4H == 0x81)) { ++ WriteReg(state, 0x30, 0xFF); ++ WriteReg(state, 0x31, 0x00); ++ WriteReg(state, 0x32, 0x00); ++ WriteReg(state, 0x33, 0x00); ++ WriteReg(state, 0x35, 0x32); ++ WriteReg(state, 0x39, 0x00); ++ WriteReg(state, 0x3A, 0x00); ++ WriteReg(state, 0xF1, 0x00); ++ WriteReg(state, 0xF4, 0x00); ++ WriteReg(state, 0xF5, 0x40); ++ WriteReg(state, 0x42, 0x24); ++ WriteReg(state, 0xE1, 0x25); ++ WriteReg(state, 0x92, 0x7F); ++ WriteReg(state, 0x93, 0x91); ++ WriteReg(state, 0x95, 0x00); ++ WriteReg(state, 0x2B, 0x33); ++ WriteReg(state, 0x2A, 0x2A); ++ WriteReg(state, 0x2E, 0x80); ++ WriteReg(state, 0x25, 0x25); ++ WriteReg(state, 0x2D, 0xFF); ++ WriteReg(state, 0x26, 0xFF); ++ WriteReg(state, 0x27, 0x00); ++ WriteReg(state, 0x24, 0x25); ++ WriteReg(state, 0xA4, 0xFF); ++ WriteReg(state, 0xA3, 0x0D); ++ } else { ++ WriteReg(state, 0x30, 0xFF); ++ WriteReg(state, 0x31, 0x00); ++ WriteReg(state, 0x32, 0x00); ++ WriteReg(state, 0x33, 0x00); ++ WriteReg(state, 0x35, 0x32); ++ WriteReg(state, 0x39, 0x00); ++ WriteReg(state, 0x3A, 0x00); ++ WriteReg(state, 0xF1, 0x00); ++ WriteReg(state, 0xF4, 0x00); ++ WriteReg(state, 0xF5, 0x40); ++ WriteReg(state, 0x42, 0x24); ++ WriteReg(state, 0xE1, 0x27); ++ WriteReg(state, 0x92, 0x7F); ++ WriteReg(state, 0x93, 0x91); ++ WriteReg(state, 0x95, 0x00); ++ WriteReg(state, 0x2B, 0x33); ++ WriteReg(state, 0x2A, 0x2A); ++ WriteReg(state, 0x2E, 0x80); ++ WriteReg(state, 0x25, 0x25); ++ WriteReg(state, 0x2D, 0xFF); ++ WriteReg(state, 0x26, 0xFF); ++ WriteReg(state, 0x27, 0x00); ++ WriteReg(state, 0x24, 0x25); ++ WriteReg(state, 0xA4, 0xFF); ++ WriteReg(state, 0xA3, 0x10); ++ } ++ WriteReg(state, 0xF6, 0x4E); ++ WriteReg(state, 0xF7, 0x20); ++ WriteReg(state, 0x89, 0x02); ++ WriteReg(state, 0x14, 0x08); ++ WriteReg(state, 0x6F, 0x0D); ++ WriteReg(state, 0x10, 0xFF); ++ WriteReg(state, 0x11, 0x00); ++ WriteReg(state, 0x12, 0x30); ++ WriteReg(state, 0x13, 0x23); ++ WriteReg(state, 0x60, 0x00); ++ WriteReg(state, 0x69, 0x00); ++ WriteReg(state, 0x6A, 0x03); ++ WriteReg(state, 0xE0, 0x75); ++ WriteReg(state, 0x8D, 0x29); ++ WriteReg(state, 0x4E, 0xD8); ++ WriteReg(state, 0x88, 0x80); ++ WriteReg(state, 0x52, 0x79); ++ WriteReg(state, 0x53, 0x03); ++ WriteReg(state, 0x59, 0x30); ++ WriteReg(state, 0x5E, 0x02); ++ WriteReg(state, 0x5F, 0x0F); ++ WriteReg(state, 0x71, 0x03); ++ WriteReg(state, 0x72, 0x12); ++ WriteReg(state, 0x73, 0x12); ++ ++ return 0; ++} ++ ++static int M88DC2000AutoTSClock_P(struct dvbsky_m88dc2800_state *state, u32 sym, ++ u16 qam) ++{ ++ u32 dataRate; ++ u8 clk_div, value; ++ printk(KERN_INFO ++ "m88dc2800: M88DC2000AutoTSClock_P, symrate=%d qam=%d\n", ++ sym, qam); ++ switch (qam) { ++ case 16: ++ dataRate = 4; ++ break; ++ case 32: ++ dataRate = 5; ++ break; ++ case 128: ++ dataRate = 7; ++ break; ++ case 256: ++ dataRate = 8; ++ break; ++ case 64: ++ default: ++ dataRate = 6; ++ break; ++ } ++ dataRate *= sym * 105; ++ dataRate /= 800; ++ if (dataRate <= 4115) ++ clk_div = 0x05; ++ else if (dataRate <= 4800) ++ clk_div = 0x04; ++ else if (dataRate <= 5760) ++ clk_div = 0x03; ++ else if (dataRate <= 7200) ++ clk_div = 0x02; ++ else if (dataRate <= 9600) ++ clk_div = 0x01; ++ else ++ clk_div = 0x00; ++ value = ReadReg(state, 0xC2); ++ value &= 0xc0; ++ value |= clk_div; ++ WriteReg(state, 0xC2, value); ++ return 0; ++} ++ ++static int M88DC2000AutoTSClock_C(struct dvbsky_m88dc2800_state *state, u32 sym, ++ u16 qam) ++{ ++ u32 dataRate; ++ u8 clk_div, value; ++ printk(KERN_INFO ++ "m88dc2800: M88DC2000AutoTSClock_C, symrate=%d qam=%d\n", ++ sym, qam); ++ switch (qam) { ++ case 16: ++ dataRate = 4; ++ break; ++ case 32: ++ dataRate = 5; ++ break; ++ case 128: ++ dataRate = 7; ++ break; ++ case 256: ++ dataRate = 8; ++ break; ++ case 64: ++ default: ++ dataRate = 6; ++ break; ++ } ++ dataRate *= sym * 105; ++ dataRate /= 800; ++ if (dataRate <= 4115) ++ clk_div = 0x3F; ++ else if (dataRate <= 4800) ++ clk_div = 0x36; ++ else if (dataRate <= 5760) ++ clk_div = 0x2D; ++ else if (dataRate <= 7200) ++ clk_div = 0x24; ++ else if (dataRate <= 9600) ++ clk_div = 0x1B; ++ else ++ clk_div = 0x12; ++ value = ReadReg(state, 0xC2); ++ value &= 0xc0; ++ value |= clk_div; ++ WriteReg(state, 0xC2, value); ++ return 0; ++} ++ ++static int M88DC2000SetTxMode(struct dvbsky_m88dc2800_state *state, u8 inverted, ++ u8 j83) ++{ ++ u8 value = 0; ++ if (inverted) ++ value |= 0x08; /* spectrum inverted */ ++ if (j83) ++ value |= 0x01; /* J83C */ ++ WriteReg(state, 0x83, value); ++ return 0; ++} ++ ++static int M88DC2000SoftReset(struct dvbsky_m88dc2800_state *state) ++{ ++ WriteReg(state, 0x80, 0x01); ++ WriteReg(state, 0x82, 0x00); ++ msleep(1); ++ WriteReg(state, 0x80, 0x00); ++ return 0; ++} ++ ++static int M88DC2000SetSym(struct dvbsky_m88dc2800_state *state, u32 sym, u32 xtal) ++{ ++ u8 value; ++ u8 reg6FH, reg12H; ++ u64 fValue; ++ u32 dwValue; ++ ++ printk(KERN_INFO "%s, sym=%d, xtal=%d\n", __func__, sym, xtal); ++ fValue = 4294967296 * (sym + 10); ++ do_div(fValue, xtal); ++ ++ /* fValue = 4294967296 * (sym + 10) / xtal; */ ++ dwValue = (u32) fValue; ++ printk(KERN_INFO "%s, fvalue1=%x\n", __func__, dwValue); ++ WriteReg(state, 0x58, (u8) ((dwValue >> 24) & 0xff)); ++ WriteReg(state, 0x57, (u8) ((dwValue >> 16) & 0xff)); ++ WriteReg(state, 0x56, (u8) ((dwValue >> 8) & 0xff)); ++ WriteReg(state, 0x55, (u8) ((dwValue >> 0) & 0xff)); ++ ++ /* fValue = 2048 * xtal / sym; */ ++ fValue = 2048 * xtal; ++ do_div(fValue, sym); ++ dwValue = (u32) fValue; ++ printk(KERN_INFO "%s, fvalue2=%x\n", __func__, dwValue); ++ WriteReg(state, 0x5D, (u8) ((dwValue >> 8) & 0xff)); ++ WriteReg(state, 0x5C, (u8) ((dwValue >> 0) & 0xff)); ++ value = ReadReg(state, 0x5A); ++ if (((dwValue >> 16) & 0x0001) == 0) ++ value &= 0x7F; ++ else ++ value |= 0x80; ++ WriteReg(state, 0x5A, value); ++ value = ReadReg(state, 0x89); ++ if (sym <= 1800) ++ value |= 0x01; ++ else ++ value &= 0xFE; ++ WriteReg(state, 0x89, value); ++ if (sym >= 6700) { ++ reg6FH = 0x0D; ++ reg12H = 0x30; ++ } else if (sym >= 4000) { ++ fValue = 22 * 4096 / sym; ++ reg6FH = (u8) fValue; ++ reg12H = 0x30; ++ } else if (sym >= 2000) { ++ fValue = 14 * 4096 / sym; ++ reg6FH = (u8) fValue; ++ reg12H = 0x20; ++ } else { ++ fValue = 7 * 4096 / sym; ++ reg6FH = (u8) fValue; ++ reg12H = 0x10; ++ } ++ WriteReg(state, 0x6F, reg6FH); ++ WriteReg(state, 0x12, reg12H); ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) ++ && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) { ++ if (sym < 3000) { ++ WriteReg(state, 0x6C, 0x16); ++ WriteReg(state, 0x6D, 0x10); ++ WriteReg(state, 0x6E, 0x18); ++ } else { ++ WriteReg(state, 0x6C, 0x14); ++ WriteReg(state, 0x6D, 0x0E); ++ WriteReg(state, 0x6E, 0x36); ++ } ++ } else { ++ WriteReg(state, 0x6C, 0x16); ++ WriteReg(state, 0x6D, 0x10); ++ WriteReg(state, 0x6E, 0x18); ++ } ++ return 0; ++} ++ ++static int M88DC2000SetQAM(struct dvbsky_m88dc2800_state *state, u16 qam) ++{ ++ u8 reg00H, reg4AH, regC2H, reg44H, reg4CH, reg4DH, reg74H, value; ++ u8 reg8BH, reg8EH; ++ printk(KERN_INFO "%s, qam=%d\n", __func__, qam); ++ regC2H = ReadReg(state, 0xC2); ++ regC2H &= 0xF8; ++ switch (qam) { ++ case 16: /* 16 QAM */ ++ reg00H = 0x08; ++ reg4AH = 0x0F; ++ regC2H |= 0x02; ++ reg44H = 0xAA; ++ reg4CH = 0x0C; ++ reg4DH = 0xF7; ++ reg74H = 0x0E; ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) ++ && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) { ++ reg8BH = 0x5A; ++ reg8EH = 0xBD; ++ } else { ++ reg8BH = 0x5B; ++ reg8EH = 0x9D; ++ } ++ WriteReg(state, 0x6E, 0x18); ++ break; ++ case 32: /* 32 QAM */ ++ reg00H = 0x18; ++ reg4AH = 0xFB; ++ regC2H |= 0x02; ++ reg44H = 0xAA; ++ reg4CH = 0x0C; ++ reg4DH = 0xF7; ++ reg74H = 0x0E; ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) ++ && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) { ++ reg8BH = 0x5A; ++ reg8EH = 0xBD; ++ } else { ++ reg8BH = 0x5B; ++ reg8EH = 0x9D; ++ } ++ WriteReg(state, 0x6E, 0x18); ++ break; ++ case 64: /* 64 QAM */ ++ reg00H = 0x48; ++ reg4AH = 0xCD; ++ regC2H |= 0x02; ++ reg44H = 0xAA; ++ reg4CH = 0x0C; ++ reg4DH = 0xF7; ++ reg74H = 0x0E; ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) ++ && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) { ++ reg8BH = 0x5A; ++ reg8EH = 0xBD; ++ } else { ++ reg8BH = 0x5B; ++ reg8EH = 0x9D; ++ } ++ break; ++ case 128: /* 128 QAM */ ++ reg00H = 0x28; ++ reg4AH = 0xFF; ++ regC2H |= 0x02; ++ reg44H = 0xA9; ++ reg4CH = 0x08; ++ reg4DH = 0xF5; ++ reg74H = 0x0E; ++ reg8BH = 0x5B; ++ reg8EH = 0x9D; ++ break; ++ case 256: /* 256 QAM */ ++ reg00H = 0x38; ++ reg4AH = 0xCD; ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) ++ && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) { ++ regC2H |= 0x02; ++ } else { ++ regC2H |= 0x01; ++ } ++ reg44H = 0xA9; ++ reg4CH = 0x08; ++ reg4DH = 0xF5; ++ reg74H = 0x0E; ++ reg8BH = 0x5B; ++ reg8EH = 0x9D; ++ break; ++ default: /* 64 QAM */ ++ reg00H = 0x48; ++ reg4AH = 0xCD; ++ regC2H |= 0x02; ++ reg44H = 0xAA; ++ reg4CH = 0x0C; ++ reg4DH = 0xF7; ++ reg74H = 0x0E; ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) ++ && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) { ++ reg8BH = 0x5A; ++ reg8EH = 0xBD; ++ } else { ++ reg8BH = 0x5B; ++ reg8EH = 0x9D; ++ } ++ break; ++ } ++ WriteReg(state, 0x00, reg00H); ++ value = ReadReg(state, 0x88); ++ value |= 0x08; ++ WriteReg(state, 0x88, value); ++ WriteReg(state, 0x4B, 0xFF); ++ WriteReg(state, 0x4A, reg4AH); ++ value &= 0xF7; ++ WriteReg(state, 0x88, value); ++ WriteReg(state, 0xC2, regC2H); ++ WriteReg(state, 0x44, reg44H); ++ WriteReg(state, 0x4C, reg4CH); ++ WriteReg(state, 0x4D, reg4DH); ++ WriteReg(state, 0x74, reg74H); ++ WriteReg(state, 0x8B, reg8BH); ++ WriteReg(state, 0x8E, reg8EH); ++ return 0; ++} ++ ++static int M88DC2000WriteTuner_TC2800(struct dvbsky_m88dc2800_state *state, ++ u32 freq_KHz) ++{ ++ printk(KERN_INFO "%s, freq=%d KHz\n", __func__, freq_KHz); ++ return mt_fe_tn_set_freq_tc2800(state, freq_KHz); ++} ++ ++static int dvbsky_m88dc2800_init(struct dvb_frontend *fe) ++{ ++ dprintk("%s()\n", __func__); ++ return 0; ++} ++ ++static int dvbsky_m88dc2800_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 is_annex_c, is_update; ++ u16 temp_qam; ++ s32 waiting_time; ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ ++ is_annex_c = c->delivery_system == SYS_DVBC_ANNEX_C ? 1 : 0; ++ ++ switch (c->modulation) { ++ case QAM_16: ++ temp_qam = 16; ++ break; ++ case QAM_32: ++ temp_qam = 32; ++ break; ++ case QAM_128: ++ temp_qam = 128; ++ break; ++ case QAM_256: ++ temp_qam = 256; ++ break; ++ default: /* QAM_64 */ ++ temp_qam = 64; ++ break; ++ } ++ ++ state->inverted = c->inversion == INVERSION_ON ? 1 : 0; ++ ++ printk(KERN_INFO ++ "m88dc2800: state, freq=%d qam=%d sym=%d inverted=%d xtal=%d\n", ++ state->freq, state->qam, state->sym, state->inverted, ++ state->xtal); ++ printk(KERN_INFO ++ "m88dc2800: set frequency to %d qam=%d symrate=%d annex-c=%d\n", ++ c->frequency, temp_qam, c->symbol_rate, is_annex_c); ++ ++ is_update = 0; ++ WriteReg(state, 0x80, 0x01); ++ if (c->frequency != state->freq) { ++ M88DC2000WriteTuner_TC2800(state, c->frequency / 1000); ++ state->freq = c->frequency; ++ } ++ if (c->symbol_rate != state->sym) { ++ M88DC2000SetSym(state, c->symbol_rate / 1000, state->xtal); ++ state->sym = c->symbol_rate; ++ is_update = 1; ++ } ++ if (temp_qam != state->qam) { ++ M88DC2000SetQAM(state, temp_qam); ++ state->qam = temp_qam; ++ is_update = 1; ++ } ++ ++ if (is_update != 0) { ++ if (state->config->ts_mode == 3) ++ M88DC2000AutoTSClock_C(state, state->sym / 1000, ++ temp_qam); ++ else ++ M88DC2000AutoTSClock_P(state, state->sym / 1000, ++ temp_qam); ++ } ++ ++ M88DC2000SetTxMode(state, state->inverted, is_annex_c); ++ M88DC2000SoftReset(state); ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) ++ && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) ++ waiting_time = 800; ++ else ++ waiting_time = 500; ++ while (waiting_time > 0) { ++ msleep(50); ++ waiting_time -= 50; ++ if (M88DC2000GetLock(state)) ++ return 0; ++ } ++ ++ state->inverted = (state->inverted != 0) ? 0 : 1; ++ M88DC2000SetTxMode(state, state->inverted, is_annex_c); ++ M88DC2000SoftReset(state); ++ if (((ReadReg(state, 0xE3) & 0x80) == 0x80) && ++ ((ReadReg(state, 0xE4) & 0x80) == 0x80)) ++ waiting_time = 800; ++ else ++ waiting_time = 500; ++ while (waiting_time > 0) { ++ msleep(50); ++ waiting_time -= 50; ++ if (M88DC2000GetLock(state)) ++ return 0; ++ } ++ return 0; ++} ++ ++static int dvbsky_m88dc2800_read_status(struct dvb_frontend *fe, ++ fe_status_t * status) ++{ ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ *status = 0; ++ ++ if (M88DC2000GetLock(state)) { ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER ++ |FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_LOCK; ++ } ++ return 0; ++} ++ ++static int dvbsky_m88dc2800_read_ber(struct dvb_frontend *fe, u32 * ber) ++{ ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ u16 tmp; ++ ++ if (M88DC2000GetLock(state) == 0) { ++ state->ber = 0; ++ } else if ((ReadReg(state, 0xA0) & 0x80) != 0x80) { ++ tmp = ReadReg(state, 0xA2) << 8; ++ tmp += ReadReg(state, 0xA1); ++ state->ber = tmp; ++ WriteReg(state, 0xA0, 0x05); ++ WriteReg(state, 0xA0, 0x85); ++ } ++ *ber = state->ber; ++ return 0; ++} ++ ++static int dvbsky_m88dc2800_read_signal_strength(struct dvb_frontend *fe, ++ u16 * strength) ++{ ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ s16 tuner_strength; ++ ++ tuner_strength = mt_fe_tn_get_signal_strength_tc2800(state); ++ *strength = tuner_strength < -107 ? 0 : tuner_strength + 107; ++ ++ return 0; ++} ++ ++static int dvbsky_m88dc2800_read_snr(struct dvb_frontend *fe, u16 * snr) ++{ ++ static const u32 mes_log[] = { ++ 0, 3010, 4771, 6021, 6990, 7781, 8451, 9031, 9542, 10000, ++ 10414, 10792, 11139, 11461, 11761, 12041, 12304, 12553, 12788, ++ 13010, 13222, 13424, 13617, 13802, 13979, 14150, 14314, 14472, ++ 14624, 14771, 14914, 15052, 15185, 15315, 15441, 15563, 15682, ++ 15798, 15911, 16021, 16128, 16232, 16335, 16435, 16532, 16628, ++ 16721, 16812, 16902, 16990, 17076, 17160, 17243, 17324, 17404, ++ 17482, 17559, 17634, 17709, 17782, 17853, 17924, 17993, 18062, ++ 18129, 18195, 18261, 18325, 18388, 18451, 18513, 18573, 18633, ++ 18692, 18751, 18808, 18865, 18921, 18976, 19031 ++ }; ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ u8 i; ++ u32 _snr, mse; ++ ++ if ((ReadReg(state, 0x91) & 0x23) != 0x03) { ++ *snr = 0; ++ return 0; ++ } ++ mse = 0; ++ for (i = 0; i < 30; i++) { ++ mse += (ReadReg(state, 0x08) << 8) + ReadReg(state, 0x07); ++ } ++ mse /= 30; ++ if (mse > 80) ++ mse = 80; ++ switch (state->qam) { ++ case 16: ++ _snr = 34080; ++ break; /* 16QAM */ ++ case 32: ++ _snr = 37600; ++ break; /* 32QAM */ ++ case 64: ++ _snr = 40310; ++ break; /* 64QAM */ ++ case 128: ++ _snr = 43720; ++ break; /* 128QAM */ ++ case 256: ++ _snr = 46390; ++ break; /* 256QAM */ ++ default: ++ _snr = 40310; ++ break; ++ } ++ _snr -= mes_log[mse - 1]; /* C - 10*log10(MSE) */ ++ _snr /= 1000; ++ if (_snr > 0xff) ++ _snr = 0xff; ++ *snr = _snr; ++ return 0; ++} ++ ++static int dvbsky_m88dc2800_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) ++{ ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ u8 u8Value; ++ ++ u8Value = ReadReg(state, 0xdf); ++ u8Value |= 0x02; /* Hold */ ++ WriteReg(state, 0xdf, u8Value); ++ ++ *ucblocks = ReadReg(state, 0xd5); ++ *ucblocks = (*ucblocks << 8) | ReadReg(state, 0xd4); ++ ++ u8Value &= 0xfe; /* Clear */ ++ WriteReg(state, 0xdf, u8Value); ++ u8Value &= 0xfc; /* Update */ ++ u8Value |= 0x01; ++ WriteReg(state, 0xdf, u8Value); ++ ++ return 0; ++} ++ ++static int dvbsky_m88dc2800_sleep(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ ++ mt_fe_tn_sleep_tc2800(state); ++ state->freq = 0; ++ ++ return 0; ++} ++ ++static void dvbsky_m88dc2800_release(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88dc2800_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops dvbsky_m88dc2800_ops; ++ ++struct dvb_frontend *dvbsky_m88dc2800_attach(const struct dvbsky_m88dc2800_config ++ *config, struct i2c_adapter *i2c) ++{ ++ struct dvbsky_m88dc2800_state *state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct dvbsky_m88dc2800_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->xtal = 28800; ++ ++ WriteReg(state, 0x80, 0x01); ++ M88DC2000RegInitial_TC2800(state); ++ M88DC2000SetTsType(state, state->config->ts_mode); ++ mt_fe_tn_init_tc2800(state); ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &dvbsky_m88dc2800_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++ error: ++ kfree(state); ++ return NULL; ++} ++ ++EXPORT_SYMBOL(dvbsky_m88dc2800_attach); ++ ++static struct dvb_frontend_ops dvbsky_m88dc2800_ops = { ++ .delsys = {SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C}, ++ .info = { ++ .name = "Montage M88DC2800 DVB-C", ++ .frequency_stepsize = 62500, ++ .frequency_min = 48000000, ++ .frequency_max = 870000000, ++ .symbol_rate_min = 870000, ++ .symbol_rate_max = 9000000, ++ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO ++ }, ++ .release = dvbsky_m88dc2800_release, ++ .init = dvbsky_m88dc2800_init, ++ .sleep = dvbsky_m88dc2800_sleep, ++ .set_frontend = dvbsky_m88dc2800_set_parameters, ++ .read_status = dvbsky_m88dc2800_read_status, ++ .read_ber = dvbsky_m88dc2800_read_ber, ++ .read_signal_strength = dvbsky_m88dc2800_read_signal_strength, ++ .read_snr = dvbsky_m88dc2800_read_snr, ++ .read_ucblocks = dvbsky_m88dc2800_read_ucblocks, ++}; ++ ++MODULE_DESCRIPTION("Montage DVB-C demodulator driver"); ++MODULE_AUTHOR("Max Nibble "); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.00"); +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88dc2800.h linux-openelec/drivers/media/dvb-frontends/dvbsky_m88dc2800.h +--- linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88dc2800.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/dvbsky_m88dc2800.h 2015-07-24 18:03:30.116842002 -0500 +@@ -0,0 +1,44 @@ ++/* ++ M88DC2800/M88TC2800 - DVB-C demodulator and tuner from Montage ++ ++ Copyright (C) 2012 Max Nibble ++ Copyright (C) 2011 Montage Technology - www.montage-tech.com ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef dvbsky_m88dc2800_H ++#define dvbsky_m88dc2800_H ++ ++#include ++#include ++ ++struct dvbsky_m88dc2800_config { ++ u8 demod_address; ++ u8 ts_mode; ++}; ++ ++#if IS_ENABLED(CONFIG_DVB_DVBSKY_M88DC2800) ++extern struct dvb_frontend* dvbsky_m88dc2800_attach(const struct dvbsky_m88dc2800_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* dvbsky_m88dc2800_attach(const struct dvbsky_m88dc2800_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_DVBSKY_M88DC2800 */ ++#endif /* dvbsky_m88dc2800_H */ +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88ds3103.c linux-openelec/drivers/media/dvb-frontends/dvbsky_m88ds3103.c +--- linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88ds3103.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/dvbsky_m88ds3103.c 2015-07-24 18:03:30.120842002 -0500 +@@ -0,0 +1,1707 @@ ++/* ++ Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner driver ++ ++ Copyright (C) 2011 Max nibble ++ Copyright (C) 2010 Montage Technology ++ Copyright (C) 2009 Konstantin Dimitrov. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "dvbsky_m88ds3103.h" ++#include "dvbsky_m88ds3103_priv.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_INFO "m88ds3103: " args); \ ++ } while (0) ++ ++/*demod register operations.*/ ++static int dvbsky_m88ds3103_writereg(struct dvbsky_m88ds3103_state *state, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, ++ .flags = 0, .buf = buf, .len = 2 }; ++ int err; ++ ++ if (debug > 1) ++ printk("m88ds3103: %s: write reg 0x%02x, value 0x%02x\n", ++ __func__, reg, data); ++ ++ err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," ++ " value == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_readreg(struct dvbsky_m88ds3103_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { .addr = state->config->demod_address, .flags = 0, ++ .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, ++ .buf = b1, .len = 1 } ++ }; ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_ERR "%s: reg=0x%x (error=%d)\n", ++ __func__, reg, ret); ++ return ret; ++ } ++ ++ if (debug > 1) ++ printk(KERN_INFO "m88ds3103: read reg 0x%02x, value 0x%02x\n", ++ reg, b1[0]); ++ ++ return b1[0]; ++} ++ ++/*tuner register operations.*/ ++static int dvbsky_m88ds3103_tuner_writereg(struct dvbsky_m88ds3103_state *state, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = 0x60, ++ .flags = 0, .buf = buf, .len = 2 }; ++ int err; ++ ++ dvbsky_m88ds3103_writereg(state, 0x03, 0x11); ++ err = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (err != 1) { ++ printk("%s: writereg error(err == %i, reg == 0x%02x," ++ " value == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_tuner_readreg(struct dvbsky_m88ds3103_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { .addr = 0x60, .flags = 0, ++ .buf = b0, .len = 1 }, ++ { .addr = 0x60, .flags = I2C_M_RD, ++ .buf = b1, .len = 1 } ++ }; ++ ++ dvbsky_m88ds3103_writereg(state, 0x03, 0x11); ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); ++ return ret; ++ } ++ ++ return b1[0]; ++} ++ ++/* Bulk demod I2C write, for firmware download. */ ++static int dvbsky_m88ds3103_writeregN(struct dvbsky_m88ds3103_state *state, int reg, ++ const u8 *data, u16 len) ++{ ++ int ret = -EREMOTEIO; ++ struct i2c_msg msg; ++ u8 *buf; ++ ++ buf = kmalloc(len + 1, GFP_KERNEL); ++ if (buf == NULL) { ++ printk("Unable to kmalloc\n"); ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ *(buf) = reg; ++ memcpy(buf + 1, data, len); ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.buf = buf; ++ msg.len = len + 1; ++ ++ if (debug > 1) ++ printk(KERN_INFO "m88ds3103: %s: write regN 0x%02x, len = %d\n", ++ __func__, reg, len); ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) { ++ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n", ++ __func__, ret, reg); ++ ret = -EREMOTEIO; ++ } ++ ++error: ++ kfree(buf); ++ ++ return ret; ++} ++ ++static int dvbsky_m88ds3103_load_firmware(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ const struct firmware *fw; ++ int i, ret = 0; ++ ++ dprintk("%s()\n", __func__); ++ ++ if (state->skip_fw_load) ++ return 0; ++ /* Load firmware */ ++ /* request the firmware, this will block until someone uploads it */ ++ if(state->demod_id == DS3000_ID){ ++ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__, ++ DS3000_DEFAULT_FIRMWARE); ++ ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE, ++ state->i2c->dev.parent); ++ }else if(state->demod_id == DS3103_ID){ ++ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__, ++ DS3103_DEFAULT_FIRMWARE); ++ ret = request_firmware(&fw, DS3103_DEFAULT_FIRMWARE, ++ state->i2c->dev.parent); ++ } ++ ++ printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__); ++ if (ret) { ++ printk(KERN_ERR "%s: No firmware uploaded (timeout or file not " ++ "found?)\n", __func__); ++ return ret; ++ } ++ ++ /* Make sure we don't recurse back through here during loading */ ++ state->skip_fw_load = 1; ++ ++ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", ++ fw->size, ++ fw->data[0], ++ fw->data[1], ++ fw->data[fw->size - 2], ++ fw->data[fw->size - 1]); ++ ++ /* stop internal mcu. */ ++ dvbsky_m88ds3103_writereg(state, 0xb2, 0x01); ++ /* split firmware to download.*/ ++ for(i = 0; i < FW_DOWN_LOOP; i++){ ++ ret = dvbsky_m88ds3103_writeregN(state, 0xb0, &(fw->data[FW_DOWN_SIZE*i]), FW_DOWN_SIZE); ++ if(ret != 1) break; ++ } ++ /* start internal mcu. */ ++ if(ret == 1) ++ dvbsky_m88ds3103_writereg(state, 0xb2, 0x00); ++ ++ release_firmware(fw); ++ ++ dprintk("%s: Firmware upload %s\n", __func__, ++ ret == 1 ? "complete" : "failed"); ++ ++ if(ret == 1) ret = 0; ++ ++ /* Ensure firmware is always loaded if required */ ++ state->skip_fw_load = 0; ++ ++ return ret; ++} ++ ++ ++static int dvbsky_m88ds3103_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 data; ++ ++ dprintk("%s(%d)\n", __func__, voltage); ++ ++ dprintk("m88ds3103:pin_ctrl = (%02x)\n", state->config->pin_ctrl); ++ ++ if(state->config->set_voltage) ++ state->config->set_voltage(fe, voltage); ++ ++ data = dvbsky_m88ds3103_readreg(state, 0xa2); ++ ++ if(state->config->pin_ctrl & 0x80){ /*If control pin is assigned.*/ ++ data &= ~0x03; /* bit0 V/H, bit1 off/on */ ++ if(state->config->pin_ctrl & 0x02) ++ data |= 0x02; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_18: ++ if((state->config->pin_ctrl & 0x01) == 0) ++ data |= 0x01; ++ break; ++ case SEC_VOLTAGE_13: ++ if(state->config->pin_ctrl & 0x01) ++ data |= 0x01; ++ break; ++ case SEC_VOLTAGE_OFF: ++ if(state->config->pin_ctrl & 0x02) ++ data &= ~0x02; ++ else ++ data |= 0x02; ++ break; ++ } ++ } ++ ++ dvbsky_m88ds3103_writereg(state, 0xa2, data); ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t* status) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ int lock = 0; ++ ++ *status = 0; ++ ++ switch (state->delivery_system){ ++ case SYS_DVBS: ++ lock = dvbsky_m88ds3103_readreg(state, 0xd1); ++ dprintk("%s: SYS_DVBS status=%x.\n", __func__, lock); ++ ++ if ((lock & 0x07) == 0x07){ ++ /*if((dvbsky_m88ds3103_readreg(state, 0x0d) & 0x07) == 0x07)*/ ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER ++ | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ ++ } ++ break; ++ case SYS_DVBS2: ++ lock = dvbsky_m88ds3103_readreg(state, 0x0d); ++ dprintk("%s: SYS_DVBS2 status=%x.\n", __func__, lock); ++ ++ if ((lock & 0x8f) == 0x8f) ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER ++ | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_read_ber(struct dvb_frontend *fe, u32* ber) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 tmp1, tmp2, tmp3; ++ u32 ldpc_frame_cnt, pre_err_packags, code_rate_fac = 0; ++ ++ dprintk("%s()\n", __func__); ++ ++ switch (state->delivery_system) { ++ case SYS_DVBS: ++ dvbsky_m88ds3103_writereg(state, 0xf9, 0x04); ++ tmp3 = dvbsky_m88ds3103_readreg(state, 0xf8); ++ if ((tmp3&0x10) == 0){ ++ tmp1 = dvbsky_m88ds3103_readreg(state, 0xf7); ++ tmp2 = dvbsky_m88ds3103_readreg(state, 0xf6); ++ tmp3 |= 0x10; ++ dvbsky_m88ds3103_writereg(state, 0xf8, tmp3); ++ state->preBer = (tmp1<<8) | tmp2; ++ } ++ break; ++ case SYS_DVBS2: ++ tmp1 = dvbsky_m88ds3103_readreg(state, 0x7e) & 0x0f; ++ switch(tmp1){ ++ case 0: code_rate_fac = 16008 - 80; break; ++ case 1: code_rate_fac = 21408 - 80; break; ++ case 2: code_rate_fac = 25728 - 80; break; ++ case 3: code_rate_fac = 32208 - 80; break; ++ case 4: code_rate_fac = 38688 - 80; break; ++ case 5: code_rate_fac = 43040 - 80; break; ++ case 6: code_rate_fac = 48408 - 80; break; ++ case 7: code_rate_fac = 51648 - 80; break; ++ case 8: code_rate_fac = 53840 - 80; break; ++ case 9: code_rate_fac = 57472 - 80; break; ++ case 10: code_rate_fac = 58192 - 80; break; ++ } ++ ++ tmp1 = dvbsky_m88ds3103_readreg(state, 0xd7) & 0xff; ++ tmp2 = dvbsky_m88ds3103_readreg(state, 0xd6) & 0xff; ++ tmp3 = dvbsky_m88ds3103_readreg(state, 0xd5) & 0xff; ++ ldpc_frame_cnt = (tmp1 << 16) | (tmp2 << 8) | tmp3; ++ ++ tmp1 = dvbsky_m88ds3103_readreg(state, 0xf8) & 0xff; ++ tmp2 = dvbsky_m88ds3103_readreg(state, 0xf7) & 0xff; ++ pre_err_packags = tmp1<<8 | tmp2; ++ ++ if (ldpc_frame_cnt > 1000){ ++ dvbsky_m88ds3103_writereg(state, 0xd1, 0x01); ++ dvbsky_m88ds3103_writereg(state, 0xf9, 0x01); ++ dvbsky_m88ds3103_writereg(state, 0xf9, 0x00); ++ dvbsky_m88ds3103_writereg(state, 0xd1, 0x00); ++ state->preBer = pre_err_packags; ++ } ++ break; ++ default: ++ break; ++ } ++ *ber = state->preBer; ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u16 gain; ++ u8 gain1, gain2, gain3 = 0; ++ ++ dprintk("%s()\n", __func__); ++ ++ gain1 = dvbsky_m88ds3103_tuner_readreg(state, 0x3d) & 0x1f; ++ dprintk("%s: gain1 = 0x%02x \n", __func__, gain1); ++ ++ if (gain1 > 15) gain1 = 15; ++ gain2 = dvbsky_m88ds3103_tuner_readreg(state, 0x21) & 0x1f; ++ dprintk("%s: gain2 = 0x%02x \n", __func__, gain2); ++ ++ if(state->tuner_id == TS2022_ID){ ++ gain3 = (dvbsky_m88ds3103_tuner_readreg(state, 0x66)>>3) & 0x07; ++ dprintk("%s: gain3 = 0x%02x \n", __func__, gain3); ++ ++ if (gain2 > 16) gain2 = 16; ++ if (gain2 < 2) gain2 = 2; ++ if (gain3 > 6) gain3 = 6; ++ }else{ ++ if (gain2 > 13) gain2 = 13; ++ gain3 = 0; ++ } ++ ++ gain = gain1*23 + gain2*35 + gain3*29; ++ *signal_strength = 60000 - gain*55; ++ ++ return 0; ++} ++ ++ ++static int dvbsky_m88ds3103_read_snr(struct dvb_frontend *fe, u16 *p_snr) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 val, npow1, npow2, spow1, cnt; ++ u16 tmp, snr; ++ u32 npow, spow, snr_total; ++ static const u16 mes_log10[] ={ ++ 0, 3010, 4771, 6021, 6990, 7781, 8451, 9031, 9542, 10000, ++ 10414, 10792, 11139, 11461, 11761, 12041, 12304, 12553, 12788, 13010, ++ 13222, 13424, 13617, 13802, 13979, 14150, 14314, 14472, 14624, 14771, ++ 14914, 15052, 15185, 15315, 15441, 15563, 15682, 15798, 15911, 16021, ++ 16128, 16232, 16335, 16435, 16532, 16628, 16721, 16812, 16902, 16990, ++ 17076, 17160, 17243, 17324, 17404, 17482, 17559, 17634, 17709, 17782, ++ 17853, 17924, 17993, 18062, 18129, 18195, 18261, 18325, 18388, 18451, ++ 18513, 18573, 18633, 18692, 18751, 18808, 18865, 18921, 18976, 19031 ++ }; ++ static const u16 mes_loge[] ={ ++ 0, 6931, 10986, 13863, 16094, 17918, 19459, 20794, 21972, 23026, ++ 23979, 24849, 25649, 26391, 27081, 27726, 28332, 28904, 29444, 29957, ++ 30445, 30910, 31355, 31781, 32189, 32581, 32958, 33322, 33673, 34012, ++ 34340, 34657, ++ }; ++ ++ dprintk("%s()\n", __func__); ++ ++ snr = 0; ++ ++ switch (state->delivery_system){ ++ case SYS_DVBS: ++ cnt = 10; snr_total = 0; ++ while(cnt > 0){ ++ val = dvbsky_m88ds3103_readreg(state, 0xff); ++ snr_total += val; ++ cnt--; ++ } ++ tmp = (u16)(snr_total/80); ++ if(tmp > 0){ ++ if (tmp > 32) tmp = 32; ++ snr = (mes_loge[tmp - 1] * 100) / 45; ++ }else{ ++ snr = 0; ++ } ++ break; ++ case SYS_DVBS2: ++ cnt = 10; npow = 0; spow = 0; ++ while(cnt >0){ ++ npow1 = dvbsky_m88ds3103_readreg(state, 0x8c) & 0xff; ++ npow2 = dvbsky_m88ds3103_readreg(state, 0x8d) & 0xff; ++ npow += (((npow1 & 0x3f) + (u16)(npow2 << 6)) >> 2); ++ ++ spow1 = dvbsky_m88ds3103_readreg(state, 0x8e) & 0xff; ++ spow += ((spow1 * spow1) >> 1); ++ cnt--; ++ } ++ npow /= 10; spow /= 10; ++ if(spow == 0){ ++ snr = 0; ++ }else if(npow == 0){ ++ snr = 19; ++ }else{ ++ if(spow > npow){ ++ tmp = (u16)(spow / npow); ++ if (tmp > 80) tmp = 80; ++ snr = mes_log10[tmp - 1]*3; ++ }else{ ++ tmp = (u16)(npow / spow); ++ if (tmp > 80) tmp = 80; ++ snr = -(mes_log10[tmp - 1] / 1000); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ *p_snr = snr; ++ ++ return 0; ++} ++ ++ ++static int dvbsky_m88ds3103_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 tmp1, tmp2, tmp3, data; ++ ++ dprintk("%s()\n", __func__); ++ ++ switch (state->delivery_system) { ++ case SYS_DVBS: ++ data = dvbsky_m88ds3103_readreg(state, 0xf8); ++ data |= 0x40; ++ dvbsky_m88ds3103_writereg(state, 0xf8, data); ++ tmp1 = dvbsky_m88ds3103_readreg(state, 0xf5); ++ tmp2 = dvbsky_m88ds3103_readreg(state, 0xf4); ++ *ucblocks = (tmp1 <<8) | tmp2; ++ data &= ~0x20; ++ dvbsky_m88ds3103_writereg(state, 0xf8, data); ++ data |= 0x20; ++ dvbsky_m88ds3103_writereg(state, 0xf8, data); ++ data &= ~0x40; ++ dvbsky_m88ds3103_writereg(state, 0xf8, data); ++ break; ++ case SYS_DVBS2: ++ tmp1 = dvbsky_m88ds3103_readreg(state, 0xda); ++ tmp2 = dvbsky_m88ds3103_readreg(state, 0xd9); ++ tmp3 = dvbsky_m88ds3103_readreg(state, 0xd8); ++ *ucblocks = (tmp1 <<16)|(tmp2 <<8)|tmp3; ++ data = dvbsky_m88ds3103_readreg(state, 0xd1); ++ data |= 0x01; ++ dvbsky_m88ds3103_writereg(state, 0xd1, data); ++ data &= ~0x01; ++ dvbsky_m88ds3103_writereg(state, 0xd1, data); ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 data_a1, data_a2; ++ ++ dprintk("%s(%d)\n", __func__, tone); ++ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { ++ printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); ++ return -EINVAL; ++ } ++ ++ data_a1 = dvbsky_m88ds3103_readreg(state, 0xa1); ++ data_a2 = dvbsky_m88ds3103_readreg(state, 0xa2); ++ if(state->demod_id == DS3103_ID) ++ data_a2 &= 0xdf; /* Normal mode */ ++ switch (tone) { ++ case SEC_TONE_ON: ++ dprintk("%s: SEC_TONE_ON\n", __func__); ++ data_a1 |= 0x04; ++ data_a1 &= ~0x03; ++ data_a1 &= ~0x40; ++ data_a2 &= ~0xc0; ++ break; ++ case SEC_TONE_OFF: ++ dprintk("%s: SEC_TONE_OFF\n", __func__); ++ data_a2 &= ~0xc0; ++ data_a2 |= 0x80; ++ break; ++ } ++ dvbsky_m88ds3103_writereg(state, 0xa2, data_a2); ++ dvbsky_m88ds3103_writereg(state, 0xa1, data_a1); ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *d) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ int i, ret = 0; ++ u8 tmp, time_out; ++ ++ /* Dump DiSEqC message */ ++ if (debug) { ++ printk(KERN_INFO "m88ds3103: %s(", __func__); ++ for (i = 0 ; i < d->msg_len ;) { ++ printk(KERN_INFO "0x%02x", d->msg[i]); ++ if (++i < d->msg_len) ++ printk(KERN_INFO ", "); ++ } ++ } ++ ++ tmp = dvbsky_m88ds3103_readreg(state, 0xa2); ++ tmp &= ~0xc0; ++ if(state->demod_id == DS3103_ID) ++ tmp &= ~0x20; ++ dvbsky_m88ds3103_writereg(state, 0xa2, tmp); ++ ++ for (i = 0; i < d->msg_len; i ++) ++ dvbsky_m88ds3103_writereg(state, (0xa3+i), d->msg[i]); ++ ++ tmp = dvbsky_m88ds3103_readreg(state, 0xa1); ++ tmp &= ~0x38; ++ tmp &= ~0x40; ++ tmp |= ((d->msg_len-1) << 3) | 0x07; ++ tmp &= ~0x80; ++ dvbsky_m88ds3103_writereg(state, 0xa1, tmp); ++ /* 1.5 * 9 * 8 = 108ms */ ++ time_out = 150; ++ while (time_out > 0){ ++ msleep(10); ++ time_out -= 10; ++ tmp = dvbsky_m88ds3103_readreg(state, 0xa1); ++ if ((tmp & 0x40) == 0) ++ break; ++ } ++ if (time_out == 0){ ++ tmp = dvbsky_m88ds3103_readreg(state, 0xa1); ++ tmp &= ~0x80; ++ tmp |= 0x40; ++ dvbsky_m88ds3103_writereg(state, 0xa1, tmp); ++ ret = 1; ++ } ++ tmp = dvbsky_m88ds3103_readreg(state, 0xa2); ++ tmp &= ~0xc0; ++ tmp |= 0x80; ++ dvbsky_m88ds3103_writereg(state, 0xa2, tmp); ++ return ret; ++} ++ ++ ++static int dvbsky_m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t burst) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 val, time_out; ++ ++ dprintk("%s()\n", __func__); ++ ++ val = dvbsky_m88ds3103_readreg(state, 0xa2); ++ val &= ~0xc0; ++ if(state->demod_id == DS3103_ID) ++ val &= 0xdf; /* Normal mode */ ++ dvbsky_m88ds3103_writereg(state, 0xa2, val); ++ /* DiSEqC burst */ ++ if (burst == SEC_MINI_B) ++ dvbsky_m88ds3103_writereg(state, 0xa1, 0x01); ++ else ++ dvbsky_m88ds3103_writereg(state, 0xa1, 0x02); ++ ++ msleep(13); ++ ++ time_out = 5; ++ do{ ++ val = dvbsky_m88ds3103_readreg(state, 0xa1); ++ if ((val & 0x40) == 0) ++ break; ++ msleep(1); ++ time_out --; ++ } while (time_out > 0); ++ ++ val = dvbsky_m88ds3103_readreg(state, 0xa2); ++ val &= ~0xc0; ++ val |= 0x80; ++ dvbsky_m88ds3103_writereg(state, 0xa2, val); ++ ++ return 0; ++} ++ ++static void dvbsky_m88ds3103_release(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ kfree(state); ++} ++ ++static int dvbsky_m88ds3103_check_id(struct dvbsky_m88ds3103_state *state) ++{ ++ int val_00, val_01; ++ ++ /*check demod id*/ ++ val_01 = dvbsky_m88ds3103_readreg(state, 0x01); ++ printk(KERN_INFO "DS3000 chip version: %x attached.\n", val_01); ++ ++ if(val_01 == 0xD0) ++ state->demod_id = DS3103_ID; ++ else if(val_01 == 0xC0) ++ state->demod_id = DS3000_ID; ++ else ++ state->demod_id = UNKNOW_ID; ++ ++ /*check tuner id*/ ++ val_00 = dvbsky_m88ds3103_tuner_readreg(state, 0x00); ++ printk(KERN_INFO "TS202x chip version[1]: %x attached.\n", val_00); ++ val_00 &= 0x03; ++ if(val_00 == 0) ++ { ++ dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x01); ++ msleep(3); ++ } ++ dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x03); ++ msleep(5); ++ ++ val_00 = dvbsky_m88ds3103_tuner_readreg(state, 0x00); ++ printk(KERN_INFO "TS202x chip version[2]: %x attached.\n", val_00); ++ val_00 &= 0xff; ++ if((val_00 == 0x01) || (val_00 == 0x41) || (val_00 == 0x81)) ++ state->tuner_id = TS2020_ID; ++ else if(((val_00 & 0xc0)== 0xc0) || (val_00 == 0x83)) ++ state->tuner_id = TS2022_ID; ++ else ++ state->tuner_id = UNKNOW_ID; ++ ++ return state->demod_id; ++} ++ ++static struct dvb_frontend_ops dvbsky_m88ds3103_ops; ++static int dvbsky_m88ds3103_initilaze(struct dvb_frontend *fe); ++ ++struct dvb_frontend *dvbsky_m88ds3103_attach(const struct dvbsky_m88ds3103_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct dvbsky_m88ds3103_state *state = NULL; ++ ++ dprintk("%s\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct dvbsky_m88ds3103_state), GFP_KERNEL); ++ if (state == NULL) { ++ printk(KERN_ERR "Unable to kmalloc\n"); ++ goto error2; ++ } ++ ++ state->config = config; ++ state->i2c = i2c; ++ state->preBer = 0xffff; ++ state->delivery_system = SYS_DVBS; /*Default to DVB-S.*/ ++ ++ /* check demod id */ ++ if(dvbsky_m88ds3103_check_id(state) == UNKNOW_ID){ ++ printk(KERN_ERR "Unable to find Montage chip\n"); ++ goto error3; ++ } ++ ++ memcpy(&state->frontend.ops, &dvbsky_m88ds3103_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ dvbsky_m88ds3103_initilaze(&state->frontend); ++ ++ return &state->frontend; ++ ++error3: ++ kfree(state); ++error2: ++ return NULL; ++} ++EXPORT_SYMBOL(dvbsky_m88ds3103_attach); ++ ++static int dvbsky_m88ds3103_set_carrier_offset(struct dvb_frontend *fe, ++ s32 carrier_offset_khz) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ s32 tmp; ++ ++ tmp = carrier_offset_khz; ++ tmp *= 65536; ++ ++ tmp = (2*tmp + MT_FE_MCLK_KHZ) / (2*MT_FE_MCLK_KHZ); ++ ++ if (tmp < 0) ++ tmp += 65536; ++ ++ dvbsky_m88ds3103_writereg(state, 0x5f, tmp >> 8); ++ dvbsky_m88ds3103_writereg(state, 0x5e, tmp & 0xff); ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_set_symrate(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u16 value; ++ ++ value = (((c->symbol_rate / 1000) << 15) + (MT_FE_MCLK_KHZ / 4)) / (MT_FE_MCLK_KHZ / 2); ++ dvbsky_m88ds3103_writereg(state, 0x61, value & 0x00ff); ++ dvbsky_m88ds3103_writereg(state, 0x62, (value & 0xff00) >> 8); ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_set_CCI(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 tmp; ++ ++ tmp = dvbsky_m88ds3103_readreg(state, 0x56); ++ tmp &= ~0x01; ++ dvbsky_m88ds3103_writereg(state, 0x56, tmp); ++ ++ tmp = dvbsky_m88ds3103_readreg(state, 0x76); ++ tmp &= ~0x80; ++ dvbsky_m88ds3103_writereg(state, 0x76, tmp); ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_init_reg(struct dvbsky_m88ds3103_state *state, const u8 *p_reg_tab, u32 size) ++{ ++ u32 i; ++ ++ for(i = 0; i < size; i+=2) ++ dvbsky_m88ds3103_writereg(state, p_reg_tab[i], p_reg_tab[i+1]); ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_get_locked_sym_rate(struct dvbsky_m88ds3103_state *state, u32 *sym_rate_KSs) ++{ ++ u16 tmp; ++ u32 sym_rate_tmp; ++ u8 val_0x6d, val_0x6e; ++ ++ val_0x6d = dvbsky_m88ds3103_readreg(state, 0x6d); ++ val_0x6e = dvbsky_m88ds3103_readreg(state, 0x6e); ++ ++ tmp = (u16)((val_0x6e<<8) | val_0x6d); ++ ++ sym_rate_tmp = (u32)(tmp * MT_FE_MCLK_KHZ); ++ sym_rate_tmp = (u32)(sym_rate_tmp / (1<<16)); ++ *sym_rate_KSs = sym_rate_tmp; ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_get_channel_info(struct dvbsky_m88ds3103_state *state, u8 *p_mode, u8 *p_coderate) ++{ ++ u8 tmp, val_0x7E; ++ ++ if(state->delivery_system == SYS_DVBS2){ ++ val_0x7E = dvbsky_m88ds3103_readreg(state, 0x7e); ++ tmp = (u8)((val_0x7E&0xC0) >> 6); ++ *p_mode = tmp; ++ tmp = (u8)(val_0x7E & 0x0f); ++ *p_coderate = tmp; ++ } else { ++ *p_mode = 0; ++ tmp = dvbsky_m88ds3103_readreg(state, 0xe6); ++ tmp = (u8)(tmp >> 5); ++ *p_coderate = tmp; ++ } ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_set_clock_ratio(struct dvbsky_m88ds3103_state *state) ++{ ++ u8 val, mod_fac, tmp1, tmp2; ++ u32 input_datarate, locked_sym_rate_KSs; ++ u32 MClk_KHz = 96000; ++ u8 mod_mode, code_rate, divid_ratio = 0; ++ ++ locked_sym_rate_KSs = 0; ++ dvbsky_m88ds3103_get_locked_sym_rate(state, &locked_sym_rate_KSs); ++ if(locked_sym_rate_KSs == 0) ++ return 0; ++ ++ dvbsky_m88ds3103_get_channel_info(state, &mod_mode, &code_rate); ++ ++ if (state->delivery_system == SYS_DVBS2) ++ { ++ switch(mod_mode) { ++ case 1: mod_fac = 3; break; ++ case 2: mod_fac = 4; break; ++ case 3: mod_fac = 5; break; ++ default: mod_fac = 2; break; ++ } ++ ++ switch(code_rate) { ++ case 0: input_datarate = locked_sym_rate_KSs*mod_fac/8/4; break; ++ case 1: input_datarate = locked_sym_rate_KSs*mod_fac/8/3; break; ++ case 2: input_datarate = locked_sym_rate_KSs*mod_fac*2/8/5; break; ++ case 3: input_datarate = locked_sym_rate_KSs*mod_fac/8/2; break; ++ case 4: input_datarate = locked_sym_rate_KSs*mod_fac*3/8/5; break; ++ case 5: input_datarate = locked_sym_rate_KSs*mod_fac*2/8/3; break; ++ case 6: input_datarate = locked_sym_rate_KSs*mod_fac*3/8/4; break; ++ case 7: input_datarate = locked_sym_rate_KSs*mod_fac*4/8/5; break; ++ case 8: input_datarate = locked_sym_rate_KSs*mod_fac*5/8/6; break; ++ case 9: input_datarate = locked_sym_rate_KSs*mod_fac*8/8/9; break; ++ case 10: input_datarate = locked_sym_rate_KSs*mod_fac*9/8/10; break; ++ default: input_datarate = locked_sym_rate_KSs*mod_fac*2/8/3; break; ++ } ++ ++ if(state->demod_id == DS3000_ID) ++ input_datarate = input_datarate * 115 / 100; ++ ++ if(input_datarate < 4800) {tmp1 = 15;tmp2 = 15;} //4.8MHz TS clock ++ else if(input_datarate < 4966) {tmp1 = 14;tmp2 = 15;} //4.966MHz TS clock ++ else if(input_datarate < 5143) {tmp1 = 14;tmp2 = 14;} //5.143MHz TS clock ++ else if(input_datarate < 5333) {tmp1 = 13;tmp2 = 14;} //5.333MHz TS clock ++ else if(input_datarate < 5538) {tmp1 = 13;tmp2 = 13;} //5.538MHz TS clock ++ else if(input_datarate < 5760) {tmp1 = 12;tmp2 = 13;} //5.76MHz TS clock allan 0809 ++ else if(input_datarate < 6000) {tmp1 = 12;tmp2 = 12;} //6MHz TS clock ++ else if(input_datarate < 6260) {tmp1 = 11;tmp2 = 12;} //6.26MHz TS clock ++ else if(input_datarate < 6545) {tmp1 = 11;tmp2 = 11;} //6.545MHz TS clock ++ else if(input_datarate < 6857) {tmp1 = 10;tmp2 = 11;} //6.857MHz TS clock ++ else if(input_datarate < 7200) {tmp1 = 10;tmp2 = 10;} //7.2MHz TS clock ++ else if(input_datarate < 7578) {tmp1 = 9;tmp2 = 10;} //7.578MHz TS clock ++ else if(input_datarate < 8000) {tmp1 = 9;tmp2 = 9;} //8MHz TS clock ++ else if(input_datarate < 8470) {tmp1 = 8;tmp2 = 9;} //8.47MHz TS clock ++ else if(input_datarate < 9000) {tmp1 = 8;tmp2 = 8;} //9MHz TS clock ++ else if(input_datarate < 9600) {tmp1 = 7;tmp2 = 8;} //9.6MHz TS clock ++ else if(input_datarate < 10285) {tmp1 = 7;tmp2 = 7;} //10.285MHz TS clock ++ else if(input_datarate < 12000) {tmp1 = 6;tmp2 = 6;} //12MHz TS clock ++ else if(input_datarate < 14400) {tmp1 = 5;tmp2 = 5;} //14.4MHz TS clock ++ else if(input_datarate < 18000) {tmp1 = 4;tmp2 = 4;} //18MHz TS clock ++ else {tmp1 = 3;tmp2 = 3;} //24MHz TS clock ++ ++ if(state->demod_id == DS3000_ID) { ++ val = (u8)((tmp1<<4) + tmp2); ++ dvbsky_m88ds3103_writereg(state, 0xfe, val); ++ } else { ++ tmp1 = dvbsky_m88ds3103_readreg(state, 0x22); ++ tmp2 = dvbsky_m88ds3103_readreg(state, 0x24); ++ ++ tmp1 >>= 6; ++ tmp1 &= 0x03; ++ tmp2 >>= 6; ++ tmp2 &= 0x03; ++ ++ if((tmp1 == 0x00) && (tmp2 == 0x01)) ++ MClk_KHz = 144000; ++ else if((tmp1 == 0x00) && (tmp2 == 0x03)) ++ MClk_KHz = 72000; ++ else if((tmp1 == 0x01) && (tmp2 == 0x01)) ++ MClk_KHz = 115200; ++ else if((tmp1 == 0x02) && (tmp2 == 0x01)) ++ MClk_KHz = 96000; ++ else if((tmp1 == 0x03) && (tmp2 == 0x00)) ++ MClk_KHz = 192000; ++ else ++ return 0; ++ ++ if(input_datarate < 5200) /*Max. 2011-12-23 11:55*/ ++ input_datarate = 5200; ++ ++ if(input_datarate != 0) ++ divid_ratio = (u8)(MClk_KHz / input_datarate); ++ else ++ divid_ratio = 0xFF; ++ ++ if(divid_ratio > 128) ++ divid_ratio = 128; ++ ++ if(divid_ratio < 2) ++ divid_ratio = 2; ++ ++ tmp1 = (u8)(divid_ratio / 2); ++ tmp2 = (u8)(divid_ratio / 2); ++ ++ if((divid_ratio % 2) != 0) ++ tmp2 += 1; ++ ++ tmp1 -= 1; ++ tmp2 -= 1; ++ ++ tmp1 &= 0x3f; ++ tmp2 &= 0x3f; ++ ++ val = dvbsky_m88ds3103_readreg(state, 0xfe); ++ val &= 0xF0; ++ val |= (tmp2 >> 2) & 0x0f; ++ dvbsky_m88ds3103_writereg(state, 0xfe, val); ++ ++ val = (u8)((tmp2 & 0x03) << 6); ++ val |= tmp1; ++ dvbsky_m88ds3103_writereg(state, 0xea, val); ++ } ++ } else { ++ mod_fac = 2; ++ ++ switch(code_rate) { ++ case 4: input_datarate = locked_sym_rate_KSs*mod_fac/2/8; break; ++ case 3: input_datarate = locked_sym_rate_KSs*mod_fac*2/3/8; break; ++ case 2: input_datarate = locked_sym_rate_KSs*mod_fac*3/4/8; break; ++ case 1: input_datarate = locked_sym_rate_KSs*mod_fac*5/6/8; break; ++ case 0: input_datarate = locked_sym_rate_KSs*mod_fac*7/8/8; break; ++ default: input_datarate = locked_sym_rate_KSs*mod_fac*3/4/8; break; ++ } ++ ++ if(state->demod_id == DS3000_ID) ++ input_datarate = input_datarate * 115 / 100; ++ ++ if(input_datarate < 6857) {tmp1 = 7;tmp2 = 7;} //6.857MHz TS clock ++ else if(input_datarate < 7384) {tmp1 = 6;tmp2 = 7;} //7.384MHz TS clock ++ else if(input_datarate < 8000) {tmp1 = 6;tmp2 = 6;} //8MHz TS clock ++ else if(input_datarate < 8727) {tmp1 = 5;tmp2 = 6;} //8.727MHz TS clock ++ else if(input_datarate < 9600) {tmp1 = 5;tmp2 = 5;} //9.6MHz TS clock ++ else if(input_datarate < 10666) {tmp1 = 4;tmp2 = 5;} //10.666MHz TS clock ++ else if(input_datarate < 12000) {tmp1 = 4;tmp2 = 4;} //12MHz TS clock ++ else if(input_datarate < 13714) {tmp1 = 3;tmp2 = 4;} //13.714MHz TS clock ++ else if(input_datarate < 16000) {tmp1 = 3;tmp2 = 3;} //16MHz TS clock ++ else if(input_datarate < 19200) {tmp1 = 2;tmp2 = 3;} //19.2MHz TS clock ++ else {tmp1 = 2;tmp2 = 2;} //24MHz TS clock ++ ++ if(state->demod_id == DS3000_ID) { ++ val = dvbsky_m88ds3103_readreg(state, 0xfe); ++ val &= 0xc0; ++ val |= ((u8)((tmp1<<3) + tmp2)); ++ dvbsky_m88ds3103_writereg(state, 0xfe, val); ++ } else { ++ if(input_datarate < 5200) /*Max. 2011-12-23 11:55*/ ++ input_datarate = 5200; ++ ++ if(input_datarate != 0) ++ divid_ratio = (u8)(MClk_KHz / input_datarate); ++ else ++ divid_ratio = 0xFF; ++ ++ if(divid_ratio > 128) ++ divid_ratio = 128; ++ ++ if(divid_ratio < 2) ++ divid_ratio = 2; ++ ++ tmp1 = (u8)(divid_ratio / 2); ++ tmp2 = (u8)(divid_ratio / 2); ++ ++ if((divid_ratio % 2) != 0) ++ tmp2 += 1; ++ ++ tmp1 -= 1; ++ tmp2 -= 1; ++ ++ tmp1 &= 0x3f; ++ tmp2 &= 0x3f; ++ ++ val = dvbsky_m88ds3103_readreg(state, 0xfe); ++ val &= 0xF0; ++ val |= (tmp2 >> 2) & 0x0f; ++ dvbsky_m88ds3103_writereg(state, 0xfe, val); ++ ++ val = (u8)((tmp2 & 0x03) << 6); ++ val |= tmp1; ++ dvbsky_m88ds3103_writereg(state, 0xea, val); ++ } ++ } ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_demod_connect(struct dvb_frontend *fe, s32 carrier_offset_khz) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u16 value; ++ u8 val1,val2,data; ++ ++ dprintk("connect delivery system = %d\n", state->delivery_system); ++ ++ /* ds3000 global reset */ ++ dvbsky_m88ds3103_writereg(state, 0x07, 0x80); ++ dvbsky_m88ds3103_writereg(state, 0x07, 0x00); ++ /* ds3000 build-in uC reset */ ++ dvbsky_m88ds3103_writereg(state, 0xb2, 0x01); ++ /* ds3000 software reset */ ++ dvbsky_m88ds3103_writereg(state, 0x00, 0x01); ++ ++ switch (state->delivery_system) { ++ case SYS_DVBS: ++ /* initialise the demod in DVB-S mode */ ++ if(state->demod_id == DS3000_ID){ ++ dvbsky_m88ds3103_init_reg(state, ds3000_dvbs_init_tab, sizeof(ds3000_dvbs_init_tab)); ++ ++ value = dvbsky_m88ds3103_readreg(state, 0xfe); ++ value &= 0xc0; ++ value |= 0x1b; ++ dvbsky_m88ds3103_writereg(state, 0xfe, value); ++ ++ if(state->config->ci_mode) ++ val1 = 0x80; ++ else if(state->config->ts_mode) ++ val1 = 0x60; ++ else ++ val1 = 0x20; ++ dvbsky_m88ds3103_writereg(state, 0xfd, val1); ++ ++ }else if(state->demod_id == DS3103_ID){ ++ dvbsky_m88ds3103_init_reg(state, ds3103_dvbs_init_tab, sizeof(ds3103_dvbs_init_tab)); ++ ++ /* set ts clock */ ++ if(state->config->ci_mode == 2){ ++ val1 = 6; val2 = 6; ++ }else if(state->config->ts_mode == 0) { ++ val1 = 3; val2 = 3; ++ }else{ ++ val1 = 0; val2 = 0; ++ } ++ val1 -= 1; val2 -= 1; ++ val1 &= 0x3f; val2 &= 0x3f; ++ data = dvbsky_m88ds3103_readreg(state, 0xfe); ++ data &= 0xf0; ++ data |= (val2 >> 2) & 0x0f; ++ dvbsky_m88ds3103_writereg(state, 0xfe, data); ++ data = (val2 & 0x03) << 6; ++ data |= val1; ++ dvbsky_m88ds3103_writereg(state, 0xea, data); ++ ++ dvbsky_m88ds3103_writereg(state, 0x4d, 0xfd & dvbsky_m88ds3103_readreg(state, 0x4d)); ++ dvbsky_m88ds3103_writereg(state, 0x30, 0xef & dvbsky_m88ds3103_readreg(state, 0x30)); ++ ++ /* set master clock */ ++ val1 = dvbsky_m88ds3103_readreg(state, 0x22); ++ val2 = dvbsky_m88ds3103_readreg(state, 0x24); ++ ++ val1 &= 0x3f; ++ val2 &= 0x3f; ++ val1 |= 0x80; ++ val2 |= 0x40; ++ ++ dvbsky_m88ds3103_writereg(state, 0x22, val1); ++ dvbsky_m88ds3103_writereg(state, 0x24, val2); ++ ++ if(state->config->ci_mode){ ++ if(state->config->ci_mode == 2) ++ val1 = 0x43; ++ else ++ val1 = 0x03; ++ } ++ else if(state->config->ts_mode) ++ val1 = 0x06; ++ else ++ val1 = 0x42; ++ dvbsky_m88ds3103_writereg(state, 0xfd, val1); ++ } ++ break; ++ case SYS_DVBS2: ++ /* initialise the demod in DVB-S2 mode */ ++ if(state->demod_id == DS3000_ID){ ++ dvbsky_m88ds3103_init_reg(state, ds3000_dvbs2_init_tab, sizeof(ds3000_dvbs2_init_tab)); ++ ++ if (c->symbol_rate >= 30000000) ++ dvbsky_m88ds3103_writereg(state, 0xfe, 0x54); ++ else ++ dvbsky_m88ds3103_writereg(state, 0xfe, 0x98); ++ ++ }else if(state->demod_id == DS3103_ID){ ++ dvbsky_m88ds3103_init_reg(state, ds3103_dvbs2_init_tab, sizeof(ds3103_dvbs2_init_tab)); ++ ++ /* set ts clock */ ++ if(state->config->ci_mode == 2){ ++ val1 = 6; val2 = 6; ++ }else if(state->config->ts_mode == 0){ ++ val1 = 5; val2 = 4; ++ }else{ ++ val1 = 0; val2 = 0; ++ } ++ val1 -= 1; val2 -= 1; ++ val1 &= 0x3f; val2 &= 0x3f; ++ data = dvbsky_m88ds3103_readreg(state, 0xfe); ++ data &= 0xf0; ++ data |= (val2 >> 2) & 0x0f; ++ dvbsky_m88ds3103_writereg(state, 0xfe, data); ++ data = (val2 & 0x03) << 6; ++ data |= val1; ++ dvbsky_m88ds3103_writereg(state, 0xea, data); ++ ++ dvbsky_m88ds3103_writereg(state, 0x4d, 0xfd & dvbsky_m88ds3103_readreg(state, 0x4d)); ++ dvbsky_m88ds3103_writereg(state, 0x30, 0xef & dvbsky_m88ds3103_readreg(state, 0x30)); ++ ++ /* set master clock */ ++ val1 = dvbsky_m88ds3103_readreg(state, 0x22); ++ val2 = dvbsky_m88ds3103_readreg(state, 0x24); ++ ++ val1 &= 0x3f; ++ val2 &= 0x3f; ++ if((state->config->ci_mode == 2) || (state->config->ts_mode == 1)){ ++ val1 |= 0x80; ++ val2 |= 0x40; ++ }else{ ++ if (c->symbol_rate >= 28000000){ ++ val1 |= 0xc0; ++ }else if (c->symbol_rate >= 18000000){ ++ val2 |= 0x40; ++ }else{ ++ val1 |= 0x80; ++ val2 |= 0x40; ++ } ++ } ++ dvbsky_m88ds3103_writereg(state, 0x22, val1); ++ dvbsky_m88ds3103_writereg(state, 0x24, val2); ++ } ++ ++ if(state->config->ci_mode){ ++ if(state->config->ci_mode == 2) ++ val1 = 0x43; ++ else ++ val1 = 0x03; ++ } ++ else if(state->config->ts_mode) ++ val1 = 0x06; ++ else ++ val1 = 0x42; ++ dvbsky_m88ds3103_writereg(state, 0xfd, val1); ++ ++ break; ++ default: ++ return 1; ++ } ++ /* disable 27MHz clock output */ ++ dvbsky_m88ds3103_writereg(state, 0x29, 0x80); ++ /* enable ac coupling */ ++ dvbsky_m88ds3103_writereg(state, 0x25, 0x8a); ++ ++ if ((c->symbol_rate / 1000) <= 3000){ ++ dvbsky_m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 32 * 100 / 64 = 400*/ ++ dvbsky_m88ds3103_writereg(state, 0xc8, 0x20); ++ dvbsky_m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/ ++ dvbsky_m88ds3103_writereg(state, 0xc7, 0x00); ++ }else if((c->symbol_rate / 1000) <= 10000){ ++ dvbsky_m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 16 * 100 / 64 = 200*/ ++ dvbsky_m88ds3103_writereg(state, 0xc8, 0x10); ++ dvbsky_m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/ ++ dvbsky_m88ds3103_writereg(state, 0xc7, 0x00); ++ }else{ ++ dvbsky_m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 6 * 100 / 64 = 75*/ ++ dvbsky_m88ds3103_writereg(state, 0xc8, 0x06); ++ dvbsky_m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/ ++ dvbsky_m88ds3103_writereg(state, 0xc7, 0x00); ++ } ++ ++ dvbsky_m88ds3103_set_symrate(fe); ++ ++ dvbsky_m88ds3103_set_CCI(fe); ++ ++ dvbsky_m88ds3103_set_carrier_offset(fe, carrier_offset_khz); ++ ++ /* ds3000 out of software reset */ ++ dvbsky_m88ds3103_writereg(state, 0x00, 0x00); ++ /* start ds3000 build-in uC */ ++ dvbsky_m88ds3103_writereg(state, 0xb2, 0x00); ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ int i; ++ fe_status_t status; ++ u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf, div4, capCode, changePLL; ++ s32 offset_khz, lpf_offset_KHz; ++ u16 value, ndiv, lpf_coeff; ++ u32 f3db, gdiv28, realFreq; ++ u8 RFgain; ++ ++ dprintk("%s() ", __func__); ++ dprintk("c frequency = %d\n", c->frequency); ++ dprintk("symbol rate = %d\n", c->symbol_rate); ++ dprintk("delivery system = %d\n", c->delivery_system); ++ ++ state->delivery_system = c->delivery_system; ++ ++ realFreq = c->frequency; ++ lpf_offset_KHz = 0; ++ if(c->symbol_rate < 5000000){ ++ lpf_offset_KHz = FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz; ++ realFreq += FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz; ++ } ++ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ ++ div4 = 0; ++ RFgain = 0; ++ if(state->tuner_id == TS2022_ID){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x0a); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x11, 0x40); ++ if (realFreq < 1103000) { ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x1b); ++ div4 = 1; ++ ndiv = (realFreq * (6 + 8) * 4)/MT_FE_CRYSTAL_KHZ; ++ }else { ++ ndiv = (realFreq * (6 + 8) * 2)/MT_FE_CRYSTAL_KHZ; ++ } ++ ndiv = ndiv + ndiv%2; ++ if(ndiv < 4095) ++ ndiv = ndiv - 1024; ++ else if (ndiv < 6143) ++ ndiv = ndiv + 1024; ++ else ++ ndiv = ndiv + 3072; ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x01, (ndiv & 0x3f00) >> 8); ++ }else{ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x00); ++ if (realFreq < 1146000){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x11); ++ div4 = 1; ++ ndiv = (realFreq * (6 + 8) * 4) / MT_FE_CRYSTAL_KHZ; ++ }else{ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x01); ++ ndiv = (realFreq * (6 + 8) * 2) / MT_FE_CRYSTAL_KHZ; ++ } ++ ndiv = ndiv + ndiv%2; ++ ndiv = ndiv - 1024; ++ dvbsky_m88ds3103_tuner_writereg(state, 0x01, (ndiv>>8)&0x0f); ++ } ++ /* set pll */ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x02, ndiv & 0x00ff); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x03, 0x06); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x0f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x10); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ ++ if(state->tuner_id == TS2022_ID){ ++ if(( realFreq >= 1650000 ) && (realFreq <= 1850000)){ ++ msleep(5); ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x14); ++ value &= 0x7f; ++ if(value < 64){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x82); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x11, 0x6f); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x0f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x10); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ } ++ } ++ msleep(5); ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x14); ++ value &= 0x1f; ++ ++ if(value > 19){ ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x10); ++ value &= 0x1d; ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, value); ++ } ++ }else{ ++ msleep(5); ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x66); ++ changePLL = (((value & 0x80) >> 7) != div4); ++ ++ if(changePLL){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x11); ++ div4 = 1; ++ ndiv = (realFreq * (6 + 8) * 4)/MT_FE_CRYSTAL_KHZ; ++ ndiv = ndiv + ndiv%2; ++ ndiv = ndiv - 1024; ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x01, (ndiv>>8) & 0x0f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x02, ndiv & 0xff); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x0f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x10); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ } ++ } ++ /*set the RF gain*/ ++ if(state->tuner_id == TS2020_ID) ++ dvbsky_m88ds3103_tuner_writereg(state, 0x60, 0x79); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x17); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x08); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ msleep(5); ++ ++ if(state->tuner_id == TS2020_ID){ ++ RFgain = dvbsky_m88ds3103_tuner_readreg(state, 0x3d); ++ RFgain &= 0x0f; ++ if(RFgain < 15){ ++ if(RFgain < 4) ++ RFgain = 0; ++ else ++ RFgain = RFgain -3; ++ value = ((RFgain << 3) | 0x01) & 0x79; ++ dvbsky_m88ds3103_tuner_writereg(state, 0x60, value); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x17); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x08); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ } ++ } ++ ++ /* set the LPF */ ++ if(state->tuner_id == TS2022_ID){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x25, 0x00); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x27, 0x70); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x41, 0x09); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x08, 0x0b); ++ } ++ ++ f3db = ((c->symbol_rate / 1000) *135) / 200 + 2000; ++ f3db += lpf_offset_KHz; ++ if (f3db < 7000) ++ f3db = 7000; ++ if (f3db > 40000) ++ f3db = 40000; ++ ++ gdiv28 = (MT_FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000; ++ dvbsky_m88ds3103_tuner_writereg(state, 0x04, gdiv28 & 0xff); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ msleep(5); ++ ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x26); ++ capCode = value & 0x3f; ++ if(state->tuner_id == TS2022_ID){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x41, 0x0d); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ ++ msleep(2); ++ ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x26); ++ value &= 0x3f; ++ value = (capCode + value) / 2; ++ } ++ else ++ value = capCode; ++ ++ gdiv28 = gdiv28 * 207 / (value * 2 + 151); ++ mlpf_max = gdiv28 * 135 / 100; ++ mlpf_min = gdiv28 * 78 / 100; ++ if (mlpf_max > 63) ++ mlpf_max = 63; ++ ++ if(state->tuner_id == TS2022_ID) ++ lpf_coeff = 3200; ++ else ++ lpf_coeff = 2766; ++ ++ nlpf = (f3db * gdiv28 * 2 / lpf_coeff / (MT_FE_CRYSTAL_KHZ / 1000) + 1) / 2 ; ++ if (nlpf > 23) nlpf = 23; ++ if (nlpf < 1) nlpf = 1; ++ ++ lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000) * lpf_coeff * 2 / f3db + 1) / 2; ++ ++ if (lpf_mxdiv < mlpf_min){ ++ nlpf++; ++ lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000) * lpf_coeff * 2 / f3db + 1) / 2; ++ } ++ ++ if (lpf_mxdiv > mlpf_max) ++ lpf_mxdiv = mlpf_max; ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x04, lpf_mxdiv); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x06, nlpf); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ msleep(5); ++ ++ if(state->tuner_id == TS2022_ID){ ++ msleep(2); ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x26); ++ capCode = value & 0x3f; ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x41, 0x09); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ ++ msleep(2); ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x26); ++ value &= 0x3f; ++ value = (capCode + value) / 2; ++ ++ value = value | 0x80; ++ dvbsky_m88ds3103_tuner_writereg(state, 0x25, value); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x27, 0x30); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x08, 0x09); ++ } ++ ++ /* Set the BB gain */ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1e); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x01); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ if(state->tuner_id == TS2020_ID){ ++ if(RFgain == 15){ ++ msleep(40); ++ value = dvbsky_m88ds3103_tuner_readreg(state, 0x21); ++ value &= 0x0f; ++ if(value < 3){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x60, 0x61); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x17); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x08); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00); ++ } ++ } ++ } ++ msleep(60); ++ ++ offset_khz = (ndiv - ndiv % 2 + 1024) * MT_FE_CRYSTAL_KHZ ++ / (6 + 8) / (div4 + 1) / 2 - realFreq; ++ ++ dvbsky_m88ds3103_demod_connect(fe, offset_khz+lpf_offset_KHz); ++ ++ for (i = 0; i < 30 ; i++) { ++ dvbsky_m88ds3103_read_status(fe, &status); ++ if (status & FE_HAS_LOCK){ ++ break; ++ } ++ msleep(20); ++ } ++ ++ if (status & FE_HAS_LOCK){ ++ if(state->config->ci_mode == 2) ++ dvbsky_m88ds3103_set_clock_ratio(state); ++ if(state->config->start_ctrl){ ++ if(state->first_lock == 0){ ++ state->config->start_ctrl(fe); ++ state->first_lock = 1; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int dvbsky_m88ds3103_tune(struct dvb_frontend *fe, ++ bool re_tune, ++ unsigned int mode_flags, ++ unsigned int *delay, ++ fe_status_t *status) ++{ ++ *delay = HZ / 5; ++ ++ dprintk("%s() ", __func__); ++ dprintk("re_tune = %d\n", re_tune); ++ ++ if (re_tune) { ++ int ret = dvbsky_m88ds3103_set_frontend(fe); ++ if (ret) ++ return ret; ++ } ++ ++ return dvbsky_m88ds3103_read_status(fe, status); ++} ++ ++static enum dvbfe_algo dvbsky_m88ds3103_get_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_HW; ++} ++ ++ /* ++ * Power config will reset and load initial firmware if required ++ */ ++static int dvbsky_m88ds3103_initilaze(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dprintk("%s()\n", __func__); ++ /* hard reset */ ++ dvbsky_m88ds3103_writereg(state, 0x07, 0x80); ++ dvbsky_m88ds3103_writereg(state, 0x07, 0x00); ++ msleep(1); ++ ++ dvbsky_m88ds3103_writereg(state, 0x08, 0x01 | dvbsky_m88ds3103_readreg(state, 0x08)); ++ msleep(1); ++ ++ if(state->tuner_id == TS2020_ID){ ++ /* TS2020 init */ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x42, 0x73); ++ msleep(2); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x05, 0x01); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x62, 0xb5); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x07, 0x02); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x08, 0x01); ++ } ++ else if(state->tuner_id == TS2022_ID){ ++ /* TS2022 init */ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x62, 0x6c); ++ msleep(2); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x42, 0x6c); ++ msleep(2); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x7d, 0x9d); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x7c, 0x9a); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x7a, 0x76); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x3b, 0x01); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x63, 0x88); ++ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x61, 0x85); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x22, 0x30); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x30, 0x40); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x20, 0x23); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x24, 0x02); ++ dvbsky_m88ds3103_tuner_writereg(state, 0x12, 0xa0); ++ } ++ ++ if(state->demod_id == DS3103_ID){ ++ dvbsky_m88ds3103_writereg(state, 0x07, 0xe0); ++ dvbsky_m88ds3103_writereg(state, 0x07, 0x00); ++ msleep(1); ++ } ++ dvbsky_m88ds3103_writereg(state, 0xb2, 0x01); ++ ++ /* Load the firmware if required */ ++ ret = dvbsky_m88ds3103_load_firmware(fe); ++ if (ret != 0){ ++ printk(KERN_ERR "%s: Unable initialize firmware\n", __func__); ++ return ret; ++ } ++ if(state->demod_id == DS3103_ID){ ++ dvbsky_m88ds3103_writereg(state, 0x4d, 0xfd & dvbsky_m88ds3103_readreg(state, 0x4d)); ++ dvbsky_m88ds3103_writereg(state, 0x30, 0xef & dvbsky_m88ds3103_readreg(state, 0x30)); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Initialise or wake up device ++ */ ++static int dvbsky_m88ds3103_initfe(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk("%s()\n", __func__); ++ ++ /* 1st step to wake up demod */ ++ dvbsky_m88ds3103_writereg(state, 0x08, 0x01 | dvbsky_m88ds3103_readreg(state, 0x08)); ++ dvbsky_m88ds3103_writereg(state, 0x04, 0xfe & dvbsky_m88ds3103_readreg(state, 0x04)); ++ dvbsky_m88ds3103_writereg(state, 0x23, 0xef & dvbsky_m88ds3103_readreg(state, 0x23)); ++ ++ /* 2nd step to wake up tuner */ ++ val = dvbsky_m88ds3103_tuner_readreg(state, 0x00) & 0xff; ++ if((val & 0x01) == 0){ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x01); ++ msleep(50); ++ } ++ dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x03); ++ msleep(50); ++ ++ return 0; ++} ++ ++/* Put device to sleep */ ++static int dvbsky_m88ds3103_sleep(struct dvb_frontend *fe) ++{ ++ struct dvbsky_m88ds3103_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ /* 1st step to sleep tuner */ ++ dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x00); ++ ++ /* 2nd step to sleep demod */ ++ dvbsky_m88ds3103_writereg(state, 0x08, 0xfe & dvbsky_m88ds3103_readreg(state, 0x08)); ++ dvbsky_m88ds3103_writereg(state, 0x04, 0x01 | dvbsky_m88ds3103_readreg(state, 0x04)); ++ dvbsky_m88ds3103_writereg(state, 0x23, 0x10 | dvbsky_m88ds3103_readreg(state, 0x23)); ++ ++ ++ return 0; ++} ++ ++static struct dvb_frontend_ops dvbsky_m88ds3103_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2}, ++ .info = { ++ .name = "Montage DS3103/TS2022", ++ .type = FE_QPSK, ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1011, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 5000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_2G_MODULATION | ++ FE_CAN_QPSK | FE_CAN_RECOVER ++ }, ++ ++ .release = dvbsky_m88ds3103_release, ++ ++ .init = dvbsky_m88ds3103_initfe, ++ .sleep = dvbsky_m88ds3103_sleep, ++ .read_status = dvbsky_m88ds3103_read_status, ++ .read_ber = dvbsky_m88ds3103_read_ber, ++ .read_signal_strength = dvbsky_m88ds3103_read_signal_strength, ++ .read_snr = dvbsky_m88ds3103_read_snr, ++ .read_ucblocks = dvbsky_m88ds3103_read_ucblocks, ++ .set_tone = dvbsky_m88ds3103_set_tone, ++ .set_voltage = dvbsky_m88ds3103_set_voltage, ++ .diseqc_send_master_cmd = dvbsky_m88ds3103_send_diseqc_msg, ++ .diseqc_send_burst = dvbsky_m88ds3103_diseqc_send_burst, ++ .get_frontend_algo = dvbsky_m88ds3103_get_algo, ++ .tune = dvbsky_m88ds3103_tune, ++ .set_frontend = dvbsky_m88ds3103_set_frontend, ++}; ++ ++MODULE_DESCRIPTION("DVB Frontend module for Montage DS3103/TS2022 hardware"); ++MODULE_AUTHOR("Max nibble"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88ds3103.h linux-openelec/drivers/media/dvb-frontends/dvbsky_m88ds3103.h +--- linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88ds3103.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/dvbsky_m88ds3103.h 2015-07-24 18:03:30.120842002 -0500 +@@ -0,0 +1,53 @@ ++/* ++ Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner driver ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef dvbsky_m88ds3103_H ++#define dvbsky_m88ds3103_H ++ ++#include ++#include ++ ++struct dvbsky_m88ds3103_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ u8 ci_mode; ++ u8 pin_ctrl; ++ u8 ts_mode; /* 0: Parallel, 1: Serial */ ++ ++ /* Set device param to start dma */ ++ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); ++ /* Start to transfer data */ ++ int (*start_ctrl)(struct dvb_frontend *fe); ++ /* Set LNB voltage */ ++ int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); ++}; ++ ++#if IS_ENABLED(CONFIG_DVB_DVBSKY_M88DS3103) ++extern struct dvb_frontend *dvbsky_m88ds3103_attach( ++ const struct dvbsky_m88ds3103_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *dvbsky_m88ds3103_attach( ++ const struct dvbsky_m88ds3103_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_DVBSKY_M88DS3103 */ ++#endif /* dvbsky_m88ds3103_H */ +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88ds3103_priv.h linux-openelec/drivers/media/dvb-frontends/dvbsky_m88ds3103_priv.h +--- linux-3.14.36/drivers/media/dvb-frontends/dvbsky_m88ds3103_priv.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/dvbsky_m88ds3103_priv.h 2015-07-24 18:03:30.120842002 -0500 +@@ -0,0 +1,403 @@ ++/* ++ Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner driver ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef dvbsky_m88ds3103_PRIV_H ++#define dvbsky_m88ds3103_PRIV_H ++ ++#define FW_DOWN_SIZE 32 ++#define FW_DOWN_LOOP (8192/FW_DOWN_SIZE) ++#define DS3103_DEFAULT_FIRMWARE "dvb-fe-ds3103.fw" ++#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds300x.fw" ++#define MT_FE_MCLK_KHZ 96000 /* in kHz */ ++#define MT_FE_CRYSTAL_KHZ 27000 /* in kHz */ ++#define FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz 3000 ++#define DS3000_ID 0x3000 ++#define DS3103_ID 0x3103 ++#define TS2020_ID 0x2020 ++#define TS2022_ID 0x2022 ++#define UNKNOW_ID 0x0000 ++ ++struct dvbsky_m88ds3103_state { ++ struct i2c_adapter *i2c; ++ const struct dvbsky_m88ds3103_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ u32 preBer; ++ u8 skip_fw_load; ++ u8 first_lock; /* The first time of signal lock */ ++ u16 demod_id; /* demod chip type */ ++ u16 tuner_id; /* tuner chip type */ ++ fe_delivery_system_t delivery_system; ++}; ++ ++/* For M88DS3103 demod dvbs mode.*/ ++static u8 ds3103_dvbs_init_tab[] = { ++ 0x23, 0x07, ++ 0x08, 0x03, ++ 0x0c, 0x02, ++ 0x21, 0x54, ++ 0x25, 0x82, ++ 0x27, 0x31, ++ 0x30, 0x08, ++ 0x31, 0x40, ++ 0x32, 0x32, ++ 0x33, 0x35, ++ 0x35, 0xff, ++ 0x3a, 0x00, ++ 0x37, 0x10, ++ 0x38, 0x10, ++ 0x39, 0x02, ++ 0x42, 0x60, ++ 0x4a, 0x80, ++ 0x4b, 0x04, ++ 0x4d, 0x91, ++ 0x5d, 0xc8, ++ 0x50, 0x36, ++ 0x51, 0x36, ++ 0x52, 0x36, ++ 0x53, 0x36, ++ 0x63, 0x0f, ++ 0x64, 0x30, ++ 0x65, 0x40, ++ 0x68, 0x26, ++ 0x69, 0x4c, ++ 0x70, 0x20, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x40, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x60, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x80, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0xa0, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x1f, ++ 0x76, 0x38, ++ 0x77, 0xa6, ++ 0x78, 0x0c, ++ 0x79, 0x80, ++ 0x7f, 0x14, ++ 0x7c, 0x00, ++ 0xae, 0x82, ++ 0x80, 0x64, ++ 0x81, 0x66, ++ 0x82, 0x44, ++ 0x85, 0x04, ++ 0xcd, 0xf4, ++ 0x90, 0x33, ++ 0xa0, 0x44, ++ 0xc0, 0x08, ++ 0xc3, 0x10, ++ 0xc4, 0x08, ++ 0xc5, 0xf0, ++ 0xc6, 0xff, ++ 0xc7, 0x00, ++ 0xc8, 0x1a, ++ 0xc9, 0x80, ++ 0xe0, 0xf8, ++ 0xe6, 0x8b, ++ 0xd0, 0x40, ++ 0xf8, 0x20, ++ 0xfa, 0x0f, ++ 0x00, 0x00, ++ 0xbd, 0x01, ++ 0xb8, 0x00, ++}; ++/* For M88DS3103 demod dvbs2 mode.*/ ++static u8 ds3103_dvbs2_init_tab[] = { ++ 0x23, 0x07, ++ 0x08, 0x07, ++ 0x0c, 0x02, ++ 0x21, 0x54, ++ 0x25, 0x82, ++ 0x27, 0x31, ++ 0x30, 0x08, ++ 0x32, 0x32, ++ 0x33, 0x35, ++ 0x35, 0xff, ++ 0x3a, 0x00, ++ 0x37, 0x10, ++ 0x38, 0x10, ++ 0x39, 0x02, ++ 0x42, 0x60, ++ 0x4a, 0x80, ++ 0x4b, 0x04, ++ 0x4d, 0x91, ++ 0x5d, 0xc8, ++ 0x50, 0x36, ++ 0x51, 0x36, ++ 0x52, 0x36, ++ 0x53, 0x36, ++ 0x63, 0x0f, ++ 0x64, 0x10, ++ 0x65, 0x20, ++ 0x68, 0x46, ++ 0x69, 0xcd, ++ 0x70, 0x20, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x40, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x60, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x80, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0xa0, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x1f, ++ 0x76, 0x38, ++ 0x77, 0xa6, ++ 0x78, 0x0c, ++ 0x79, 0x80, ++ 0x7f, 0x14, ++ 0x85, 0x08, ++ 0xcd, 0xf4, ++ 0x90, 0x33, ++ 0x86, 0x00, ++ 0x87, 0x0f, ++ 0x89, 0x00, ++ 0x8b, 0x44, ++ 0x8c, 0x66, ++ 0x9d, 0xc1, ++ 0x8a, 0x10, ++ 0xad, 0x40, ++ 0xa0, 0x44, ++ 0xc0, 0x08, ++ 0xc1, 0x10, ++ 0xc2, 0x08, ++ 0xc3, 0x10, ++ 0xc4, 0x08, ++ 0xc5, 0xf0, ++ 0xc6, 0xff, ++ 0xc7, 0x00, ++ 0xc8, 0x1a, ++ 0xc9, 0x80, ++ 0xca, 0x23, ++ 0xcb, 0x24, ++ 0xcc, 0xf4, ++ 0xce, 0x74, ++ 0x00, 0x00, ++ 0xbd, 0x01, ++ 0xb8, 0x00, ++}; ++ ++/* For M88DS3000 demod dvbs mode.*/ ++static u8 ds3000_dvbs_init_tab[] = { ++ 0x23, 0x05, ++ 0x08, 0x03, ++ 0x0c, 0x02, ++ 0x21, 0x54, ++ 0x25, 0x82, ++ 0x27, 0x31, ++ 0x30, 0x08, ++ 0x31, 0x40, ++ 0x32, 0x32, ++ 0x33, 0x35, ++ 0x35, 0xff, ++ 0x3a, 0x00, ++ 0x37, 0x10, ++ 0x38, 0x10, ++ 0x39, 0x02, ++ 0x42, 0x60, ++ 0x4a, 0x40, ++ 0x4b, 0x04, ++ 0x4d, 0x91, ++ 0x5d, 0xc8, ++ 0x50, 0x77, ++ 0x51, 0x77, ++ 0x52, 0x36, ++ 0x53, 0x36, ++ 0x56, 0x01, ++ 0x63, 0x47, ++ 0x64, 0x30, ++ 0x65, 0x40, ++ 0x68, 0x26, ++ 0x69, 0x4c, ++ 0x70, 0x20, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x40, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x60, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x80, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0xa0, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x1f, ++ 0x76, 0x00, ++ 0x77, 0xd1, ++ 0x78, 0x0c, ++ 0x79, 0x80, ++ 0x7f, 0x04, ++ 0x7c, 0x00, ++ 0x80, 0x86, ++ 0x81, 0xa6, ++ 0x85, 0x04, ++ 0xcd, 0xf4, ++ 0x90, 0x33, ++ 0xa0, 0x44, ++ 0xc0, 0x18, ++ 0xc3, 0x10, ++ 0xc4, 0x08, ++ 0xc5, 0x80, ++ 0xc6, 0x80, ++ 0xc7, 0x0a, ++ 0xc8, 0x1a, ++ 0xc9, 0x80, ++ 0xfe, 0xb6, ++ 0xe0, 0xf8, ++ 0xe6, 0x8b, ++ 0xd0, 0x40, ++ 0xf8, 0x20, ++ 0xfa, 0x0f, ++ 0xad, 0x20, ++ 0xae, 0x07, ++ 0xb8, 0x00, ++}; ++ ++/* For M88DS3000 demod dvbs2 mode.*/ ++static u8 ds3000_dvbs2_init_tab[] = { ++ 0x23, 0x0f, ++ 0x08, 0x07, ++ 0x0c, 0x02, ++ 0x21, 0x54, ++ 0x25, 0x82, ++ 0x27, 0x31, ++ 0x30, 0x08, ++ 0x31, 0x32, ++ 0x32, 0x32, ++ 0x33, 0x35, ++ 0x35, 0xff, ++ 0x3a, 0x00, ++ 0x37, 0x10, ++ 0x38, 0x10, ++ 0x39, 0x02, ++ 0x42, 0x60, ++ 0x4a, 0x80, ++ 0x4b, 0x04, ++ 0x4d, 0x91, ++ 0x5d, 0x88, ++ 0x50, 0x36, ++ 0x51, 0x36, ++ 0x52, 0x36, ++ 0x53, 0x36, ++ 0x63, 0x60, ++ 0x64, 0x10, ++ 0x65, 0x10, ++ 0x68, 0x04, ++ 0x69, 0x29, ++ 0x70, 0x20, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x40, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x60, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x80, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0xa0, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x1f, ++ 0xa0, 0x44, ++ 0xc0, 0x08, ++ 0xc1, 0x10, ++ 0xc2, 0x08, ++ 0xc3, 0x10, ++ 0xc4, 0x08, ++ 0xc5, 0xf0, ++ 0xc6, 0xf0, ++ 0xc7, 0x0a, ++ 0xc8, 0x1a, ++ 0xc9, 0x80, ++ 0xca, 0x23, ++ 0xcb, 0x24, ++ 0xce, 0x74, ++ 0x56, 0x01, ++ 0x90, 0x03, ++ 0x76, 0x80, ++ 0x77, 0x42, ++ 0x78, 0x0a, ++ 0x79, 0x80, ++ 0xad, 0x40, ++ 0xae, 0x07, ++ 0x7f, 0xd4, ++ 0x7c, 0x00, ++ 0x80, 0xa8, ++ 0x81, 0xda, ++ 0x7c, 0x01, ++ 0x80, 0xda, ++ 0x81, 0xec, ++ 0x7c, 0x02, ++ 0x80, 0xca, ++ 0x81, 0xeb, ++ 0x7c, 0x03, ++ 0x80, 0xba, ++ 0x81, 0xdb, ++ 0x85, 0x08, ++ 0x86, 0x00, ++ 0x87, 0x02, ++ 0x89, 0x80, ++ 0x8b, 0x44, ++ 0x8c, 0xaa, ++ 0x8a, 0x10, ++ 0xba, 0x00, ++ 0xf5, 0x04, ++ 0xd2, 0x32, ++ 0xb8, 0x00, ++}; ++ ++#endif /* dvbsky_m88ds3103_PRIV_H */ +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/Kconfig linux-openelec/drivers/media/dvb-frontends/Kconfig +--- linux-3.14.36/drivers/media/dvb-frontends/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/Kconfig 2015-07-24 18:03:30.364842002 -0500 +@@ -4,6 +4,13 @@ + comment "Multistandard (satellite) frontends" + depends on DVB_CORE + ++config DVB_CX24120 ++ tristate "Conexant CX24120 based" ++ depends on DVB_CORE && I2C ++ default m if DVB_FE_CUSTOMISE ++ help ++ A DVB-S/DVB-S2 tuner module. Say Y when you want to support this frontend. ++ + config DVB_STB0899 + tristate "STB0899 based" + depends on DVB_CORE && I2C +@@ -214,6 +221,20 @@ + help + A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend. + ++config DVB_DVBSKY_M88DS3103 ++ tristate "Montage M88DS3103 based (dvbsky)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 tuner module. Say Y when you want to support this frontend. ++ ++config DVB_DVBSKY_M88DC2800 ++ tristate "Montage M88DC2800 based (dvbsky)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ + config DVB_SI21XX + tristate "Silicon Labs SI21XX based" + depends on DVB_CORE && I2C +@@ -753,6 +774,16 @@ + tristate "Afatech AF9033 DVB-T demodulator" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT ++ ++config DVB_SI2168 ++ tristate "Afatech AF9033 DVB-T demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ ++config DVB_SP2 ++ tristate "Afatech AF9033 DVB-T demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT + + comment "Tools to develop new frontends" + +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/Kconfig.orig linux-openelec/drivers/media/dvb-frontends/Kconfig.orig +--- linux-3.14.36/drivers/media/dvb-frontends/Kconfig.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/Kconfig.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,762 @@ ++menu "Customise DVB Frontends" ++ visible if !MEDIA_SUBDRV_AUTOSELECT ++ ++comment "Multistandard (satellite) frontends" ++ depends on DVB_CORE ++ ++config DVB_STB0899 ++ tristate "STB0899 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want ++ to support this demodulator based frontends ++ ++config DVB_STB6100 ++ tristate "STB6100 based tuners" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A Silicon tuner from ST used in conjunction with the STB0899 ++ demodulator. Say Y when you want to support this tuner. ++ ++config DVB_STV090x ++ tristate "STV0900/STV0903(A/B) based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators. ++ Say Y when you want to support these frontends. ++ ++config DVB_STV6110x ++ tristate "STV6110/(A) based tuners" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A Silicon tuner that supports DVB-S and DVB-S2 modes ++ ++config DVB_M88DS3103 ++ tristate "Montage M88DS3103" ++ depends on DVB_CORE && I2C && I2C_MUX ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++comment "Multistandard (cable + terrestrial) frontends" ++ depends on DVB_CORE ++ ++config DVB_DRXK ++ tristate "Micronas DRXK based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Micronas DRX-K DVB-C/T demodulator. ++ ++ Say Y when you want to support this frontend. ++ ++config DVB_TDA18271C2DD ++ tristate "NXP TDA18271C2 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ NXP TDA18271 silicon tuner. ++ ++ Say Y when you want to support this tuner. ++ ++comment "DVB-S (satellite) frontends" ++ depends on DVB_CORE ++ ++config DVB_CX24110 ++ tristate "Conexant CX24110 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_CX24123 ++ tristate "Conexant CX24123 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_MT312 ++ tristate "Zarlink VP310/MT312/ZL10313 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ZL10036 ++ tristate "Zarlink ZL10036 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ZL10039 ++ tristate "Zarlink ZL10039 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_S5H1420 ++ tristate "Samsung S5H1420 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STV0288 ++ tristate "ST STV0288 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STB6000 ++ tristate "ST STB6000 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S silicon tuner module. Say Y when you want to support this tuner. ++ ++config DVB_STV0299 ++ tristate "ST STV0299 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STV6110 ++ tristate "ST STV6110 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S silicon tuner module. Say Y when you want to support this tuner. ++ ++config DVB_STV0900 ++ tristate "ST STV0900 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 demodulator. Say Y when you want to support this frontend. ++ ++config DVB_TDA8083 ++ tristate "Philips TDA8083 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA10086 ++ tristate "Philips TDA10086 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA8261 ++ tristate "Philips TDA8261 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_VES1X93 ++ tristate "VLSI VES1893 or VES1993 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TUNER_ITD1000 ++ tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TUNER_CX24113 ++ tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++ ++config DVB_TDA826X ++ tristate "Philips TDA826X silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S silicon tuner module. Say Y when you want to support this tuner. ++ ++config DVB_TUA6100 ++ tristate "Infineon TUA6100 PLL" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S PLL chip. ++ ++config DVB_CX24116 ++ tristate "Conexant CX24116 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 tuner module. Say Y when you want to support this frontend. ++ ++config DVB_CX24117 ++ tristate "Conexant CX24117 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend. ++ ++config DVB_SI21XX ++ tristate "Silicon Labs SI21XX based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TS2020 ++ tristate "Montage Tehnology TS2020 based tuners" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. ++ ++config DVB_DS3000 ++ tristate "Montage Tehnology DS3000 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 tuner module. Say Y when you want to support this frontend. ++ ++config DVB_MB86A16 ++ tristate "Fujitsu MB86A16 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/DSS Direct Conversion reveiver. ++ Say Y when you want to support this frontend. ++ ++config DVB_TDA10071 ++ tristate "NXP TDA10071" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++comment "DVB-T (terrestrial) frontends" ++ depends on DVB_CORE ++ ++config DVB_SP8870 ++ tristate "Spase sp8870 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the command ++ "/Documentation/dvb/get_dvb_firmware sp8870" to ++ download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_SP887X ++ tristate "Spase sp887x based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the command ++ "/Documentation/dvb/get_dvb_firmware sp887x" to ++ download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_CX22700 ++ tristate "Conexant CX22700 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_CX22702 ++ tristate "Conexant cx22702 demodulator (OFDM)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_S5H1432 ++ tristate "Samsung s5h1432 demodulator (OFDM)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_DRXD ++ tristate "Micronas DRXD driver" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ Note: this driver was based on vendor driver reference code (released ++ under the GPL) as opposed to the existing drx397xd driver, which ++ was written via reverse engineering. ++ ++config DVB_L64781 ++ tristate "LSI L64781" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA1004X ++ tristate "Philips TDA10045H/TDA10046H based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the commands ++ "/Documentation/dvb/get_dvb_firmware tda10045", ++ "/Documentation/dvb/get_dvb_firmware tda10046" to ++ download/extract them, and then copy them to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_NXT6000 ++ tristate "NxtWave Communications NXT6000 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_MT352 ++ tristate "Zarlink MT352 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ZL10353 ++ tristate "Zarlink ZL10353 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_DIB3000MB ++ tristate "DiBcom 3000M-B" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB3000MC ++ tristate "DiBcom 3000P/M-C" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB7000M ++ tristate "DiBcom 7000MA/MB/PA/PB/MC" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB7000P ++ tristate "DiBcom 7000PC" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB9000 ++ tristate "DiBcom 9000" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_TDA10048 ++ tristate "Philips TDA10048HN based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_AF9013 ++ tristate "Afatech AF9013 demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_EC100 ++ tristate "E3C EC100" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_HD29L2 ++ tristate "HDIC HD29L2" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_STV0367 ++ tristate "ST STV0367 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T/C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_CXD2820R ++ tristate "Sony CXD2820R" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_RTL2830 ++ tristate "Realtek RTL2830 DVB-T" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_RTL2832 ++ tristate "Realtek RTL2832 DVB-T" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++comment "DVB-C (cable) frontends" ++ depends on DVB_CORE ++ ++config DVB_VES1820 ++ tristate "VLSI VES1820 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA10021 ++ tristate "Philips TDA10021 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA10023 ++ tristate "Philips TDA10023 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STV0297 ++ tristate "ST STV0297 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" ++ depends on DVB_CORE ++ ++config DVB_NXT200X ++ tristate "NxtWave Communications NXT2002/NXT2004 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++ This driver needs external firmware. Please use the commands ++ "/Documentation/dvb/get_dvb_firmware nxt2002" and ++ "/Documentation/dvb/get_dvb_firmware nxt2004" to ++ download/extract them, and then copy them to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_OR51211 ++ tristate "Oren OR51211 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the command ++ "/Documentation/dvb/get_dvb_firmware or51211" to ++ download it, and then copy it to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_OR51132 ++ tristate "Oren OR51132 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++ This driver needs external firmware. Please use the commands ++ "/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or ++ "/Documentation/dvb/get_dvb_firmware or51132_qam" to ++ download firmwares for 8VSB and QAM64/256, respectively. Copy them to ++ /usr/lib/hotplug/firmware or /lib/firmware (depending on ++ configuration of firmware hotplug). ++ ++config DVB_BCM3510 ++ tristate "Broadcom BCM3510" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to ++ support this frontend. ++ ++config DVB_LGDT330X ++ tristate "LG Electronics LGDT3302/LGDT3303 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++config DVB_LGDT3305 ++ tristate "LG Electronics LGDT3304 and LGDT3305 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++config DVB_LG2160 ++ tristate "LG Electronics LG216x based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC/MH demodulator module. Say Y when you want ++ to support this frontend. ++ ++config DVB_S5H1409 ++ tristate "Samsung S5H1409 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++config DVB_AU8522 ++ depends on I2C ++ tristate ++ ++config DVB_AU8522_DTV ++ tristate "Auvitek AU8522 based DTV demod" ++ depends on DVB_CORE && I2C ++ select DVB_AU8522 ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when ++ you want to enable DTV demodulation support for this frontend. ++ ++config DVB_AU8522_V4L ++ tristate "Auvitek AU8522 based ATV demod" ++ depends on VIDEO_V4L2 && I2C ++ select DVB_AU8522 ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when ++ you want to enable ATV demodulation support for this frontend. ++ ++config DVB_S5H1411 ++ tristate "Samsung S5H1411 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++comment "ISDB-T (terrestrial) frontends" ++ depends on DVB_CORE ++ ++config DVB_S921 ++ tristate "Sharp S921 frontend" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. ++ Say Y when you want to support this frontend. ++ ++config DVB_DIB8000 ++ tristate "DiBcom 8000MB/MC" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. ++ Say Y when you want to support this frontend. ++ ++config DVB_MB86A20S ++ tristate "Fujitsu mb86a20s" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. ++ Say Y when you want to support this frontend. ++ ++comment "Digital terrestrial only tuners/PLL" ++ depends on DVB_CORE ++ ++config DVB_PLL ++ tristate "Generic I2C PLL based tuners" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ This module drives a number of tuners based on PLL chips with a ++ common I2C interface. Say Y when you want to support these tuners. ++ ++config DVB_TUNER_DIB0070 ++ tristate "DiBcom DiB0070 silicon base-band tuner" ++ depends on I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for the silicon baseband tuner DiB0070 from DiBcom. ++ This device is only used inside a SiP called together with a ++ demodulator for now. ++ ++config DVB_TUNER_DIB0090 ++ tristate "DiBcom DiB0090 silicon base-band tuner" ++ depends on I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for the silicon baseband tuner DiB0090 from DiBcom. ++ This device is only used inside a SiP called together with a ++ demodulator for now. ++ ++comment "SEC control devices for DVB-S" ++ depends on DVB_CORE ++ ++config DVB_LNBP21 ++ tristate "LNBP21/LNBH24 SEC controllers" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An SEC control chips. ++ ++config DVB_LNBP22 ++ tristate "LNBP22 SEC controllers" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ LNB power supply and control voltage ++ regulator chip with step-up converter ++ and I2C interface. ++ Say Y when you want to support this chip. ++ ++config DVB_ISL6405 ++ tristate "ISL6405 SEC controller" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An SEC control chip. ++ ++config DVB_ISL6421 ++ tristate "ISL6421 SEC controller" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An SEC control chip. ++ ++config DVB_ISL6423 ++ tristate "ISL6423 SEC controller" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A SEC controller chip from Intersil ++ ++config DVB_A8293 ++ tristate "Allegro A8293" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ ++config DVB_LGS8GL5 ++ tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DMB-TH tuner module. Say Y when you want to support this frontend. ++ ++config DVB_LGS8GXX ++ tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" ++ depends on DVB_CORE && I2C ++ select FW_LOADER ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DMB-TH tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ATBM8830 ++ tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DMB-TH tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA665x ++ tristate "TDA665x tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for tuner modules based on Philips TDA6650/TDA6651 chips. ++ Say Y when you want to support this chip. ++ ++ Currently supported tuners: ++ * Panasonic ENV57H12D5 (ET-50DT) ++ ++config DVB_IX2505V ++ tristate "Sharp IX2505V silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_IT913X_FE ++ tristate "it913x frontend and it9137 tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. ++ Say Y when you want to support this frontend. ++ ++config DVB_M88RS2000 ++ tristate "M88RS2000 DVB-S demodulator and tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. ++ Say Y when you want to support this frontend. ++ ++config DVB_AF9033 ++ tristate "Afatech AF9033 DVB-T demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ ++comment "Tools to develop new frontends" ++ ++config DVB_DUMMY_FE ++ tristate "Dummy frontend driver" ++ default n ++endmenu +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/m88ds3103.c linux-openelec/drivers/media/dvb-frontends/m88ds3103.c +--- linux-3.14.36/drivers/media/dvb-frontends/m88ds3103.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/m88ds3103.c 2015-07-24 18:03:30.144842002 -0500 +@@ -1,5 +1,5 @@ + /* +- * Montage M88DS3103 demodulator driver ++ * Montage M88DS3103/M88RS6000 demodulator driver + * + * Copyright (C) 2013 Antti Palosaari + * +@@ -159,9 +159,10 @@ + { + int ret, i, j; + u8 buf[83]; ++ + dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len); + +- if (tab_len > 83) { ++ if (tab_len > 86) { + ret = -EINVAL; + goto err; + } +@@ -244,11 +245,12 @@ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, len; + const struct m88ds3103_reg_val *init; +- u8 u8tmp, u8tmp1, u8tmp2; +- u8 buf[2]; +- u16 u16tmp, divide_ratio; +- u32 tuner_frequency, target_mclk, ts_clk; ++ u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */ ++ u8 buf[3]; ++ u16 u16tmp, divide_ratio = 0; ++ u32 tuner_frequency, target_mclk; + s32 s32tmp; ++ + dev_dbg(&priv->i2c->dev, + "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + __func__, c->delivery_system, +@@ -260,6 +262,22 @@ + goto err; + } + ++ /* reset */ ++ ret = m88ds3103_wr_reg(priv, 0x07, 0x80); ++ if (ret) ++ goto err; ++ ++ ret = m88ds3103_wr_reg(priv, 0x07, 0x00); ++ if (ret) ++ goto err; ++ ++ /* Disable demod clock path */ ++ if (priv->chip_id == M88RS6000_CHIP_ID) { ++ ret = m88ds3103_wr_reg(priv, 0x06, 0xe0); ++ if (ret) ++ goto err; ++ } ++ + /* program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); +@@ -271,54 +289,53 @@ + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency); + if (ret) + goto err; ++ } else { ++ /* ++ * Use nominal target frequency as tuner driver does not provide ++ * actual frequency used. Carrier offset calculation is not ++ * valid. ++ */ ++ tuner_frequency = c->frequency; + } + +- /* reset */ +- ret = m88ds3103_wr_reg(priv, 0x07, 0x80); +- if (ret) +- goto err; +- +- ret = m88ds3103_wr_reg(priv, 0x07, 0x00); +- if (ret) +- goto err; +- +- ret = m88ds3103_wr_reg(priv, 0xb2, 0x01); +- if (ret) +- goto err; ++ /* select M88RS6000 demod main mclk and ts mclk from tuner die. */ ++ if (priv->chip_id == M88RS6000_CHIP_ID) { ++ if (c->symbol_rate > 45010000) ++ priv->mclk_khz = 110250; ++ else ++ priv->mclk_khz = 96000; + +- ret = m88ds3103_wr_reg(priv, 0x00, 0x01); +- if (ret) +- goto err; ++ if (c->delivery_system == SYS_DVBS) ++ target_mclk = 96000; ++ else ++ target_mclk = 144000; + +- switch (c->delivery_system) { +- case SYS_DVBS: +- len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals); +- init = m88ds3103_dvbs_init_reg_vals; +- target_mclk = 96000; +- break; +- case SYS_DVBS2: +- len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals); +- init = m88ds3103_dvbs2_init_reg_vals; ++ /* Enable demod clock path */ ++ ret = m88ds3103_wr_reg(priv, 0x06, 0x00); ++ if (ret) ++ goto err; ++ usleep_range(10000, 20000); ++ } else { ++ /* set M88DS3103 mclk and ts mclk. */ ++ priv->mclk_khz = 96000; + + switch (priv->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: +- if (c->symbol_rate < 18000000) +- target_mclk = 96000; +- else +- target_mclk = 144000; ++ target_mclk = priv->cfg->ts_clk; + break; + case M88DS3103_TS_PARALLEL: +- case M88DS3103_TS_PARALLEL_12: +- case M88DS3103_TS_PARALLEL_16: +- case M88DS3103_TS_PARALLEL_19_2: + case M88DS3103_TS_CI: +- if (c->symbol_rate < 18000000) ++ if (c->delivery_system == SYS_DVBS) + target_mclk = 96000; +- else if (c->symbol_rate < 28000000) +- target_mclk = 144000; +- else +- target_mclk = 192000; ++ else { ++ if (c->symbol_rate < 18000000) ++ target_mclk = 96000; ++ else if (c->symbol_rate < 28000000) ++ target_mclk = 144000; ++ else ++ target_mclk = 192000; ++ } + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", +@@ -326,6 +343,55 @@ + ret = -EINVAL; + goto err; + } ++ ++ switch (target_mclk) { ++ case 96000: ++ u8tmp1 = 0x02; /* 0b10 */ ++ u8tmp2 = 0x01; /* 0b01 */ ++ break; ++ case 144000: ++ u8tmp1 = 0x00; /* 0b00 */ ++ u8tmp2 = 0x01; /* 0b01 */ ++ break; ++ case 192000: ++ u8tmp1 = 0x03; /* 0b11 */ ++ u8tmp2 = 0x00; /* 0b00 */ ++ break; ++ } ++ ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0); ++ if (ret) ++ goto err; ++ ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0); ++ if (ret) ++ goto err; ++ } ++ ++ ret = m88ds3103_wr_reg(priv, 0xb2, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = m88ds3103_wr_reg(priv, 0x00, 0x01); ++ if (ret) ++ goto err; ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ if (priv->chip_id == M88RS6000_CHIP_ID) { ++ len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals); ++ init = m88rs6000_dvbs_init_reg_vals; ++ } else { ++ len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals); ++ init = m88ds3103_dvbs_init_reg_vals; ++ } ++ break; ++ case SYS_DVBS2: ++ if (priv->chip_id == M88RS6000_CHIP_ID) { ++ len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals); ++ init = m88rs6000_dvbs2_init_reg_vals; ++ } else { ++ len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals); ++ init = m88ds3103_dvbs2_init_reg_vals; ++ } + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", +@@ -341,37 +407,44 @@ + goto err; + } + +- u8tmp1 = 0; /* silence compiler warning */ ++ if (priv->chip_id == M88RS6000_CHIP_ID) { ++ if ((c->delivery_system == SYS_DVBS2) ++ && ((c->symbol_rate / 1000) <= 5000)) { ++ ret = m88ds3103_wr_reg(priv, 0xc0, 0x04); ++ if (ret) ++ goto err; ++ buf[0] = 0x09; ++ buf[1] = 0x22; ++ buf[2] = 0x88; ++ ret = m88ds3103_wr_regs(priv, 0x8a, buf, 3); ++ if (ret) ++ goto err; ++ } ++ ret = m88ds3103_wr_reg_mask(priv, 0x9d, 0x08, 0x08); ++ if (ret) ++ goto err; ++ ret = m88ds3103_wr_reg(priv, 0xf1, 0x01); ++ if (ret) ++ goto err; ++ ret = m88ds3103_wr_reg_mask(priv, 0x30, 0x80, 0x80); ++ if (ret) ++ goto err; ++ } ++ + switch (priv->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + u8tmp1 = 0x00; +- ts_clk = 0; +- u8tmp = 0x46; ++ u8tmp = 0x06; + break; + case M88DS3103_TS_SERIAL_D7: + u8tmp1 = 0x20; +- ts_clk = 0; +- u8tmp = 0x46; ++ u8tmp = 0x06; + break; + case M88DS3103_TS_PARALLEL: +- ts_clk = 24000; +- u8tmp = 0x42; +- break; +- case M88DS3103_TS_PARALLEL_12: +- ts_clk = 12000; +- u8tmp = 0x42; +- break; +- case M88DS3103_TS_PARALLEL_16: +- ts_clk = 16000; +- u8tmp = 0x42; +- break; +- case M88DS3103_TS_PARALLEL_19_2: +- ts_clk = 19200; +- u8tmp = 0x42; ++ u8tmp = 0x02; + break; + case M88DS3103_TS_CI: +- ts_clk = 6000; +- u8tmp = 0x43; ++ u8tmp = 0x03; + break; + default: + dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__); +@@ -379,6 +452,9 @@ + goto err; + } + ++ if (priv->cfg->ts_clk_pol) ++ u8tmp |= 0x40; ++ + /* TS mode */ + ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp); + if (ret) +@@ -390,21 +466,20 @@ + ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20); + if (ret) + goto err; +- } +- +- if (ts_clk) { +- divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk); +- u8tmp1 = divide_ratio / 2; +- u8tmp2 = DIV_ROUND_UP(divide_ratio, 2); +- } else { +- divide_ratio = 0; + u8tmp1 = 0; + u8tmp2 = 0; ++ break; ++ default: ++ if (priv->cfg->ts_clk) { ++ divide_ratio = DIV_ROUND_UP(target_mclk, priv->cfg->ts_clk); ++ u8tmp1 = divide_ratio / 2; ++ u8tmp2 = DIV_ROUND_UP(divide_ratio, 2); ++ } + } + + dev_dbg(&priv->i2c->dev, + "%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n", +- __func__, target_mclk, ts_clk, divide_ratio); ++ __func__, target_mclk, priv->cfg->ts_clk, divide_ratio); + + u8tmp1--; + u8tmp2--; +@@ -427,41 +502,6 @@ + if (ret) + goto err; + +- switch (target_mclk) { +- case 72000: +- u8tmp1 = 0x00; /* 0b00 */ +- u8tmp2 = 0x03; /* 0b11 */ +- break; +- case 96000: +- u8tmp1 = 0x02; /* 0b10 */ +- u8tmp2 = 0x01; /* 0b01 */ +- break; +- case 115200: +- u8tmp1 = 0x01; /* 0b01 */ +- u8tmp2 = 0x01; /* 0b01 */ +- break; +- case 144000: +- u8tmp1 = 0x00; /* 0b00 */ +- u8tmp2 = 0x01; /* 0b01 */ +- break; +- case 192000: +- u8tmp1 = 0x03; /* 0b11 */ +- u8tmp2 = 0x00; /* 0b00 */ +- break; +- default: +- dev_dbg(&priv->i2c->dev, "%s: invalid target_mclk\n", __func__); +- ret = -EINVAL; +- goto err; +- } +- +- ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0); +- if (ret) +- goto err; +- +- ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0); +- if (ret) +- goto err; +- + if (c->symbol_rate <= 3000000) + u8tmp = 0x20; + else if (c->symbol_rate <= 10000000) +@@ -485,7 +525,7 @@ + if (ret) + goto err; + +- u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, M88DS3103_MCLK_KHZ / 2); ++ u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, priv->mclk_khz / 2); + buf[0] = (u16tmp >> 0) & 0xff; + buf[1] = (u16tmp >> 8) & 0xff; + ret = m88ds3103_wr_regs(priv, 0x61, buf, 2); +@@ -508,7 +548,7 @@ + (tuner_frequency - c->frequency)); + + s32tmp = 0x10000 * (tuner_frequency - c->frequency); +- s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ); ++ s32tmp = DIV_ROUND_CLOSEST(s32tmp, priv->mclk_khz); + if (s32tmp < 0) + s32tmp += 0x10000; + +@@ -539,8 +579,9 @@ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret, len, remaining; + const struct firmware *fw = NULL; +- u8 *fw_file = M88DS3103_FIRMWARE; ++ u8 *fw_file; + u8 u8tmp; ++ + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + /* set cold state by default */ +@@ -559,15 +600,6 @@ + if (ret) + goto err; + +- /* reset */ +- ret = m88ds3103_wr_reg(priv, 0x07, 0x60); +- if (ret) +- goto err; +- +- ret = m88ds3103_wr_reg(priv, 0x07, 0x00); +- if (ret) +- goto err; +- + /* firmware status */ + ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp); + if (ret) +@@ -578,10 +610,23 @@ + if (u8tmp) + goto skip_fw_download; + ++ /* global reset, global diseqc reset, golbal fec reset */ ++ ret = m88ds3103_wr_reg(priv, 0x07, 0xe0); ++ if (ret) ++ goto err; ++ ++ ret = m88ds3103_wr_reg(priv, 0x07, 0x00); ++ if (ret) ++ goto err; ++ + /* cold state - try to download firmware */ + dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, m88ds3103_ops.info.name); + ++ if (priv->chip_id == M88RS6000_CHIP_ID) ++ fw_file = M88RS6000_FIRMWARE; ++ else ++ fw_file = M88DS3103_FIRMWARE; + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); + if (ret) { +@@ -595,7 +640,7 @@ + + ret = m88ds3103_wr_reg(priv, 0xb2, 0x01); + if (ret) +- goto err; ++ goto error_fw_release; + + for (remaining = fw->size; remaining > 0; + remaining -= (priv->cfg->i2c_wr_max - 1)) { +@@ -609,13 +654,13 @@ + dev_err(&priv->i2c->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); +- goto err; ++ goto error_fw_release; + } + } + + ret = m88ds3103_wr_reg(priv, 0xb2, 0x00); + if (ret) +- goto err; ++ goto error_fw_release; + + release_firmware(fw); + fw = NULL; +@@ -641,10 +686,10 @@ + priv->warm = true; + + return 0; +-err: +- if (fw) +- release_firmware(fw); + ++error_fw_release: ++ release_firmware(fw); ++err: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + return ret; + } +@@ -653,12 +698,18 @@ + { + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret; ++ u8 u8tmp; ++ + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + priv->delivery_system = SYS_UNDEFINED; + + /* TS Hi-Z */ +- ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01); ++ if (priv->chip_id == M88RS6000_CHIP_ID) ++ u8tmp = 0x29; ++ else ++ u8tmp = 0x27; ++ ret = m88ds3103_wr_reg_mask(priv, u8tmp, 0x00, 0x01); + if (ret) + goto err; + +@@ -687,6 +738,7 @@ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u8 buf[3]; ++ + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + + if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { +@@ -711,9 +763,6 @@ + case 1: + c->inversion = INVERSION_ON; + break; +- default: +- dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", +- __func__); + } + + switch ((buf[1] >> 5) & 0x07) { +@@ -793,9 +842,6 @@ + case 1: + c->pilot = PILOT_ON; + break; +- default: +- dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n", +- __func__); + } + + switch ((buf[0] >> 6) & 0x07) { +@@ -823,9 +869,6 @@ + case 1: + c->inversion = INVERSION_ON; + break; +- default: +- dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", +- __func__); + } + + switch ((buf[2] >> 0) & 0x03) { +@@ -855,7 +898,7 @@ + goto err; + + c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) * +- M88DS3103_MCLK_KHZ * 1000 / 0x10000; ++ priv->mclk_khz * 1000 / 0x10000; + + return 0; + err: +@@ -871,6 +914,7 @@ + u8 buf[3]; + u16 noise, signal; + u32 noise_tot, signal_tot; ++ + dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + /* reports SNR in resolution of 0.1 dB */ + +@@ -893,7 +937,7 @@ + /* SNR(X) dB = 10 * ln(X) / ln(10) dB */ + tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS); + if (tmp) +- *snr = 100ul * intlog2(tmp) / intlog2(10); ++ *snr = div_u64((u64) 100 * intlog2(tmp), intlog2(10)); + else + *snr = 0; + break; +@@ -922,7 +966,7 @@ + /* SNR(X) dB = 10 * log10(X) dB */ + if (signal > noise) { + tmp = signal / noise; +- *snr = 100ul * intlog10(tmp) / (1 << 24); ++ *snr = div_u64((u64) 100 * intlog10(tmp), (1 << 24)); + } else { + *snr = 0; + } +@@ -940,6 +984,87 @@ + return ret; + } + ++static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct m88ds3103_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ unsigned int utmp; ++ u8 buf[3], u8tmp; ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ ret = m88ds3103_wr_reg(priv, 0xf9, 0x04); ++ if (ret) ++ goto err; ++ ++ ret = m88ds3103_rd_reg(priv, 0xf8, &u8tmp); ++ if (ret) ++ goto err; ++ ++ if (!(u8tmp & 0x10)) { ++ u8tmp |= 0x10; ++ ++ ret = m88ds3103_rd_regs(priv, 0xf6, buf, 2); ++ if (ret) ++ goto err; ++ ++ priv->ber = (buf[1] << 8) | (buf[0] << 0); ++ ++ /* restart counters */ ++ ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp); ++ if (ret) ++ goto err; ++ } ++ break; ++ case SYS_DVBS2: ++ ret = m88ds3103_rd_regs(priv, 0xd5, buf, 3); ++ if (ret) ++ goto err; ++ ++ utmp = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); ++ ++ if (utmp > 3000) { ++ ret = m88ds3103_rd_regs(priv, 0xf7, buf, 2); ++ if (ret) ++ goto err; ++ ++ priv->ber = (buf[1] << 8) | (buf[0] << 0); ++ ++ /* restart counters */ ++ ret = m88ds3103_wr_reg(priv, 0xd1, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = m88ds3103_wr_reg(priv, 0xf9, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = m88ds3103_wr_reg(priv, 0xf9, 0x00); ++ if (ret) ++ goto err; ++ ++ ret = m88ds3103_wr_reg(priv, 0xd1, 0x00); ++ if (ret) ++ goto err; ++ } ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", ++ __func__); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ *ber = priv->ber; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} + + static int m88ds3103_set_tone(struct dvb_frontend *fe, + fe_sec_tone_mode_t fe_sec_tone_mode) +@@ -947,6 +1072,7 @@ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret; + u8 u8tmp, tone, reg_a1_mask; ++ + dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__, + fe_sec_tone_mode); + +@@ -958,7 +1084,7 @@ + switch (fe_sec_tone_mode) { + case SEC_TONE_ON: + tone = 0; +- reg_a1_mask = 0x87; ++ reg_a1_mask = 0x47; + break; + case SEC_TONE_OFF: + tone = 1; +@@ -987,12 +1113,64 @@ + return ret; + } + ++static int m88ds3103_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t fe_sec_voltage) ++{ ++ struct m88ds3103_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 u8tmp; ++ bool voltage_sel, voltage_dis; ++ ++ dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__, ++ fe_sec_voltage); ++ ++ if (!priv->warm) { ++ ret = -EAGAIN; ++ goto err; ++ } ++ ++ switch (fe_sec_voltage) { ++ case SEC_VOLTAGE_18: ++ voltage_sel = true; ++ voltage_dis = false; ++ break; ++ case SEC_VOLTAGE_13: ++ voltage_sel = false; ++ voltage_dis = false; ++ break; ++ case SEC_VOLTAGE_OFF: ++ voltage_sel = false; ++ voltage_dis = true; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n", ++ __func__); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ /* output pin polarity */ ++ voltage_sel ^= priv->cfg->lnb_hv_pol; ++ voltage_dis ^= priv->cfg->lnb_en_pol; ++ ++ u8tmp = voltage_dis << 1 | voltage_sel << 0; ++ ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0x03); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ + static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *diseqc_cmd) + { + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret, i; + u8 u8tmp; ++ + dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__, + diseqc_cmd->msg_len, diseqc_cmd->msg); + +@@ -1064,6 +1242,7 @@ + struct m88ds3103_priv *priv = fe->demodulator_priv; + int ret, i; + u8 u8tmp, burst; ++ + dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, + fe_sec_mini_cmd); + +@@ -1136,6 +1315,7 @@ + static void m88ds3103_release(struct dvb_frontend *fe) + { + struct m88ds3103_priv *priv = fe->demodulator_priv; ++ + i2c_del_mux_adapter(priv->i2c_adapter); + kfree(priv); + } +@@ -1198,18 +1378,22 @@ + priv->i2c = i2c; + mutex_init(&priv->i2c_mutex); + +- ret = m88ds3103_rd_reg(priv, 0x01, &chip_id); ++ /* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */ ++ ret = m88ds3103_rd_reg(priv, 0x00, &chip_id); + if (ret) + goto err; + +- dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); ++ chip_id >>= 1; ++ dev_info(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); + + switch (chip_id) { +- case 0xd0: ++ case M88RS6000_CHIP_ID: ++ case M88DS3103_CHIP_ID: + break; + default: + goto err; + } ++ priv->chip_id = chip_id; + + switch (priv->cfg->clock_out) { + case M88DS3103_CLOCK_OUT_DISABLED: +@@ -1225,6 +1409,11 @@ + goto err; + } + ++ /* 0x29 register is defined differently for m88rs6000. */ ++ /* set internal tuner address to 0x21 */ ++ if (chip_id == M88RS6000_CHIP_ID) ++ u8tmp = 0x00; ++ + ret = m88ds3103_wr_reg(priv, 0x29, u8tmp); + if (ret) + goto err; +@@ -1252,6 +1441,9 @@ + + /* create dvb_frontend */ + memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); ++ if (priv->chip_id == M88RS6000_CHIP_ID) ++ strncpy(priv->fe.ops.info.name, ++ "Montage M88RS6000", sizeof(priv->fe.ops.info.name)); + priv->fe.demodulator_priv = priv; + + return &priv->fe; +@@ -1298,14 +1490,17 @@ + + .read_status = m88ds3103_read_status, + .read_snr = m88ds3103_read_snr, ++ .read_ber = m88ds3103_read_ber, + + .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd, + .diseqc_send_burst = m88ds3103_diseqc_send_burst, + + .set_tone = m88ds3103_set_tone, ++ .set_voltage = m88ds3103_set_voltage, + }; + + MODULE_AUTHOR("Antti Palosaari "); + MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver"); + MODULE_LICENSE("GPL"); + MODULE_FIRMWARE(M88DS3103_FIRMWARE); ++MODULE_FIRMWARE(M88RS6000_FIRMWARE); +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/m88ds3103.h linux-openelec/drivers/media/dvb-frontends/m88ds3103.h +--- linux-3.14.36/drivers/media/dvb-frontends/m88ds3103.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/m88ds3103.h 2015-07-24 18:03:30.148842002 -0500 +@@ -47,14 +47,23 @@ + */ + #define M88DS3103_TS_SERIAL 0 /* TS output pin D0, normal */ + #define M88DS3103_TS_SERIAL_D7 1 /* TS output pin D7 */ +-#define M88DS3103_TS_PARALLEL 2 /* 24 MHz, normal */ +-#define M88DS3103_TS_PARALLEL_12 3 /* 12 MHz */ +-#define M88DS3103_TS_PARALLEL_16 4 /* 16 MHz */ +-#define M88DS3103_TS_PARALLEL_19_2 5 /* 19.2 MHz */ +-#define M88DS3103_TS_CI 6 /* 6 MHz */ ++#define M88DS3103_TS_PARALLEL 2 /* TS Parallel mode */ ++#define M88DS3103_TS_CI 3 /* TS CI Mode */ + u8 ts_mode; + + /* ++ * TS clk in KHz ++ * Default: 0. ++ */ ++ u32 ts_clk; ++ ++ /* ++ * TS clk polarity. ++ * Default: 0. 1-active at falling edge; 0-active at rising edge. ++ */ ++ u8 ts_clk_pol:1; ++ ++ /* + * spectrum inversion + * Default: 0 + */ +@@ -86,6 +95,22 @@ + * Default: none, must set + */ + u8 agc; ++ ++ /* ++ * LNB H/V pin polarity ++ * Default: 0. ++ * 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18. ++ * 0: pin high set to VOLTAGE_18, pin low to set VOLTAGE_13. ++ */ ++ u8 lnb_hv_pol:1; ++ ++ /* ++ * LNB enable pin polarity ++ * Default: 0. ++ * 1: pin high to enable, pin low to disable. ++ * 0: pin high to disable, pin low to enable. ++ */ ++ u8 lnb_en_pol:1; + }; + + /* +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/m88ds3103_priv.h linux-openelec/drivers/media/dvb-frontends/m88ds3103_priv.h +--- linux-3.14.36/drivers/media/dvb-frontends/m88ds3103_priv.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/m88ds3103_priv.h 2015-07-24 18:03:30.148842002 -0500 +@@ -22,9 +22,13 @@ + #include "dvb_math.h" + #include + #include ++#include + + #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" ++#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw" + #define M88DS3103_MCLK_KHZ 96000 ++#define M88RS6000_CHIP_ID 0x74 ++#define M88DS3103_CHIP_ID 0x70 + + struct m88ds3103_priv { + struct i2c_adapter *i2c; +@@ -34,8 +38,13 @@ + struct dvb_frontend fe; + fe_delivery_system_t delivery_system; + fe_status_t fe_status; ++ u32 ber; + bool warm; /* FW running */ + struct i2c_adapter *i2c_adapter; ++ /* auto detect chip id to do different config */ ++ u8 chip_id; ++ /* main mclk is calculated for M88RS6000 dynamically */ ++ u32 mclk_khz; + }; + + struct m88ds3103_reg_val { +@@ -212,4 +221,178 @@ + {0xb8, 0x00}, + }; + ++static const struct m88ds3103_reg_val m88rs6000_dvbs_init_reg_vals[] = { ++ {0x23, 0x07}, ++ {0x08, 0x03}, ++ {0x0c, 0x02}, ++ {0x20, 0x00}, ++ {0x21, 0x54}, ++ {0x25, 0x82}, ++ {0x27, 0x31}, ++ {0x30, 0x08}, ++ {0x31, 0x40}, ++ {0x32, 0x32}, ++ {0x33, 0x35}, ++ {0x35, 0xff}, ++ {0x3a, 0x00}, ++ {0x37, 0x10}, ++ {0x38, 0x10}, ++ {0x39, 0x02}, ++ {0x42, 0x60}, ++ {0x4a, 0x80}, ++ {0x4b, 0x04}, ++ {0x4d, 0x91}, ++ {0x5d, 0xc8}, ++ {0x50, 0x36}, ++ {0x51, 0x36}, ++ {0x52, 0x36}, ++ {0x53, 0x36}, ++ {0x63, 0x0f}, ++ {0x64, 0x30}, ++ {0x65, 0x40}, ++ {0x68, 0x26}, ++ {0x69, 0x4c}, ++ {0x70, 0x20}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x40}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x60}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x80}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0xa0}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x1f}, ++ {0x76, 0x38}, ++ {0x77, 0xa6}, ++ {0x78, 0x0c}, ++ {0x79, 0x80}, ++ {0x7f, 0x14}, ++ {0x7c, 0x00}, ++ {0xae, 0x82}, ++ {0x80, 0x64}, ++ {0x81, 0x66}, ++ {0x82, 0x44}, ++ {0x85, 0x04}, ++ {0xcd, 0xf4}, ++ {0x90, 0x33}, ++ {0xa0, 0x44}, ++ {0xbe, 0x00}, ++ {0xc0, 0x08}, ++ {0xc3, 0x10}, ++ {0xc4, 0x08}, ++ {0xc5, 0xf0}, ++ {0xc6, 0xff}, ++ {0xc7, 0x00}, ++ {0xc8, 0x1a}, ++ {0xc9, 0x80}, ++ {0xe0, 0xf8}, ++ {0xe6, 0x8b}, ++ {0xd0, 0x40}, ++ {0xf8, 0x20}, ++ {0xfa, 0x0f}, ++ {0x00, 0x00}, ++ {0xbd, 0x01}, ++ {0xb8, 0x00}, ++ {0x29, 0x11}, ++}; ++ ++static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = { ++ {0x23, 0x07}, ++ {0x08, 0x07}, ++ {0x0c, 0x02}, ++ {0x20, 0x00}, ++ {0x21, 0x54}, ++ {0x25, 0x82}, ++ {0x27, 0x31}, ++ {0x30, 0x08}, ++ {0x32, 0x32}, ++ {0x33, 0x35}, ++ {0x35, 0xff}, ++ {0x3a, 0x00}, ++ {0x37, 0x10}, ++ {0x38, 0x10}, ++ {0x39, 0x02}, ++ {0x42, 0x60}, ++ {0x4a, 0x80}, ++ {0x4b, 0x04}, ++ {0x4d, 0x91}, ++ {0x5d, 0xc8}, ++ {0x50, 0x36}, ++ {0x51, 0x36}, ++ {0x52, 0x36}, ++ {0x53, 0x36}, ++ {0x63, 0x0f}, ++ {0x64, 0x10}, ++ {0x65, 0x20}, ++ {0x68, 0x46}, ++ {0x69, 0xcd}, ++ {0x70, 0x20}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x40}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x60}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x80}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0xa0}, ++ {0x71, 0x70}, ++ {0x72, 0x04}, ++ {0x73, 0x00}, ++ {0x70, 0x1f}, ++ {0x76, 0x38}, ++ {0x77, 0xa6}, ++ {0x78, 0x0c}, ++ {0x79, 0x80}, ++ {0x7f, 0x14}, ++ {0x85, 0x08}, ++ {0xcd, 0xf4}, ++ {0x90, 0x33}, ++ {0x86, 0x00}, ++ {0x87, 0x0f}, ++ {0x89, 0x00}, ++ {0x8b, 0x44}, ++ {0x8c, 0x66}, ++ {0x9d, 0xc1}, ++ {0x8a, 0x10}, ++ {0xad, 0x40}, ++ {0xa0, 0x44}, ++ {0xbe, 0x00}, ++ {0xc0, 0x08}, ++ {0xc1, 0x10}, ++ {0xc2, 0x08}, ++ {0xc3, 0x10}, ++ {0xc4, 0x08}, ++ {0xc5, 0xf0}, ++ {0xc6, 0xff}, ++ {0xc7, 0x00}, ++ {0xc8, 0x1a}, ++ {0xc9, 0x80}, ++ {0xca, 0x23}, ++ {0xcb, 0x24}, ++ {0xcc, 0xf4}, ++ {0xce, 0x74}, ++ {0x00, 0x00}, ++ {0xbd, 0x01}, ++ {0xb8, 0x00}, ++ {0x29, 0x01}, ++}; + #endif +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/Makefile linux-openelec/drivers/media/dvb-frontends/Makefile +--- linux-3.14.36/drivers/media/dvb-frontends/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/Makefile 2015-07-24 18:03:30.364842002 -0500 +@@ -19,6 +19,10 @@ + obj-$(CONFIG_DVB_CX22700) += cx22700.o + obj-$(CONFIG_DVB_S5H1432) += s5h1432.o + obj-$(CONFIG_DVB_CX24110) += cx24110.o ++ ++# inserted by Custler ++obj-$(CONFIG_DVB_CX24120) += cx24120.o ++ + obj-$(CONFIG_DVB_TDA8083) += tda8083.o + obj-$(CONFIG_DVB_L64781) += l64781.o + obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o +@@ -105,4 +109,7 @@ + obj-$(CONFIG_DVB_RTL2832) += rtl2832.o + obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o + obj-$(CONFIG_DVB_AF9033) += af9033.o +- ++obj-$(CONFIG_DVB_SP2) += sp2.o ++obj-$(CONFIG_DVB_SI2168) += si2168.o ++obj-$(CONFIG_DVB_DVBSKY_M88DS3103) += dvbsky_m88ds3103.o ++obj-$(CONFIG_DVB_DVBSKY_M88DC2800) += dvbsky_m88dc2800.o +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/Makefile.orig linux-openelec/drivers/media/dvb-frontends/Makefile.orig +--- linux-3.14.36/drivers/media/dvb-frontends/Makefile.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/Makefile.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,108 @@ ++# ++# Makefile for the kernel DVB frontend device drivers. ++# ++ ++ccflags-y += -I$(srctree)/drivers/media/dvb-core/ ++ccflags-y += -I$(srctree)/drivers/media/tuners/ ++ ++stb0899-objs := stb0899_drv.o stb0899_algo.o ++stv0900-objs := stv0900_core.o stv0900_sw.o ++drxd-objs := drxd_firm.o drxd_hard.o ++cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o ++drxk-objs := drxk_hard.o ++ ++obj-$(CONFIG_DVB_PLL) += dvb-pll.o ++obj-$(CONFIG_DVB_STV0299) += stv0299.o ++obj-$(CONFIG_DVB_STB0899) += stb0899.o ++obj-$(CONFIG_DVB_STB6100) += stb6100.o ++obj-$(CONFIG_DVB_SP8870) += sp8870.o ++obj-$(CONFIG_DVB_CX22700) += cx22700.o ++obj-$(CONFIG_DVB_S5H1432) += s5h1432.o ++obj-$(CONFIG_DVB_CX24110) += cx24110.o ++obj-$(CONFIG_DVB_TDA8083) += tda8083.o ++obj-$(CONFIG_DVB_L64781) += l64781.o ++obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o ++obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o ++obj-$(CONFIG_DVB_MT312) += mt312.o ++obj-$(CONFIG_DVB_VES1820) += ves1820.o ++obj-$(CONFIG_DVB_VES1X93) += ves1x93.o ++obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o ++obj-$(CONFIG_DVB_SP887X) += sp887x.o ++obj-$(CONFIG_DVB_NXT6000) += nxt6000.o ++obj-$(CONFIG_DVB_MT352) += mt352.o ++obj-$(CONFIG_DVB_ZL10036) += zl10036.o ++obj-$(CONFIG_DVB_ZL10039) += zl10039.o ++obj-$(CONFIG_DVB_ZL10353) += zl10353.o ++obj-$(CONFIG_DVB_CX22702) += cx22702.o ++obj-$(CONFIG_DVB_DRXD) += drxd.o ++obj-$(CONFIG_DVB_TDA10021) += tda10021.o ++obj-$(CONFIG_DVB_TDA10023) += tda10023.o ++obj-$(CONFIG_DVB_STV0297) += stv0297.o ++obj-$(CONFIG_DVB_NXT200X) += nxt200x.o ++obj-$(CONFIG_DVB_OR51211) += or51211.o ++obj-$(CONFIG_DVB_OR51132) += or51132.o ++obj-$(CONFIG_DVB_BCM3510) += bcm3510.o ++obj-$(CONFIG_DVB_S5H1420) += s5h1420.o ++obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o ++obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o ++obj-$(CONFIG_DVB_LG2160) += lg2160.o ++obj-$(CONFIG_DVB_CX24123) += cx24123.o ++obj-$(CONFIG_DVB_LNBP21) += lnbp21.o ++obj-$(CONFIG_DVB_LNBP22) += lnbp22.o ++obj-$(CONFIG_DVB_ISL6405) += isl6405.o ++obj-$(CONFIG_DVB_ISL6421) += isl6421.o ++obj-$(CONFIG_DVB_TDA10086) += tda10086.o ++obj-$(CONFIG_DVB_TDA826X) += tda826x.o ++obj-$(CONFIG_DVB_TDA8261) += tda8261.o ++obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o ++obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o ++obj-$(CONFIG_DVB_TUA6100) += tua6100.o ++obj-$(CONFIG_DVB_S5H1409) += s5h1409.o ++obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o ++obj-$(CONFIG_DVB_AU8522) += au8522_common.o ++obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o ++obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o ++obj-$(CONFIG_DVB_TDA10048) += tda10048.o ++obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o ++obj-$(CONFIG_DVB_S5H1411) += s5h1411.o ++obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o ++obj-$(CONFIG_DVB_TDA665x) += tda665x.o ++obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o ++obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o ++obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o ++obj-$(CONFIG_DVB_AF9013) += af9013.o ++obj-$(CONFIG_DVB_CX24116) += cx24116.o ++obj-$(CONFIG_DVB_CX24117) += cx24117.o ++obj-$(CONFIG_DVB_SI21XX) += si21xx.o ++obj-$(CONFIG_DVB_STV0288) += stv0288.o ++obj-$(CONFIG_DVB_STB6000) += stb6000.o ++obj-$(CONFIG_DVB_S921) += s921.o ++obj-$(CONFIG_DVB_STV6110) += stv6110.o ++obj-$(CONFIG_DVB_STV0900) += stv0900.o ++obj-$(CONFIG_DVB_STV090x) += stv090x.o ++obj-$(CONFIG_DVB_STV6110x) += stv6110x.o ++obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o ++obj-$(CONFIG_DVB_ISL6423) += isl6423.o ++obj-$(CONFIG_DVB_EC100) += ec100.o ++obj-$(CONFIG_DVB_HD29L2) += hd29l2.o ++obj-$(CONFIG_DVB_DS3000) += ds3000.o ++obj-$(CONFIG_DVB_TS2020) += ts2020.o ++obj-$(CONFIG_DVB_MB86A16) += mb86a16.o ++obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o ++obj-$(CONFIG_DVB_IX2505V) += ix2505v.o ++obj-$(CONFIG_DVB_STV0367) += stv0367.o ++obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o ++obj-$(CONFIG_DVB_DRXK) += drxk.o ++obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o ++obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o ++obj-$(CONFIG_DVB_A8293) += a8293.o ++obj-$(CONFIG_DVB_TDA10071) += tda10071.o ++obj-$(CONFIG_DVB_RTL2830) += rtl2830.o ++obj-$(CONFIG_DVB_RTL2832) += rtl2832.o ++obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o ++obj-$(CONFIG_DVB_AF9033) += af9033.o ++ +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/si2168.c linux-openelec/drivers/media/dvb-frontends/si2168.c +--- linux-3.14.36/drivers/media/dvb-frontends/si2168.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/si2168.c 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,756 @@ ++/* ++ * Silicon Labs Si2168 DVB-T/T2/C demodulator driver ++ * ++ * Copyright (C) 2014 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include "si2168_priv.h" ++ ++static const struct dvb_frontend_ops si2168_ops; ++ ++/* execute firmware command */ ++static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) ++{ ++ int ret; ++ unsigned long timeout; ++ ++ mutex_lock(&s->i2c_mutex); ++ ++ if (cmd->wlen) { ++ /* write cmd and args for firmware */ ++ ret = i2c_master_send(s->client, cmd->args, cmd->wlen); ++ if (ret < 0) { ++ goto err_mutex_unlock; ++ } else if (ret != cmd->wlen) { ++ ret = -EREMOTEIO; ++ goto err_mutex_unlock; ++ } ++ } ++ ++ if (cmd->rlen) { ++ /* wait cmd execution terminate */ ++ #define TIMEOUT 50 ++ timeout = jiffies + msecs_to_jiffies(TIMEOUT); ++ while (!time_after(jiffies, timeout)) { ++ ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); ++ if (ret < 0) { ++ goto err_mutex_unlock; ++ } else if (ret != cmd->rlen) { ++ ret = -EREMOTEIO; ++ goto err_mutex_unlock; ++ } ++ ++ /* firmware ready? */ ++ if ((cmd->args[0] >> 7) & 0x01) ++ break; ++ } ++ ++ dev_dbg(&s->client->dev, "cmd execution took %d ms\n", ++ jiffies_to_msecs(jiffies) - ++ (jiffies_to_msecs(timeout) - TIMEOUT)); ++ ++ if (!((cmd->args[0] >> 7) & 0x01)) { ++ ret = -ETIMEDOUT; ++ goto err_mutex_unlock; ++ } ++ } ++ ++ ret = 0; ++ ++err_mutex_unlock: ++ mutex_unlock(&s->i2c_mutex); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct si2168 *s = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ struct si2168_cmd cmd; ++ ++ *status = 0; ++ ++ if (!s->active) { ++ ret = -EAGAIN; ++ goto err; ++ } ++ ++ switch (c->delivery_system) { ++ case SYS_DVBT: ++ memcpy(cmd.args, "\xa0\x01", 2); ++ cmd.wlen = 2; ++ cmd.rlen = 13; ++ break; ++ case SYS_DVBC_ANNEX_A: ++ memcpy(cmd.args, "\x90\x01", 2); ++ cmd.wlen = 2; ++ cmd.rlen = 9; ++ break; ++ case SYS_DVBT2: ++ memcpy(cmd.args, "\x50\x01", 2); ++ cmd.wlen = 2; ++ cmd.rlen = 14; ++ break; ++ default: ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ /* ++ * Possible values seen, in order from strong signal to weak: ++ * 16 0001 0110 full lock ++ * 1e 0001 1110 partial lock ++ * 1a 0001 1010 partial lock ++ * 18 0001 1000 no lock ++ * ++ * [b3:b1] lock bits ++ * [b4] statistics ready? Set in a few secs after lock is gained. ++ */ ++ ++ switch ((cmd.args[2] >> 1) & 0x03) { ++ case 0x01: ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ break; ++ case 0x03: ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | ++ FE_HAS_SYNC | FE_HAS_LOCK; ++ break; ++ } ++ ++ s->fe_status = *status; ++ ++ if (*status & FE_HAS_LOCK) { ++ c->cnr.len = 1; ++ c->cnr.stat[0].scale = FE_SCALE_DECIBEL; ++ c->cnr.stat[0].svalue = cmd.args[3] * 1000 / 4; ++ } else { ++ c->cnr.len = 1; ++ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; ++ } ++ ++ dev_dbg(&s->client->dev, "status=%02x args=%*ph\n", ++ *status, cmd.rlen, cmd.args); ++ ++ return 0; ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2168_set_frontend(struct dvb_frontend *fe) ++{ ++ struct si2168 *s = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ struct si2168_cmd cmd; ++ u8 bandwidth, delivery_system; ++ ++ dev_dbg(&s->client->dev, ++ "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u, stream_id=%d\n", ++ c->delivery_system, c->modulation, ++ c->frequency, c->bandwidth_hz, c->symbol_rate, ++ c->inversion, c->stream_id); ++ ++ if (!s->active) { ++ ret = -EAGAIN; ++ goto err; ++ } ++ ++ switch (c->delivery_system) { ++ case SYS_DVBT: ++ delivery_system = 0x20; ++ break; ++ case SYS_DVBC_ANNEX_A: ++ delivery_system = 0x30; ++ break; ++ case SYS_DVBT2: ++ delivery_system = 0x70; ++ break; ++ default: ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (c->bandwidth_hz <= 5000000) ++ bandwidth = 0x05; ++ else if (c->bandwidth_hz <= 6000000) ++ bandwidth = 0x06; ++ else if (c->bandwidth_hz <= 7000000) ++ bandwidth = 0x07; ++ else if (c->bandwidth_hz <= 8000000) ++ bandwidth = 0x08; ++ else if (c->bandwidth_hz <= 9000000) ++ bandwidth = 0x09; ++ else if (c->bandwidth_hz <= 10000000) ++ bandwidth = 0x0a; ++ else ++ bandwidth = 0x0f; ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) { ++ ret = fe->ops.tuner_ops.set_params(fe); ++ if (ret) ++ goto err; ++ } ++ ++ memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5); ++ cmd.wlen = 5; ++ cmd.rlen = 5; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ /* that has no big effect */ ++ if (c->delivery_system == SYS_DVBT) ++ memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6); ++ else if (c->delivery_system == SYS_DVBC_ANNEX_A) ++ memcpy(cmd.args, "\x89\x21\x06\x11\x89\xf0", 6); ++ else if (c->delivery_system == SYS_DVBT2) ++ memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 3; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ if (c->delivery_system == SYS_DVBT2) { ++ /* select PLP */ ++ cmd.args[0] = 0x52; ++ cmd.args[1] = c->stream_id & 0xff; ++ cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1; ++ cmd.wlen = 3; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ } ++ ++ memcpy(cmd.args, "\x51\x03", 2); ++ cmd.wlen = 2; ++ cmd.rlen = 12; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x12\x08\x04", 3); ++ cmd.wlen = 3; ++ cmd.rlen = 3; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6); ++ cmd.args[4] = delivery_system | bandwidth; ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ /* set DVB-C symbol rate */ ++ if (c->delivery_system == SYS_DVBC_ANNEX_A) { ++ memcpy(cmd.args, "\x14\x00\x02\x11", 4); ++ cmd.args[4] = (c->symbol_rate / 1000) & 0xff; ++ cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff; ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ } ++ ++ memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x08", 6); ++ cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10; ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x05", 6); ++ cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10; ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x00", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x85", 1); ++ cmd.wlen = 1; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ s->delivery_system = c->delivery_system; ++ ++ return 0; ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2168_init(struct dvb_frontend *fe) ++{ ++ struct si2168 *s = fe->demodulator_priv; ++ int ret, len, remaining; ++ const struct firmware *fw = NULL; ++ u8 *fw_file; ++ const unsigned int i2c_wr_max = 8; ++ struct si2168_cmd cmd; ++ unsigned int chip_id; ++ ++ dev_dbg(&s->client->dev, "\n"); ++ ++ /* initialize */ ++ memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13); ++ cmd.wlen = 13; ++ cmd.rlen = 0; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ if (s->fw_loaded) { ++ /* resume */ ++ memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8); ++ cmd.wlen = 8; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ memcpy(cmd.args, "\x85", 1); ++ cmd.wlen = 1; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ goto warm; ++ } ++ ++ /* power up */ ++ memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8); ++ cmd.wlen = 8; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ /* query chip revision */ ++ memcpy(cmd.args, "\x02", 1); ++ cmd.wlen = 1; ++ cmd.rlen = 13; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | ++ cmd.args[4] << 0; ++ ++ #define SI2168_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) ++ #define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) ++ #define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) ++ ++ switch (chip_id) { ++ case SI2168_A20: ++ fw_file = SI2168_A20_FIRMWARE; ++ break; ++ case SI2168_A30: ++ fw_file = SI2168_A30_FIRMWARE; ++ break; ++ case SI2168_B40: ++ fw_file = SI2168_B40_FIRMWARE; ++ break; ++ default: ++ dev_err(&s->client->dev, ++ "unknown chip version Si21%d-%c%c%c\n", ++ cmd.args[2], cmd.args[1], ++ cmd.args[3], cmd.args[4]); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ /* cold state - try to download firmware */ ++ dev_info(&s->client->dev, "found a '%s' in cold state\n", ++ si2168_ops.info.name); ++ ++ /* request the firmware, this will block and timeout */ ++ ret = request_firmware(&fw, fw_file, &s->client->dev); ++ if (ret) { ++ /* fallback mechanism to handle old name for Si2168 B40 fw */ ++ if (chip_id == SI2168_B40) { ++ fw_file = SI2168_B40_FIRMWARE_FALLBACK; ++ ret = request_firmware(&fw, fw_file, &s->client->dev); ++ } ++ ++ if (ret == 0) { ++ dev_notice(&s->client->dev, ++ "please install firmware file '%s'\n", ++ SI2168_B40_FIRMWARE); ++ } else { ++ dev_err(&s->client->dev, ++ "firmware file '%s' not found\n", ++ fw_file); ++ goto error_fw_release; ++ } ++ } ++ ++ dev_info(&s->client->dev, "downloading firmware from file '%s'\n", ++ fw_file); ++ ++ if ((fw->size % 17 == 0) && (fw->data[0] > 5)) { ++ /* firmware is in the new format */ ++ for (remaining = fw->size; remaining > 0; remaining -= 17) { ++ len = fw->data[fw->size - remaining]; ++ memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); ++ cmd.wlen = len; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) { ++ dev_err(&s->client->dev, ++ "firmware download failed=%d\n", ++ ret); ++ goto error_fw_release; ++ } ++ } ++ } else { ++ /* firmware is in the old format */ ++ for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) { ++ len = remaining; ++ if (len > i2c_wr_max) ++ len = i2c_wr_max; ++ ++ memcpy(cmd.args, &fw->data[fw->size - remaining], len); ++ cmd.wlen = len; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) { ++ dev_err(&s->client->dev, ++ "firmware download failed=%d\n", ++ ret); ++ goto error_fw_release; ++ } ++ } ++ } ++ ++ release_firmware(fw); ++ fw = NULL; ++ ++ memcpy(cmd.args, "\x01\x01", 2); ++ cmd.wlen = 2; ++ cmd.rlen = 1; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ /* query firmware version */ ++ memcpy(cmd.args, "\x11", 1); ++ cmd.wlen = 1; ++ cmd.rlen = 10; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ dev_dbg(&s->client->dev, "firmware version: %c.%c.%d\n", ++ cmd.args[6], cmd.args[7], cmd.args[8]); ++ ++ /* set ts mode */ ++ memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); ++ cmd.args[4] |= s->ts_mode; ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ s->fw_loaded = true; ++ ++ dev_info(&s->client->dev, "found a '%s' in warm state\n", ++ si2168_ops.info.name); ++warm: ++ s->active = true; ++ ++ return 0; ++ ++error_fw_release: ++ release_firmware(fw); ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2168_sleep(struct dvb_frontend *fe) ++{ ++ struct si2168 *s = fe->demodulator_priv; ++ int ret; ++ struct si2168_cmd cmd; ++ ++ dev_dbg(&s->client->dev, "\n"); ++ ++ s->active = false; ++ ++ memcpy(cmd.args, "\x13", 1); ++ cmd.wlen = 1; ++ cmd.rlen = 0; ++ ret = si2168_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2168_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ s->min_delay_ms = 900; ++ ++ return 0; ++} ++ ++/* ++ * I2C gate logic ++ * We must use unlocked i2c_transfer() here because I2C lock is already taken ++ * by tuner driver. ++ */ ++static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) ++{ ++ struct si2168 *s = mux_priv; ++ int ret; ++ struct i2c_msg gate_open_msg = { ++ .addr = s->client->addr, ++ .flags = 0, ++ .len = 3, ++ .buf = "\xc0\x0d\x01", ++ }; ++ ++ mutex_lock(&s->i2c_mutex); ++ ++ /* open tuner I2C gate */ ++ ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1); ++ if (ret != 1) { ++ dev_warn(&s->client->dev, "i2c write failed=%d\n", ret); ++ if (ret >= 0) ++ ret = -EREMOTEIO; ++ } else { ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) ++{ ++ struct si2168 *s = mux_priv; ++ int ret; ++ struct i2c_msg gate_close_msg = { ++ .addr = s->client->addr, ++ .flags = 0, ++ .len = 3, ++ .buf = "\xc0\x0d\x00", ++ }; ++ ++ /* close tuner I2C gate */ ++ ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1); ++ if (ret != 1) { ++ dev_warn(&s->client->dev, "i2c write failed=%d\n", ret); ++ if (ret >= 0) ++ ret = -EREMOTEIO; ++ } else { ++ ret = 0; ++ } ++ ++ mutex_unlock(&s->i2c_mutex); ++ ++ return ret; ++} ++ ++static const struct dvb_frontend_ops si2168_ops = { ++ .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, ++ .info = { ++ .name = "Silicon Labs Si2168", ++ .caps = FE_CAN_FEC_1_2 | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | ++ FE_CAN_QAM_256 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_MUTE_TS | ++ FE_CAN_2G_MODULATION | ++ FE_CAN_MULTISTREAM ++ }, ++ ++ .get_tune_settings = si2168_get_tune_settings, ++ ++ .init = si2168_init, ++ .sleep = si2168_sleep, ++ ++ .set_frontend = si2168_set_frontend, ++ ++ .read_status = si2168_read_status, ++}; ++ ++static int si2168_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct si2168_config *config = client->dev.platform_data; ++ struct si2168 *s; ++ int ret; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ s = kzalloc(sizeof(struct si2168), GFP_KERNEL); ++ if (!s) { ++ ret = -ENOMEM; ++ dev_err(&client->dev, "kzalloc() failed\n"); ++ goto err; ++ } ++ ++ s->client = client; ++ mutex_init(&s->i2c_mutex); ++ ++ /* create mux i2c adapter for tuner */ ++ s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s, ++ 0, 0, 0, si2168_select, si2168_deselect); ++ if (s->adapter == NULL) { ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); ++ s->fe.demodulator_priv = s; ++ ++ *config->i2c_adapter = s->adapter; ++ *config->fe = &s->fe; ++ s->ts_mode = config->ts_mode; ++ s->ts_clock_inv = config->ts_clock_inv; ++ s->fw_loaded = false; ++ ++ i2c_set_clientdata(client, s); ++ ++ dev_info(&s->client->dev, ++ "Silicon Labs Si2168 successfully attached\n"); ++ return 0; ++err: ++ kfree(s); ++ dev_dbg(&client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2168_remove(struct i2c_client *client) ++{ ++ struct si2168 *s = i2c_get_clientdata(client); ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ i2c_del_mux_adapter(s->adapter); ++ ++ s->fe.ops.release = NULL; ++ s->fe.demodulator_priv = NULL; ++ ++ kfree(s); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id si2168_id[] = { ++ {"si2168", 0}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, si2168_id); ++ ++static struct i2c_driver si2168_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "si2168", ++ }, ++ .probe = si2168_probe, ++ .remove = si2168_remove, ++ .id_table = si2168_id, ++}; ++ ++module_i2c_driver(si2168_driver); ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE(SI2168_A20_FIRMWARE); ++MODULE_FIRMWARE(SI2168_A30_FIRMWARE); ++MODULE_FIRMWARE(SI2168_B40_FIRMWARE); +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/si2168.h linux-openelec/drivers/media/dvb-frontends/si2168.h +--- linux-3.14.36/drivers/media/dvb-frontends/si2168.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/si2168.h 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,49 @@ ++/* ++ * Silicon Labs Si2168 DVB-T/T2/C demodulator driver ++ * ++ * Copyright (C) 2014 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef SI2168_H ++#define SI2168_H ++ ++#include ++/* ++ * I2C address ++ * 0x64 ++ */ ++struct si2168_config { ++ /* ++ * frontend ++ * returned by driver ++ */ ++ struct dvb_frontend **fe; ++ ++ /* ++ * tuner I2C adapter ++ * returned by driver ++ */ ++ struct i2c_adapter **i2c_adapter; ++ ++ /* TS mode */ ++ u8 ts_mode; ++ ++ /* TS clock inverted */ ++ bool ts_clock_inv; ++ ++}; ++ ++#define SI2168_TS_PARALLEL 0x06 ++#define SI2168_TS_SERIAL 0x03 ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/si2168_priv.h linux-openelec/drivers/media/dvb-frontends/si2168_priv.h +--- linux-3.14.36/drivers/media/dvb-frontends/si2168_priv.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/si2168_priv.h 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,52 @@ ++/* ++ * Silicon Labs Si2168 DVB-T/T2/C demodulator driver ++ * ++ * Copyright (C) 2014 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef SI2168_PRIV_H ++#define SI2168_PRIV_H ++ ++#include "si2168.h" ++#include "dvb_frontend.h" ++#include ++#include ++ ++#define SI2168_A20_FIRMWARE "dvb-demod-si2168-a20-01.fw" ++#define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw" ++#define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw" ++#define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-02.fw" ++ ++/* state struct */ ++struct si2168 { ++ struct i2c_client *client; ++ struct i2c_adapter *adapter; ++ struct mutex i2c_mutex; ++ struct dvb_frontend fe; ++ fe_delivery_system_t delivery_system; ++ fe_status_t fe_status; ++ bool active; ++ bool fw_loaded; ++ u8 ts_mode; ++ bool ts_clock_inv; ++}; ++ ++/* firmare command struct */ ++#define SI2168_ARGLEN 30 ++struct si2168_cmd { ++ u8 args[SI2168_ARGLEN]; ++ unsigned wlen; ++ unsigned rlen; ++}; ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/sp2.c linux-openelec/drivers/media/dvb-frontends/sp2.c +--- linux-3.14.36/drivers/media/dvb-frontends/sp2.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/sp2.c 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,444 @@ ++/* ++ * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver ++ * ++ * Copyright (C) 2014 Olli Salonen ++ * ++ * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual ++ * DVB-S2 CI card (cimax2) with following copyrights: ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include "sp2_priv.h" ++ ++static int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) ++{ ++ int ret; ++ struct i2c_client *client = s->client; ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msg[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .buf = ®, ++ .len = 1 ++ }, { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .buf = buf, ++ .len = len ++ } ++ }; ++ ++ ret = i2c_transfer(adap, msg, 2); ++ ++ if (ret != 2) { ++ dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n", ++ reg, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EIO; ++ } ++ ++ dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n", ++ client->addr, reg, buf[0]); ++ ++ return 0; ++} ++ ++static int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) ++{ ++ int ret; ++ u8 buffer[35]; ++ struct i2c_client *client = s->client; ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .buf = &buffer[0], ++ .len = len + 1 ++ }; ++ ++ if ((len + 1) > sizeof(buffer)) { ++ dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n", ++ reg, len); ++ return -EINVAL; ++ } ++ ++ buffer[0] = reg; ++ memcpy(&buffer[1], buf, len); ++ ++ ret = i2c_transfer(adap, &msg, 1); ++ ++ if (ret != 1) { ++ dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n", ++ reg, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EIO; ++ } ++ ++ dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %*ph\n", ++ client->addr, reg, len, buf); ++ ++ return 0; ++} ++ ++static int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs, ++ u8 read, int addr, u8 data) ++{ ++ struct sp2 *s = en50221->data; ++ u8 store; ++ int mem, ret; ++ int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ /* ++ * change module access type between IO space and attribute memory ++ * when needed ++ */ ++ if (s->module_access_type != acs) { ++ ret = sp2_read_i2c(s, 0x00, &store, 1); ++ ++ if (ret) ++ return ret; ++ ++ store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0); ++ store |= acs; ++ ++ ret = sp2_write_i2c(s, 0x00, &store, 1); ++ if (ret) ++ return ret; ++ } ++ ++ s->module_access_type = acs; ++ ++ /* implementation of ci_op_cam is device specific */ ++ if (ci_op_cam) { ++ ret = ci_op_cam(s->priv, read, addr, data, &mem); ++ } else { ++ dev_err(&s->client->dev, "callback not defined"); ++ return -EINVAL; ++ } ++ ++ if (ret) ++ return ret; ++ ++ dev_dbg(&s->client->dev, "%s: slot=%d, addr=0x%04x, %s, data=%x", ++ (read) ? "read" : "write", slot, addr, ++ (acs == SP2_CI_ATTR_ACS) ? "attr" : "io", ++ (read) ? mem : data); ++ ++ if (read) ++ return mem; ++ else ++ return 0; ++ ++} ++ ++int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr) ++{ ++ return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, ++ SP2_CI_RD, addr, 0); ++} ++ ++int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr, u8 data) ++{ ++ return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, ++ SP2_CI_WR, addr, data); ++} ++ ++int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221, ++ int slot, u8 addr) ++{ ++ return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, ++ SP2_CI_RD, addr, 0); ++} ++ ++int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221, ++ int slot, u8 addr, u8 data) ++{ ++ return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, ++ SP2_CI_WR, addr, data); ++} ++ ++int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct sp2 *s = en50221->data; ++ u8 buf; ++ int ret; ++ ++ dev_dbg(&s->client->dev, "slot: %d\n", slot); ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ /* RST on */ ++ buf = SP2_MOD_CTL_RST; ++ ret = sp2_write_i2c(s, 0x00, &buf, 1); ++ ++ if (ret) ++ return ret; ++ ++ usleep_range(500, 600); ++ ++ /* RST off */ ++ buf = 0x00; ++ ret = sp2_write_i2c(s, 0x00, &buf, 1); ++ ++ if (ret) ++ return ret; ++ ++ msleep(1000); ++ ++ return 0; ++} ++ ++int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct sp2 *s = en50221->data; ++ ++ dev_dbg(&s->client->dev, "slot:%d\n", slot); ++ ++ /* not implemented */ ++ return 0; ++} ++ ++int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct sp2 *s = en50221->data; ++ u8 buf; ++ ++ dev_dbg(&s->client->dev, "slot:%d\n", slot); ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ sp2_read_i2c(s, 0x00, &buf, 1); ++ ++ /* disable bypass and enable TS */ ++ buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN); ++ return sp2_write_i2c(s, 0, &buf, 1); ++} ++ ++int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221, ++ int slot, int open) ++{ ++ struct sp2 *s = en50221->data; ++ u8 buf[2]; ++ int ret; ++ ++ dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open); ++ ++ /* ++ * CAM module INSERT/REMOVE processing. Slow operation because of i2c ++ * transfers. Throttle read to one per sec. ++ */ ++ if (time_after(jiffies, s->next_status_checked_time)) { ++ ret = sp2_read_i2c(s, 0x00, buf, 1); ++ s->next_status_checked_time = jiffies + msecs_to_jiffies(1000); ++ ++ if (ret) ++ return 0; ++ ++ if (buf[0] & SP2_MOD_CTL_DET) ++ s->status = DVB_CA_EN50221_POLL_CAM_PRESENT | ++ DVB_CA_EN50221_POLL_CAM_READY; ++ else ++ s->status = 0; ++ } ++ ++ return s->status; ++} ++ ++static int sp2_init(struct sp2 *s) ++{ ++ int ret = 0; ++ u8 buf; ++ u8 cimax_init[34] = { ++ 0x00, /* module A control*/ ++ 0x00, /* auto select mask high A */ ++ 0x00, /* auto select mask low A */ ++ 0x00, /* auto select pattern high A */ ++ 0x00, /* auto select pattern low A */ ++ 0x44, /* memory access time A, 600 ns */ ++ 0x00, /* invert input A */ ++ 0x00, /* RFU */ ++ 0x00, /* RFU */ ++ 0x00, /* module B control*/ ++ 0x00, /* auto select mask high B */ ++ 0x00, /* auto select mask low B */ ++ 0x00, /* auto select pattern high B */ ++ 0x00, /* auto select pattern low B */ ++ 0x44, /* memory access time B, 600 ns */ ++ 0x00, /* invert input B */ ++ 0x00, /* RFU */ ++ 0x00, /* RFU */ ++ 0x00, /* auto select mask high Ext */ ++ 0x00, /* auto select mask low Ext */ ++ 0x00, /* auto select pattern high Ext */ ++ 0x00, /* auto select pattern low Ext */ ++ 0x00, /* RFU */ ++ 0x02, /* destination - module A */ ++ 0x01, /* power control reg, VCC power on */ ++ 0x00, /* RFU */ ++ 0x00, /* int status read only */ ++ 0x00, /* Interrupt Mask Register */ ++ 0x05, /* EXTINT=active-high, INT=push-pull */ ++ 0x00, /* USCG1 */ ++ 0x04, /* ack active low */ ++ 0x00, /* LOCK = 0 */ ++ 0x22, /* unknown */ ++ 0x00, /* synchronization? */ ++ }; ++ ++ dev_dbg(&s->client->dev, "\n"); ++ ++ s->ca.owner = THIS_MODULE; ++ s->ca.read_attribute_mem = sp2_ci_read_attribute_mem; ++ s->ca.write_attribute_mem = sp2_ci_write_attribute_mem; ++ s->ca.read_cam_control = sp2_ci_read_cam_control; ++ s->ca.write_cam_control = sp2_ci_write_cam_control; ++ s->ca.slot_reset = sp2_ci_slot_reset; ++ s->ca.slot_shutdown = sp2_ci_slot_shutdown; ++ s->ca.slot_ts_enable = sp2_ci_slot_ts_enable; ++ s->ca.poll_slot_status = sp2_ci_poll_slot_status; ++ s->ca.data = s; ++ s->module_access_type = 0; ++ ++ /* initialize all regs */ ++ ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34); ++ if (ret) ++ goto err; ++ ++ /* lock registers */ ++ buf = 1; ++ ret = sp2_write_i2c(s, 0x1f, &buf, 1); ++ if (ret) ++ goto err; ++ ++ /* power on slots */ ++ ret = sp2_write_i2c(s, 0x18, &buf, 1); ++ if (ret) ++ goto err; ++ ++ ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1); ++ if (ret) ++ goto err; ++ ++ return 0; ++ ++err: ++ dev_dbg(&s->client->dev, "init failed=%d\n", ret); ++ return ret; ++} ++ ++static int sp2_exit(struct i2c_client *client) ++{ ++ struct sp2 *s; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ if (client == NULL) ++ return 0; ++ ++ s = i2c_get_clientdata(client); ++ if (s == NULL) ++ return 0; ++ ++ if (s->ca.data == NULL) ++ return 0; ++ ++ dvb_ca_en50221_release(&s->ca); ++ ++ return 0; ++} ++ ++static int sp2_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct sp2_config *cfg = client->dev.platform_data; ++ struct sp2 *s; ++ int ret; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ s = kzalloc(sizeof(struct sp2), GFP_KERNEL); ++ if (!s) { ++ ret = -ENOMEM; ++ dev_err(&client->dev, "kzalloc() failed\n"); ++ goto err; ++ } ++ ++ s->client = client; ++ s->dvb_adap = cfg->dvb_adap; ++ s->priv = cfg->priv; ++ s->ci_control = cfg->ci_control; ++ ++ i2c_set_clientdata(client, s); ++ ++ ret = sp2_init(s); ++ if (ret) ++ goto err; ++ ++ dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n"); ++ return 0; ++err: ++ dev_dbg(&client->dev, "init failed=%d\n", ret); ++ kfree(s); ++ ++ return ret; ++} ++ ++static int sp2_remove(struct i2c_client *client) ++{ ++ struct sp2 *s = i2c_get_clientdata(client); ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ sp2_exit(client); ++ if (s != NULL) ++ kfree(s); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id sp2_id[] = { ++ {"sp2", 0}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, sp2_id); ++ ++static struct i2c_driver sp2_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "sp2", ++ }, ++ .probe = sp2_probe, ++ .remove = sp2_remove, ++ .id_table = sp2_id, ++}; ++ ++module_i2c_driver(sp2_driver); ++ ++MODULE_DESCRIPTION("CIMaX SP2/HF CI driver"); ++MODULE_AUTHOR("Olli Salonen "); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/sp2.h linux-openelec/drivers/media/dvb-frontends/sp2.h +--- linux-3.14.36/drivers/media/dvb-frontends/sp2.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/sp2.h 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,53 @@ ++/* ++ * CIMaX SP2/HF CI driver ++ * ++ * Copyright (C) 2014 Olli Salonen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef SP2_H ++#define SP2_H ++ ++#include ++#include "dvb_ca_en50221.h" ++ ++/* ++ * I2C address ++ * 0x40 (port 0) ++ * 0x41 (port 1) ++ */ ++struct sp2_config { ++ /* dvb_adapter to attach the ci to */ ++ struct dvb_adapter *dvb_adap; ++ ++ /* function ci_control handles the device specific ci ops */ ++ void *ci_control; ++ ++ /* priv is passed back to function ci_control */ ++ void *priv; ++}; ++ ++extern int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr); ++extern int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr, u8 data); ++extern int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221, ++ int slot, u8 addr); ++extern int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221, ++ int slot, u8 addr, u8 data); ++extern int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot); ++extern int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot); ++extern int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot); ++extern int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221, ++ int slot, int open); ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/sp2_priv.h linux-openelec/drivers/media/dvb-frontends/sp2_priv.h +--- linux-3.14.36/drivers/media/dvb-frontends/sp2_priv.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/sp2_priv.h 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,50 @@ ++/* ++ * CIMaX SP2/HF CI driver ++ * ++ * Copyright (C) 2014 Olli Salonen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef SP2_PRIV_H ++#define SP2_PRIV_H ++ ++#include "sp2.h" ++#include "dvb_frontend.h" ++ ++/* state struct */ ++struct sp2 { ++ int status; ++ struct i2c_client *client; ++ struct dvb_adapter *dvb_adap; ++ struct dvb_ca_en50221 ca; ++ int module_access_type; ++ unsigned long next_status_checked_time; ++ void *priv; ++ void *ci_control; ++}; ++ ++#define SP2_CI_ATTR_ACS 0x00 ++#define SP2_CI_IO_ACS 0x04 ++#define SP2_CI_WR 0 ++#define SP2_CI_RD 1 ++ ++/* Module control register (0x00 module A, 0x09 module B) bits */ ++#define SP2_MOD_CTL_DET 0x01 ++#define SP2_MOD_CTL_AUTO 0x02 ++#define SP2_MOD_CTL_ACS0 0x04 ++#define SP2_MOD_CTL_ACS1 0x08 ++#define SP2_MOD_CTL_HAD 0x10 ++#define SP2_MOD_CTL_TSIEN 0x20 ++#define SP2_MOD_CTL_TSOEN 0x40 ++#define SP2_MOD_CTL_RST 0x80 ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/stb0899_algo.c linux-openelec/drivers/media/dvb-frontends/stb0899_algo.c +--- linux-3.14.36/drivers/media/dvb-frontends/stb0899_algo.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/stb0899_algo.c 2015-07-24 18:03:30.132842002 -0500 +@@ -206,7 +206,6 @@ + static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state) + { + struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; + + short int derot_step, derot_freq = 0, derot_limit, next_loop = 3; + int index = 0; +@@ -216,10 +215,9 @@ + + /* timing loop computation & symbol rate optimisation */ + derot_limit = (internal->sub_range / 2L) / internal->mclk; +- derot_step = (params->srate / 2L) / internal->mclk; ++ derot_step = internal->derot_step * 4; /* dertot_step = decreasing delta */ + + while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) { +- index++; + derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */ + + if (abs(derot_freq) > derot_limit) +@@ -230,6 +228,7 @@ + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + } ++ index++; + internal->direction = -internal->direction; /* Change zigzag direction */ + } + +@@ -278,14 +277,18 @@ + { + struct stb0899_internal *internal = &state->internal; + +- short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3; ++ short int derot_freq = 0, last_derot_freq = 0, derot_limit, derot_step, next_loop = 3; + int index = 0; ++ int base_freq; + u8 cfr[2]; + u8 reg; + + internal->status = NOCARRIER; + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_freq = internal->derot_freq; ++ derot_step = internal->derot_step * 2; ++ last_derot_freq = internal->derot_freq; ++ base_freq = internal->derot_freq; + + reg = stb0899_read_reg(state, STB0899_CFD); + STB0899_SETFIELD_VAL(CFD_ON, reg, 1); +@@ -294,11 +297,10 @@ + do { + dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk); + if (stb0899_check_carrier(state) == NOCARRIER) { +- index++; + last_derot_freq = derot_freq; +- derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */ ++ derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */ + +- if(abs(derot_freq) > derot_limit) ++ if (derot_freq > base_freq + derot_limit || derot_freq < base_freq - derot_limit) + next_loop--; + + if (next_loop) { +@@ -310,9 +312,10 @@ + STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + } ++ index++; ++ internal->direction = -internal->direction; /* Change zigzag direction */ + } + +- internal->direction = -internal->direction; /* Change zigzag direction */ + } while ((internal->status != CARRIEROK) && next_loop); + + if (internal->status == CARRIEROK) { +@@ -338,6 +341,7 @@ + int lock = 0, index = 0, dataTime = 500, loop; + u8 reg; + ++ msleep(1); + internal->status = NODATA; + + /* RESET FEC */ +@@ -348,6 +352,7 @@ + reg = stb0899_read_reg(state, STB0899_TSTRES); + STB0899_SETFIELD_VAL(FRESACS, reg, 0); + stb0899_write_reg(state, STB0899_TSTRES, reg); ++ msleep(1); + + if (params->srate <= 2000000) + dataTime = 2000; +@@ -363,6 +368,7 @@ + + stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */ + while (1) { ++ msleep(1); // Alex: added 1 mSec + /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */ + reg = stb0899_read_reg(state, STB0899_VSTATUS); + lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg); +@@ -390,20 +396,21 @@ + short int derot_freq, derot_step, derot_limit, next_loop = 3; + u8 cfr[2]; + u8 reg; +- int index = 1; ++ int index = 0; ++ int base_freq; + + struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; + +- derot_step = (params->srate / 4L) / internal->mclk; ++ derot_step = internal->derot_step; + derot_limit = (internal->sub_range / 2L) / internal->mclk; + derot_freq = internal->derot_freq; ++ base_freq = internal->derot_freq; + + do { + if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) { + + derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */ +- if (abs(derot_freq) > derot_limit) ++ if (derot_freq > base_freq + derot_limit || derot_freq < base_freq - derot_limit) + next_loop--; + + if (next_loop) { +@@ -417,9 +424,9 @@ + stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ + + stb0899_check_carrier(state); +- index++; + } + } ++ index++; + internal->direction = -internal->direction; /* change zig zag direction */ + } while ((internal->status != DATAOK) && next_loop); + +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/stb0899_algo.c.orig linux-openelec/drivers/media/dvb-frontends/stb0899_algo.c.orig +--- linux-3.14.36/drivers/media/dvb-frontends/stb0899_algo.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/stb0899_algo.c.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1536 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include "stb0899_drv.h" ++#include "stb0899_priv.h" ++#include "stb0899_reg.h" ++ ++static inline u32 stb0899_do_div(u64 n, u32 d) ++{ ++ /* wrap do_div() for ease of use */ ++ ++ do_div(n, d); ++ return n; ++} ++ ++#if 0 ++/* These functions are currently unused */ ++/* ++ * stb0899_calc_srate ++ * Compute symbol rate ++ */ ++static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr) ++{ ++ u64 tmp; ++ ++ /* srate = (SFR * master_clk) >> 20 */ ++ ++ /* sfr is of size 20 bit, stored with an offset of 4 bit */ ++ tmp = (((u32)sfr[0]) << 16) | (((u32)sfr[1]) << 8) | sfr[2]; ++ tmp &= ~0xf; ++ tmp *= master_clk; ++ tmp >>= 24; ++ ++ return tmp; ++} ++ ++/* ++ * stb0899_get_srate ++ * Get the current symbol rate ++ */ ++static u32 stb0899_get_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u8 sfr[3]; ++ ++ stb0899_read_regs(state, STB0899_SFRH, sfr, 3); ++ ++ return stb0899_calc_srate(internal->master_clk, sfr); ++} ++#endif ++ ++/* ++ * stb0899_set_srate ++ * Set symbol frequency ++ * MasterClock: master clock frequency (hz) ++ * SymbolRate: symbol rate (bauds) ++ * return symbol frequency ++ */ ++static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 srate) ++{ ++ u32 tmp; ++ u8 sfr[3]; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "-->"); ++ /* ++ * in order to have the maximum precision, the symbol rate entered into ++ * the chip is computed as the closest value of the "true value". ++ * In this purpose, the symbol rate value is rounded (1 is added on the bit ++ * below the LSB ) ++ * ++ * srate = (SFR * master_clk) >> 20 ++ * <=> ++ * SFR = srate << 20 / master_clk ++ * ++ * rounded: ++ * SFR = (srate << 21 + master_clk) / (2 * master_clk) ++ * ++ * stored as 20 bit number with an offset of 4 bit: ++ * sfr = SFR << 4; ++ */ ++ ++ tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk); ++ tmp <<= 4; ++ ++ sfr[0] = tmp >> 16; ++ sfr[1] = tmp >> 8; ++ sfr[2] = tmp; ++ ++ stb0899_write_regs(state, STB0899_SFRH, sfr, 3); ++ ++ return srate; ++} ++ ++/* ++ * stb0899_calc_derot_time ++ * Compute the amount of time needed by the derotator to lock ++ * SymbolRate: Symbol rate ++ * return: derotator time constant (ms) ++ */ ++static long stb0899_calc_derot_time(long srate) ++{ ++ if (srate > 0) ++ return (100000 / (srate / 1000)); ++ else ++ return 0; ++} ++ ++/* ++ * stb0899_carr_width ++ * Compute the width of the carrier ++ * return: width of carrier (kHz or Mhz) ++ */ ++long stb0899_carr_width(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ return (internal->srate + (internal->srate * internal->rolloff) / 100); ++} ++ ++/* ++ * stb0899_first_subrange ++ * Compute the first subrange of the search ++ */ ++static void stb0899_first_subrange(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ struct stb0899_config *config = state->config; ++ ++ int range = 0; ++ u32 bandwidth = 0; ++ ++ if (config->tuner_get_bandwidth) { ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ config->tuner_get_bandwidth(&state->frontend, &bandwidth); ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ range = bandwidth - stb0899_carr_width(state) / 2; ++ } ++ ++ if (range > 0) ++ internal->sub_range = min(internal->srch_range, range); ++ else ++ internal->sub_range = 0; ++ ++ internal->freq = params->freq; ++ internal->tuner_offst = 0L; ++ internal->sub_dir = 1; ++} ++ ++/* ++ * stb0899_check_tmg ++ * check for timing lock ++ * internal.Ttiming: time to wait for loop lock ++ */ ++static enum stb0899_status stb0899_check_tmg(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ int lock; ++ u8 reg; ++ s8 timing; ++ ++ msleep(internal->t_derot); ++ ++ stb0899_write_reg(state, STB0899_RTF, 0xf2); ++ reg = stb0899_read_reg(state, STB0899_TLIR); ++ lock = STB0899_GETFIELD(TLIR_TMG_LOCK_IND, reg); ++ timing = stb0899_read_reg(state, STB0899_RTF); ++ ++ if (lock >= 42) { ++ if ((lock > 48) && (abs(timing) >= 110)) { ++ internal->status = ANALOGCARRIER; ++ dprintk(state->verbose, FE_DEBUG, 1, "-->ANALOG Carrier !"); ++ } else { ++ internal->status = TIMINGOK; ++ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK !"); ++ } ++ } else { ++ internal->status = NOTIMING; ++ dprintk(state->verbose, FE_DEBUG, 1, "-->NO TIMING !"); ++ } ++ return internal->status; ++} ++ ++/* ++ * stb0899_search_tmg ++ * perform a fs/2 zig-zag to find timing ++ */ ++static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ short int derot_step, derot_freq = 0, derot_limit, next_loop = 3; ++ int index = 0; ++ u8 cfr[2]; ++ ++ internal->status = NOTIMING; ++ ++ /* timing loop computation & symbol rate optimisation */ ++ derot_limit = (internal->sub_range / 2L) / internal->mclk; ++ derot_step = (params->srate / 2L) / internal->mclk; ++ ++ while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) { ++ index++; ++ derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */ ++ ++ if (abs(derot_freq) > derot_limit) ++ next_loop--; ++ ++ if (next_loop) { ++ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq)); ++ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ ++ } ++ internal->direction = -internal->direction; /* Change zigzag direction */ ++ } ++ ++ if (internal->status == TIMINGOK) { ++ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ ++ internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]); ++ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_check_carrier ++ * Check for carrier found ++ */ ++static enum stb0899_status stb0899_check_carrier(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u8 reg; ++ ++ msleep(internal->t_derot); /* wait for derotator ok */ ++ ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ reg = stb0899_read_reg(state, STB0899_DSTATUS); ++ dprintk(state->verbose, FE_DEBUG, 1, "--------------------> STB0899_DSTATUS=[0x%02x]", reg); ++ if (STB0899_GETFIELD(CARRIER_FOUND, reg)) { ++ internal->status = CARRIEROK; ++ dprintk(state->verbose, FE_DEBUG, 1, "-------------> CARRIEROK !"); ++ } else { ++ internal->status = NOCARRIER; ++ dprintk(state->verbose, FE_DEBUG, 1, "-------------> NOCARRIER !"); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_search_carrier ++ * Search for a QPSK carrier with the derotator ++ */ ++static enum stb0899_status stb0899_search_carrier(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3; ++ int index = 0; ++ u8 cfr[2]; ++ u8 reg; ++ ++ internal->status = NOCARRIER; ++ derot_limit = (internal->sub_range / 2L) / internal->mclk; ++ derot_freq = internal->derot_freq; ++ ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ do { ++ dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk); ++ if (stb0899_check_carrier(state) == NOCARRIER) { ++ index++; ++ last_derot_freq = derot_freq; ++ derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */ ++ ++ if(abs(derot_freq) > derot_limit) ++ next_loop--; ++ ++ if (next_loop) { ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq)); ++ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ ++ } ++ } ++ ++ internal->direction = -internal->direction; /* Change zigzag direction */ ++ } while ((internal->status != CARRIEROK) && next_loop); ++ ++ if (internal->status == CARRIEROK) { ++ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ ++ internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]); ++ dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq); ++ } else { ++ internal->derot_freq = last_derot_freq; ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_check_data ++ * Check for data found ++ */ ++static enum stb0899_status stb0899_check_data(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ int lock = 0, index = 0, dataTime = 500, loop; ++ u8 reg; ++ ++ internal->status = NODATA; ++ ++ /* RESET FEC */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESACS, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ msleep(1); ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESACS, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ if (params->srate <= 2000000) ++ dataTime = 2000; ++ else if (params->srate <= 5000000) ++ dataTime = 1500; ++ else if (params->srate <= 15000000) ++ dataTime = 1000; ++ else ++ dataTime = 500; ++ ++ /* clear previous failed END_LOOPVIT */ ++ stb0899_read_reg(state, STB0899_VSTATUS); ++ ++ stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */ ++ while (1) { ++ /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */ ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg); ++ loop = STB0899_GETFIELD(VSTATUS_END_LOOPVIT, reg); ++ ++ if (lock || loop || (index > dataTime)) ++ break; ++ index++; ++ } ++ ++ if (lock) { /* DATA LOCK indicator */ ++ internal->status = DATAOK; ++ dprintk(state->verbose, FE_DEBUG, 1, "-----------------> DATA OK !"); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_search_data ++ * Search for a QPSK carrier with the derotator ++ */ ++static enum stb0899_status stb0899_search_data(struct stb0899_state *state) ++{ ++ short int derot_freq, derot_step, derot_limit, next_loop = 3; ++ u8 cfr[2]; ++ u8 reg; ++ int index = 1; ++ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ derot_step = (params->srate / 4L) / internal->mclk; ++ derot_limit = (internal->sub_range / 2L) / internal->mclk; ++ derot_freq = internal->derot_freq; ++ ++ do { ++ if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) { ++ ++ derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */ ++ if (abs(derot_freq) > derot_limit) ++ next_loop--; ++ ++ if (next_loop) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Derot freq=%d, mclk=%d", derot_freq, internal->mclk); ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq)); ++ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq)); ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ ++ ++ stb0899_check_carrier(state); ++ index++; ++ } ++ } ++ internal->direction = -internal->direction; /* change zig zag direction */ ++ } while ((internal->status != DATAOK) && next_loop); ++ ++ if (internal->status == DATAOK) { ++ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ ++ ++ /* store autodetected IQ swapping as default for DVB-S2 tuning */ ++ reg = stb0899_read_reg(state, STB0899_IQSWAP); ++ if (STB0899_GETFIELD(SYM, reg)) ++ internal->inversion = IQ_SWAP_ON; ++ else ++ internal->inversion = IQ_SWAP_OFF; ++ ++ internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]); ++ dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_check_range ++ * check if the found frequency is in the correct range ++ */ ++static enum stb0899_status stb0899_check_range(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ int range_offst, tp_freq; ++ ++ range_offst = internal->srch_range / 2000; ++ tp_freq = internal->freq - (internal->derot_freq * internal->mclk) / 1000; ++ ++ if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) { ++ internal->status = RANGEOK; ++ dprintk(state->verbose, FE_DEBUG, 1, "----> RANGEOK !"); ++ } else { ++ internal->status = OUTOFRANGE; ++ dprintk(state->verbose, FE_DEBUG, 1, "----> OUT OF RANGE !"); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * NextSubRange ++ * Compute the next subrange of the search ++ */ ++static void next_sub_range(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ long old_sub_range; ++ ++ if (internal->sub_dir > 0) { ++ old_sub_range = internal->sub_range; ++ internal->sub_range = min((internal->srch_range / 2) - ++ (internal->tuner_offst + internal->sub_range / 2), ++ internal->sub_range); ++ ++ if (internal->sub_range < 0) ++ internal->sub_range = 0; ++ ++ internal->tuner_offst += (old_sub_range + internal->sub_range) / 2; ++ } ++ ++ internal->freq = params->freq + (internal->sub_dir * internal->tuner_offst) / 1000; ++ internal->sub_dir = -internal->sub_dir; ++} ++ ++/* ++ * stb0899_dvbs_algo ++ * Search for a signal, timing, carrier and data for a ++ * given frequency in a given range ++ */ ++enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state) ++{ ++ struct stb0899_params *params = &state->params; ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u8 bclc, reg; ++ u8 cfr[2]; ++ u8 eq_const[10]; ++ s32 clnI = 3; ++ u32 bandwidth = 0; ++ ++ /* BETA values rated @ 99MHz */ ++ s32 betaTab[5][4] = { ++ /* 5 10 20 30MBps */ ++ { 37, 34, 32, 31 }, /* QPSK 1/2 */ ++ { 37, 35, 33, 31 }, /* QPSK 2/3 */ ++ { 37, 35, 33, 31 }, /* QPSK 3/4 */ ++ { 37, 36, 33, 32 }, /* QPSK 5/6 */ ++ { 37, 36, 33, 32 } /* QPSK 7/8 */ ++ }; ++ ++ internal->direction = 1; ++ ++ stb0899_set_srate(state, internal->master_clk, params->srate); ++ /* Carrier loop optimization versus symbol rate for acquisition*/ ++ if (params->srate <= 5000000) { ++ stb0899_write_reg(state, STB0899_ACLC, 0x89); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x1c); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 0; ++ } else if (params->srate <= 15000000) { ++ stb0899_write_reg(state, STB0899_ACLC, 0xc9); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x22); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 1; ++ } else if(params->srate <= 25000000) { ++ stb0899_write_reg(state, STB0899_ACLC, 0x89); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x27); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 2; ++ } else { ++ stb0899_write_reg(state, STB0899_ACLC, 0xc8); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x29); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 3; ++ } ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Set the timing loop to acquisition"); ++ /* Set the timing loop to acquisition */ ++ stb0899_write_reg(state, STB0899_RTC, 0x46); ++ stb0899_write_reg(state, STB0899_CFD, 0xee); ++ ++ /* !! WARNING !! ++ * Do not read any status variables while acquisition, ++ * If any needed, read before the acquisition starts ++ * querying status while acquiring causes the ++ * acquisition to go bad and hence no locks. ++ */ ++ dprintk(state->verbose, FE_DEBUG, 1, "Derot Percent=%d Srate=%d mclk=%d", ++ internal->derot_percent, params->srate, internal->mclk); ++ ++ /* Initial calculations */ ++ internal->derot_step = internal->derot_percent * (params->srate / 1000L) / internal->mclk; /* DerotStep/1000 * Fsymbol */ ++ internal->t_derot = stb0899_calc_derot_time(params->srate); ++ internal->t_data = 500; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "RESET stream merger"); ++ /* RESET Stream merger */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ /* ++ * Set KDIVIDER to an intermediate value between ++ * 1/2 and 7/8 for acquisition ++ */ ++ reg = stb0899_read_reg(state, STB0899_DEMAPVIT); ++ STB0899_SETFIELD_VAL(DEMAPVIT_KDIVIDER, reg, 60); ++ stb0899_write_reg(state, STB0899_DEMAPVIT, reg); ++ ++ stb0899_write_reg(state, STB0899_EQON, 0x01); /* Equalizer OFF while acquiring */ ++ stb0899_write_reg(state, STB0899_VITSYNC, 0x19); ++ ++ stb0899_first_subrange(state); ++ do { ++ /* Initialisations */ ++ cfr[0] = cfr[1] = 0; ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* RESET derotator frequency */ ++ ++ stb0899_write_reg(state, STB0899_RTF, 0); ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ internal->derot_freq = 0; ++ internal->status = NOAGC1; ++ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ /* Move tuner to frequency */ ++ dprintk(state->verbose, FE_DEBUG, 1, "Tuner set frequency"); ++ if (state->config->tuner_set_frequency) ++ state->config->tuner_set_frequency(&state->frontend, internal->freq); ++ ++ if (state->config->tuner_get_frequency) ++ state->config->tuner_get_frequency(&state->frontend, &internal->freq); ++ ++ msleep(internal->t_agc1 + internal->t_agc2 + internal->t_derot); /* AGC1, AGC2 and timing loop */ ++ dprintk(state->verbose, FE_DEBUG, 1, "current derot freq=%d", internal->derot_freq); ++ internal->status = AGC1OK; ++ ++ /* There is signal in the band */ ++ if (config->tuner_get_bandwidth) ++ config->tuner_get_bandwidth(&state->frontend, &bandwidth); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++ if (params->srate <= bandwidth / 2) ++ stb0899_search_tmg(state); /* For low rates (SCPC) */ ++ else ++ stb0899_check_tmg(state); /* For high rates (MCPC) */ ++ ++ if (internal->status == TIMINGOK) { ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "TIMING OK ! Derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ if (stb0899_search_carrier(state) == CARRIEROK) { /* Search for carrier */ ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "CARRIER OK ! Derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ if (stb0899_search_data(state) == DATAOK) { /* Check for data */ ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "DATA OK ! Derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ if (stb0899_check_range(state) == RANGEOK) { ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "RANGE OK ! derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ internal->freq = params->freq - ((internal->derot_freq * internal->mclk) / 1000); ++ reg = stb0899_read_reg(state, STB0899_PLPARM); ++ internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg); ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "freq=%d, internal resultant freq=%d", ++ params->freq, internal->freq); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "internal puncture rate=%d", ++ internal->fecrate); ++ } ++ } ++ } ++ } ++ if (internal->status != RANGEOK) ++ next_sub_range(state); ++ ++ } while (internal->sub_range && internal->status != RANGEOK); ++ ++ /* Set the timing loop to tracking */ ++ stb0899_write_reg(state, STB0899_RTC, 0x33); ++ stb0899_write_reg(state, STB0899_CFD, 0xf7); ++ /* if locked and range ok, set Kdiv */ ++ if (internal->status == RANGEOK) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Locked & Range OK !"); ++ stb0899_write_reg(state, STB0899_EQON, 0x41); /* Equalizer OFF while acquiring */ ++ stb0899_write_reg(state, STB0899_VITSYNC, 0x39); /* SN to b'11 for acquisition */ ++ ++ /* ++ * Carrier loop optimization versus ++ * symbol Rate/Puncture Rate for Tracking ++ */ ++ reg = stb0899_read_reg(state, STB0899_BCLC); ++ switch (internal->fecrate) { ++ case STB0899_FEC_1_2: /* 13 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 0x1a); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[0][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_2_3: /* 18 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 44); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[1][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_3_4: /* 21 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 60); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[2][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_5_6: /* 24 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 75); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[3][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_6_7: /* 25 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 88); ++ stb0899_write_reg(state, STB0899_ACLC, 0x88); ++ stb0899_write_reg(state, STB0899_BCLC, 0x9a); ++ break; ++ case STB0899_FEC_7_8: /* 26 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 94); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[4][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported Puncture Rate"); ++ break; ++ } ++ /* release stream merger RESET */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ /* disable carrier detector */ ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 0); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ stb0899_read_regs(state, STB0899_EQUAI1, eq_const, 10); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_dvbs2_config_uwp ++ * Configure UWP state machine ++ */ ++static void stb0899_dvbs2_config_uwp(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ u32 uwp1, uwp2, uwp3, reg; ++ ++ uwp1 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); ++ uwp2 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL2); ++ uwp3 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL3); ++ ++ STB0899_SETFIELD_VAL(UWP_ESN0_AVE, uwp1, config->esno_ave); ++ STB0899_SETFIELD_VAL(UWP_ESN0_QUANT, uwp1, config->esno_quant); ++ STB0899_SETFIELD_VAL(UWP_TH_SOF, uwp1, config->uwp_threshold_sof); ++ ++ STB0899_SETFIELD_VAL(FE_COARSE_TRK, uwp2, internal->av_frame_coarse); ++ STB0899_SETFIELD_VAL(FE_FINE_TRK, uwp2, internal->av_frame_fine); ++ STB0899_SETFIELD_VAL(UWP_MISS_TH, uwp2, config->miss_threshold); ++ ++ STB0899_SETFIELD_VAL(UWP_TH_ACQ, uwp3, config->uwp_threshold_acq); ++ STB0899_SETFIELD_VAL(UWP_TH_TRACK, uwp3, config->uwp_threshold_track); ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL1, STB0899_OFF0_UWP_CNTRL1, uwp1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL2, STB0899_OFF0_UWP_CNTRL2, uwp2); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL3, STB0899_OFF0_UWP_CNTRL3, uwp3); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, SOF_SRCH_TO); ++ STB0899_SETFIELD_VAL(SOF_SEARCH_TIMEOUT, reg, config->sof_search_timeout); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_SOF_SRCH_TO, STB0899_OFF0_SOF_SRCH_TO, reg); ++} ++ ++/* ++ * stb0899_dvbs2_config_csm_auto ++ * Set CSM to AUTO mode ++ */ ++static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state) ++{ ++ u32 reg; ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg); ++} ++ ++static long Log2Int(int number) ++{ ++ int i; ++ ++ i = 0; ++ while ((1 << i) <= abs(number)) ++ i++; ++ ++ if (number == 0) ++ i = 1; ++ ++ return i - 1; ++} ++ ++/* ++ * stb0899_dvbs2_calc_srate ++ * compute BTR_NOM_FREQ for the symbol rate ++ */ ++static u32 stb0899_dvbs2_calc_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u32 dec_ratio, dec_rate, decim, remain, intval, btr_nom_freq; ++ u32 master_clk, srate; ++ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ dec_rate = Log2Int(dec_ratio); ++ decim = 1 << dec_rate; ++ master_clk = internal->master_clk / 1000; ++ srate = internal->srate / 1000; ++ ++ if (decim <= 4) { ++ intval = (decim * (1 << (config->btr_nco_bits - 1))) / master_clk; ++ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; ++ } else { ++ intval = (1 << (config->btr_nco_bits - 1)) / (master_clk / 100) * decim / 100; ++ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; ++ } ++ btr_nom_freq = (intval * srate) + ((remain * srate) / master_clk); ++ ++ return btr_nom_freq; ++} ++ ++/* ++ * stb0899_dvbs2_calc_dev ++ * compute the correction to be applied to symbol rate ++ */ ++static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u32 dec_ratio, correction, master_clk, srate; ++ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ ++ master_clk = internal->master_clk / 1000; /* for integer Caculation*/ ++ srate = internal->srate / 1000; /* for integer Caculation*/ ++ correction = (512 * master_clk) / (2 * dec_ratio * srate); ++ ++ return correction; ++} ++ ++/* ++ * stb0899_dvbs2_set_srate ++ * Set DVBS2 symbol rate ++ */ ++static void stb0899_dvbs2_set_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ u32 dec_ratio, dec_rate, win_sel, decim, f_sym, btr_nom_freq; ++ u32 correction, freq_adj, band_lim, decim_cntrl, reg; ++ u8 anti_alias; ++ ++ /*set decimation to 1*/ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ dec_rate = Log2Int(dec_ratio); ++ ++ win_sel = 0; ++ if (dec_rate >= 5) ++ win_sel = dec_rate - 4; ++ ++ decim = (1 << dec_rate); ++ /* (FSamp/Fsymbol *100) for integer Caculation */ ++ f_sym = internal->master_clk / ((decim * internal->srate) / 1000); ++ ++ if (f_sym <= 2250) /* don't band limit signal going into btr block*/ ++ band_lim = 1; ++ else ++ band_lim = 0; /* band limit signal going into btr block*/ ++ ++ decim_cntrl = ((win_sel << 3) & 0x18) + ((band_lim << 5) & 0x20) + (dec_rate & 0x7); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DECIM_CNTRL, STB0899_OFF0_DECIM_CNTRL, decim_cntrl); ++ ++ if (f_sym <= 3450) ++ anti_alias = 0; ++ else if (f_sym <= 4250) ++ anti_alias = 1; ++ else ++ anti_alias = 2; ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ANTI_ALIAS_SEL, STB0899_OFF0_ANTI_ALIAS_SEL, anti_alias); ++ btr_nom_freq = stb0899_dvbs2_calc_srate(state); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_NOM_FREQ, STB0899_OFF0_BTR_NOM_FREQ, btr_nom_freq); ++ ++ correction = stb0899_dvbs2_calc_dev(state); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); ++ STB0899_SETFIELD_VAL(BTR_FREQ_CORR, reg, correction); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); ++ ++ /* scale UWP+CSM frequency to sample rate*/ ++ freq_adj = internal->srate / (internal->master_clk / 4096); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_FREQ_ADJ_SCALE, STB0899_OFF0_FREQ_ADJ_SCALE, freq_adj); ++} ++ ++/* ++ * stb0899_dvbs2_set_btr_loopbw ++ * set bit timing loop bandwidth as a percentage of the symbol rate ++ */ ++static void stb0899_dvbs2_set_btr_loopbw(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u32 sym_peak = 23, zeta = 707, loopbw_percent = 60; ++ s32 dec_ratio, dec_rate, k_btr1_rshft, k_btr1, k_btr0_rshft; ++ s32 k_btr0, k_btr2_rshft, k_direct_shift, k_indirect_shift; ++ u32 decim, K, wn, k_direct, k_indirect; ++ u32 reg; ++ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ dec_rate = Log2Int(dec_ratio); ++ decim = (1 << dec_rate); ++ ++ sym_peak *= 576000; ++ K = (1 << config->btr_nco_bits) / (internal->master_clk / 1000); ++ K *= (internal->srate / 1000000) * decim; /*k=k 10^-8*/ ++ ++ if (K != 0) { ++ K = sym_peak / K; ++ wn = (4 * zeta * zeta) + 1000000; ++ wn = (2 * (loopbw_percent * 1000) * 40 * zeta) /wn; /*wn =wn 10^-8*/ ++ ++ k_indirect = (wn * wn) / K; ++ k_indirect = k_indirect; /*kindirect = kindirect 10^-6*/ ++ k_direct = (2 * wn * zeta) / K; /*kDirect = kDirect 10^-2*/ ++ k_direct *= 100; ++ ++ k_direct_shift = Log2Int(k_direct) - Log2Int(10000) - 2; ++ k_btr1_rshft = (-1 * k_direct_shift) + config->btr_gain_shift_offset; ++ k_btr1 = k_direct / (1 << k_direct_shift); ++ k_btr1 /= 10000; ++ ++ k_indirect_shift = Log2Int(k_indirect + 15) - 20 /*- 2*/; ++ k_btr0_rshft = (-1 * k_indirect_shift) + config->btr_gain_shift_offset; ++ k_btr0 = k_indirect * (1 << (-k_indirect_shift)); ++ k_btr0 /= 1000000; ++ ++ k_btr2_rshft = 0; ++ if (k_btr0_rshft > 15) { ++ k_btr2_rshft = k_btr0_rshft - 15; ++ k_btr0_rshft = 15; ++ } ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_LOOP_GAIN); ++ STB0899_SETFIELD_VAL(KBTR0_RSHFT, reg, k_btr0_rshft); ++ STB0899_SETFIELD_VAL(KBTR0, reg, k_btr0); ++ STB0899_SETFIELD_VAL(KBTR1_RSHFT, reg, k_btr1_rshft); ++ STB0899_SETFIELD_VAL(KBTR1, reg, k_btr1); ++ STB0899_SETFIELD_VAL(KBTR2_RSHFT, reg, k_btr2_rshft); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, reg); ++ } else ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, 0xc4c4f); ++} ++ ++/* ++ * stb0899_dvbs2_set_carr_freq ++ * set nominal frequency for carrier search ++ */ ++static void stb0899_dvbs2_set_carr_freq(struct stb0899_state *state, s32 carr_freq, u32 master_clk) ++{ ++ struct stb0899_config *config = state->config; ++ s32 crl_nom_freq; ++ u32 reg; ++ ++ crl_nom_freq = (1 << config->crl_nco_bits) / master_clk; ++ crl_nom_freq *= carr_freq; ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); ++ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, crl_nom_freq); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); ++} ++ ++/* ++ * stb0899_dvbs2_init_calc ++ * Initialize DVBS2 UWP, CSM, carrier and timing loops ++ */ ++static void stb0899_dvbs2_init_calc(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ s32 steps, step_size; ++ u32 range, reg; ++ ++ /* config uwp and csm */ ++ stb0899_dvbs2_config_uwp(state); ++ stb0899_dvbs2_config_csm_auto(state); ++ ++ /* initialize BTR */ ++ stb0899_dvbs2_set_srate(state); ++ stb0899_dvbs2_set_btr_loopbw(state); ++ ++ if (internal->srate / 1000000 >= 15) ++ step_size = (1 << 17) / 5; ++ else if (internal->srate / 1000000 >= 10) ++ step_size = (1 << 17) / 7; ++ else if (internal->srate / 1000000 >= 5) ++ step_size = (1 << 17) / 10; ++ else ++ step_size = (1 << 17) / 4; ++ ++ range = internal->srch_range / 1000000; ++ steps = (10 * range * (1 << 17)) / (step_size * (internal->srate / 1000000)); ++ steps = (steps + 6) / 10; ++ steps = (steps == 0) ? 1 : steps; ++ if (steps % 2 == 0) ++ stb0899_dvbs2_set_carr_freq(state, internal->center_freq - ++ (internal->step_size * (internal->srate / 20000000)), ++ (internal->master_clk) / 1000000); ++ else ++ stb0899_dvbs2_set_carr_freq(state, internal->center_freq, (internal->master_clk) / 1000000); ++ ++ /*Set Carrier Search params (zigzag, num steps and freq step size*/ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, ACQ_CNTRL2); ++ STB0899_SETFIELD_VAL(ZIGZAG, reg, 1); ++ STB0899_SETFIELD_VAL(NUM_STEPS, reg, steps); ++ STB0899_SETFIELD_VAL(FREQ_STEPSIZE, reg, step_size); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQ_CNTRL2, STB0899_OFF0_ACQ_CNTRL2, reg); ++} ++ ++/* ++ * stb0899_dvbs2_btr_init ++ * initialize the timing loop ++ */ ++static void stb0899_dvbs2_btr_init(struct stb0899_state *state) ++{ ++ u32 reg; ++ ++ /* set enable BTR loopback */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); ++ STB0899_SETFIELD_VAL(INTRP_PHS_SENSE, reg, 1); ++ STB0899_SETFIELD_VAL(BTR_ERR_ENA, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); ++ ++ /* fix btr freq accum at 0 */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x10000000); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x00000000); ++ ++ /* fix btr freq accum at 0 */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x10000000); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x00000000); ++} ++ ++/* ++ * stb0899_dvbs2_reacquire ++ * trigger a DVB-S2 acquisition ++ */ ++static void stb0899_dvbs2_reacquire(struct stb0899_state *state) ++{ ++ u32 reg = 0; ++ ++ /* demod soft reset */ ++ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); ++ ++ /*Reset Timing Loop */ ++ stb0899_dvbs2_btr_init(state); ++ ++ /* reset Carrier loop */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, (1 << 30)); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_LOOP_GAIN, STB0899_OFF0_CRL_LOOP_GAIN, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, (1 << 30)); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, 0); ++ ++ /*release demod soft reset */ ++ reg = 0; ++ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); ++ ++ /* start acquisition process */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQUIRE_TRIG, STB0899_OFF0_ACQUIRE_TRIG, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_LOCK_LOST, STB0899_OFF0_LOCK_LOST, 0); ++ ++ /* equalizer Init */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 1); ++ ++ /*Start equilizer */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 0); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); ++ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0); ++ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 0); ++ STB0899_SETFIELD_VAL(EQ_DELAY, reg, 0x05); ++ STB0899_SETFIELD_VAL(EQ_ADAPT_MODE, reg, 0x01); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); ++ ++ /* RESET Packet delineator */ ++ stb0899_write_reg(state, STB0899_PDELCTRL, 0x4a); ++} ++ ++/* ++ * stb0899_dvbs2_get_dmd_status ++ * get DVB-S2 Demod LOCK status ++ */ ++static enum stb0899_status stb0899_dvbs2_get_dmd_status(struct stb0899_state *state, int timeout) ++{ ++ int time = -10, lock = 0, uwp, csm; ++ u32 reg; ++ ++ do { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STATUS); ++ dprintk(state->verbose, FE_DEBUG, 1, "DMD_STATUS=[0x%02x]", reg); ++ if (STB0899_GETFIELD(IF_AGC_LOCK, reg)) ++ dprintk(state->verbose, FE_DEBUG, 1, "------------->IF AGC LOCKED !"); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); ++ dprintk(state->verbose, FE_DEBUG, 1, "----------->DMD STAT2=[0x%02x]", reg); ++ uwp = STB0899_GETFIELD(UWP_LOCK, reg); ++ csm = STB0899_GETFIELD(CSM_LOCK, reg); ++ if (uwp && csm) ++ lock = 1; ++ ++ time += 10; ++ msleep(10); ++ ++ } while ((!lock) && (time <= timeout)); ++ ++ if (lock) { ++ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 LOCK !"); ++ return DVBS2_DEMOD_LOCK; ++ } else { ++ return DVBS2_DEMOD_NOLOCK; ++ } ++} ++ ++/* ++ * stb0899_dvbs2_get_data_lock ++ * get FEC status ++ */ ++static int stb0899_dvbs2_get_data_lock(struct stb0899_state *state, int timeout) ++{ ++ int time = 0, lock = 0; ++ u8 reg; ++ ++ while ((!lock) && (time < timeout)) { ++ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); ++ dprintk(state->verbose, FE_DEBUG, 1, "---------> CFGPDELSTATUS=[0x%02x]", reg); ++ lock = STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg); ++ time++; ++ } ++ ++ return lock; ++} ++ ++/* ++ * stb0899_dvbs2_get_fec_status ++ * get DVB-S2 FEC LOCK status ++ */ ++static enum stb0899_status stb0899_dvbs2_get_fec_status(struct stb0899_state *state, int timeout) ++{ ++ int time = 0, Locked; ++ ++ do { ++ Locked = stb0899_dvbs2_get_data_lock(state, 1); ++ time++; ++ msleep(1); ++ ++ } while ((!Locked) && (time < timeout)); ++ ++ if (Locked) { ++ dprintk(state->verbose, FE_DEBUG, 1, "---------->DVB-S2 FEC LOCK !"); ++ return DVBS2_FEC_LOCK; ++ } else { ++ return DVBS2_FEC_NOLOCK; ++ } ++} ++ ++ ++/* ++ * stb0899_dvbs2_init_csm ++ * set parameters for manual mode ++ */ ++static void stb0899_dvbs2_init_csm(struct stb0899_state *state, int pilots, enum stb0899_modcod modcod) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ s32 dvt_tbl = 1, two_pass = 0, agc_gain = 6, agc_shift = 0, loop_shift = 0, phs_diff_thr = 0x80; ++ s32 gamma_acq, gamma_rho_acq, gamma_trk, gamma_rho_trk, lock_count_thr; ++ u32 csm1, csm2, csm3, csm4; ++ ++ if (((internal->master_clk / internal->srate) <= 4) && (modcod <= 11) && (pilots == 1)) { ++ switch (modcod) { ++ case STB0899_QPSK_12: ++ gamma_acq = 25; ++ gamma_rho_acq = 2700; ++ gamma_trk = 12; ++ gamma_rho_trk = 180; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_35: ++ gamma_acq = 38; ++ gamma_rho_acq = 7182; ++ gamma_trk = 14; ++ gamma_rho_trk = 308; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_23: ++ gamma_acq = 42; ++ gamma_rho_acq = 9408; ++ gamma_trk = 17; ++ gamma_rho_trk = 476; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_34: ++ gamma_acq = 53; ++ gamma_rho_acq = 16642; ++ gamma_trk = 19; ++ gamma_rho_trk = 646; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_45: ++ gamma_acq = 53; ++ gamma_rho_acq = 17119; ++ gamma_trk = 22; ++ gamma_rho_trk = 880; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_56: ++ gamma_acq = 55; ++ gamma_rho_acq = 19250; ++ gamma_trk = 23; ++ gamma_rho_trk = 989; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_89: ++ gamma_acq = 60; ++ gamma_rho_acq = 24240; ++ gamma_trk = 24; ++ gamma_rho_trk = 1176; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_910: ++ gamma_acq = 66; ++ gamma_rho_acq = 29634; ++ gamma_trk = 24; ++ gamma_rho_trk = 1176; ++ lock_count_thr = 8; ++ break; ++ default: ++ gamma_acq = 66; ++ gamma_rho_acq = 29634; ++ gamma_trk = 24; ++ gamma_rho_trk = 1176; ++ lock_count_thr = 8; ++ break; ++ } ++ ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, csm1, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ csm2 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL2); ++ csm3 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL3); ++ csm4 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL4); ++ ++ STB0899_SETFIELD_VAL(CSM_DVT_TABLE, csm1, dvt_tbl); ++ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, two_pass); ++ STB0899_SETFIELD_VAL(CSM_AGC_GAIN, csm1, agc_gain); ++ STB0899_SETFIELD_VAL(CSM_AGC_SHIFT, csm1, agc_shift); ++ STB0899_SETFIELD_VAL(FE_LOOP_SHIFT, csm1, loop_shift); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_ACQ, csm2, gamma_acq); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOACQ, csm2, gamma_rho_acq); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_TRACK, csm3, gamma_trk); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOTRACK, csm3, gamma_rho_trk); ++ STB0899_SETFIELD_VAL(CSM_LOCKCOUNT_THRESH, csm4, lock_count_thr); ++ STB0899_SETFIELD_VAL(CSM_PHASEDIFF_THRESH, csm4, phs_diff_thr); ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL2, STB0899_OFF0_CSM_CNTRL2, csm2); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL3, STB0899_OFF0_CSM_CNTRL3, csm3); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL4, STB0899_OFF0_CSM_CNTRL4, csm4); ++ } ++} ++ ++/* ++ * stb0899_dvbs2_get_srate ++ * get DVB-S2 Symbol Rate ++ */ ++static u32 stb0899_dvbs2_get_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u32 bTrNomFreq, srate, decimRate, intval1, intval2, reg; ++ int div1, div2, rem1, rem2; ++ ++ div1 = config->btr_nco_bits / 2; ++ div2 = config->btr_nco_bits - div1 - 1; ++ ++ bTrNomFreq = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_NOM_FREQ); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DECIM_CNTRL); ++ decimRate = STB0899_GETFIELD(DECIM_RATE, reg); ++ decimRate = (1 << decimRate); ++ ++ intval1 = internal->master_clk / (1 << div1); ++ intval2 = bTrNomFreq / (1 << div2); ++ ++ rem1 = internal->master_clk % (1 << div1); ++ rem2 = bTrNomFreq % (1 << div2); ++ /* only for integer calculation */ ++ srate = (intval1 * intval2) + ((intval1 * rem2) / (1 << div2)) + ((intval2 * rem1) / (1 << div1)); ++ srate /= decimRate; /*symbrate = (btrnomfreq_register_val*MasterClock)/2^(27+decim_rate_field) */ ++ ++ return srate; ++} ++ ++/* ++ * stb0899_dvbs2_algo ++ * Search for signal, timing, carrier and data for a given ++ * frequency in a given range ++ */ ++enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ enum stb0899_modcod modcod; ++ ++ s32 offsetfreq, searchTime, FecLockTime, pilots, iqSpectrum; ++ int i = 0; ++ u32 reg, csm1; ++ ++ if (internal->srate <= 2000000) { ++ searchTime = 5000; /* 5000 ms max time to lock UWP and CSM, SYMB <= 2Mbs */ ++ FecLockTime = 350; /* 350 ms max time to lock FEC, SYMB <= 2Mbs */ ++ } else if (internal->srate <= 5000000) { ++ searchTime = 2500; /* 2500 ms max time to lock UWP and CSM, 2Mbs < SYMB <= 5Mbs */ ++ FecLockTime = 170; /* 170 ms max time to lock FEC, 2Mbs< SYMB <= 5Mbs */ ++ } else if (internal->srate <= 10000000) { ++ searchTime = 1500; /* 1500 ms max time to lock UWP and CSM, 5Mbs srate <= 15000000) { ++ searchTime = 500; /* 500 ms max time to lock UWP and CSM, 10Mbs srate <= 20000000) { ++ searchTime = 300; /* 300 ms max time to lock UWP and CSM, 15Mbs < SYMB <= 20Mbs */ ++ FecLockTime = 30; /* 50 ms max time to lock FEC, 15Mbs< SYMB <= 20Mbs */ ++ } else if (internal->srate <= 25000000) { ++ searchTime = 250; /* 250 ms max time to lock UWP and CSM, 20 Mbs < SYMB <= 25Mbs */ ++ FecLockTime = 25; /* 25 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ ++ } else { ++ searchTime = 150; /* 150 ms max time to lock UWP and CSM, SYMB > 25Mbs */ ++ FecLockTime = 20; /* 20 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ ++ } ++ ++ /* Maintain Stream Merger in reset during acquisition */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ /* Move tuner to frequency */ ++ if (state->config->tuner_set_frequency) ++ state->config->tuner_set_frequency(&state->frontend, internal->freq); ++ if (state->config->tuner_get_frequency) ++ state->config->tuner_get_frequency(&state->frontend, &internal->freq); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++ /* Set IF AGC to acquisition */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); ++ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 4); ++ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 32); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); ++ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); ++ ++ /* Initialisation */ ++ stb0899_dvbs2_init_calc(state); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); ++ switch (internal->inversion) { ++ case IQ_SWAP_OFF: ++ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 0); ++ break; ++ case IQ_SWAP_ON: ++ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); ++ break; ++ } ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); ++ stb0899_dvbs2_reacquire(state); ++ ++ /* Wait for demod lock (UWP and CSM) */ ++ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); ++ ++ if (internal->status == DVBS2_DEMOD_LOCK) { ++ dprintk(state->verbose, FE_DEBUG, 1, "------------> DVB-S2 DEMOD LOCK !"); ++ i = 0; ++ /* Demod Locked, check FEC status */ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ ++ /*If false lock (UWP and CSM Locked but no FEC) try 3 time max*/ ++ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { ++ /* Read the frequency offset*/ ++ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); ++ ++ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); ++ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); ++ stb0899_dvbs2_reacquire(state); ++ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); ++ i++; ++ } ++ } ++ ++ if (internal->status != DVBS2_FEC_LOCK) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); ++ iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg); ++ /* IQ Spectrum Inversion */ ++ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); ++ /* start acquistion process */ ++ stb0899_dvbs2_reacquire(state); ++ ++ /* Wait for demod lock (UWP and CSM) */ ++ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); ++ if (internal->status == DVBS2_DEMOD_LOCK) { ++ i = 0; ++ /* Demod Locked, check FEC */ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ /*try thrice for false locks, (UWP and CSM Locked but no FEC) */ ++ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { ++ /* Read the frequency offset*/ ++ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); ++ ++ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); ++ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); ++ ++ stb0899_dvbs2_reacquire(state); ++ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); ++ i++; ++ } ++ } ++/* ++ if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED) ++ pParams->IQLocked = !iqSpectrum; ++*/ ++ } ++ if (internal->status == DVBS2_FEC_LOCK) { ++ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !"); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); ++ modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; ++ pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; ++ ++ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && ++ (INRANGE(STB0899_QPSK_23, modcod, STB0899_QPSK_910)) && ++ (pilots == 1)) { ++ ++ stb0899_dvbs2_init_csm(state, pilots, modcod); ++ /* Wait for UWP,CSM and data LOCK 20ms max */ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ ++ i = 0; ++ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ i++; ++ } ++ } ++ ++ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && ++ (INRANGE(STB0899_QPSK_12, modcod, STB0899_QPSK_35)) && ++ (pilots == 1)) { ++ ++ /* Equalizer Disable update */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); ++ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); ++ } ++ ++ /* slow down the Equalizer once locked */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); ++ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0x02); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); ++ ++ /* Store signal parameters */ ++ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); ++ ++ /* sign extend 30 bit value before using it in calculations */ ++ if (offsetfreq & (1 << 29)) ++ offsetfreq |= -1 << 30; ++ ++ offsetfreq = offsetfreq / ((1 << 30) / 1000); ++ offsetfreq *= (internal->master_clk / 1000000); ++ ++ /* store current inversion for next run */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); ++ if (STB0899_GETFIELD(SPECTRUM_INVERT, reg)) ++ internal->inversion = IQ_SWAP_ON; ++ else ++ internal->inversion = IQ_SWAP_OFF; ++ ++ internal->freq = internal->freq + offsetfreq; ++ internal->srate = stb0899_dvbs2_get_srate(state); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); ++ internal->modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; ++ internal->pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; ++ internal->frame_length = (STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 1) & 0x01; ++ ++ /* Set IF AGC to tracking */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); ++ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 3); ++ ++ /* if QPSK 1/2,QPSK 3/5 or QPSK 2/3 set IF AGC reference to 16 otherwise 32*/ ++ if (INRANGE(STB0899_QPSK_12, internal->modcod, STB0899_QPSK_23)) ++ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 16); ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); ++ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 7); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); ++ } ++ ++ /* Release Stream Merger Reset */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ return internal->status; ++} +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/stb0899_drv.c linux-openelec/drivers/media/dvb-frontends/stb0899_drv.c +--- linux-3.14.36/drivers/media/dvb-frontends/stb0899_drv.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/dvb-frontends/stb0899_drv.c 2015-07-24 18:03:30.136842002 -0500 +@@ -981,6 +981,16 @@ + + *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val); + *strength += 750; ++ ++ const int MIN_STRENGTH_DVBS = 0; ++ const int MAX_STRENGTH_DVBS = 680; ++ if (*strength < MIN_STRENGTH_DVBS) ++ *strength = 0; ++ else if(*strength > MAX_STRENGTH_DVBS) ++ *strength = 0xFFFF; ++ else ++ *strength = (*strength - MIN_STRENGTH_DVBS) * 0xFFFF / (MAX_STRENGTH_DVBS - MIN_STRENGTH_DVBS); ++ + dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm", + val & 0xff, *strength); + } +@@ -993,6 +1003,7 @@ + + *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val); + *strength += 950; ++ *strength = *strength << 4; + dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm", + val & 0x3fff, *strength); + } +@@ -1026,6 +1037,16 @@ + val = MAKEWORD16(buf[0], buf[1]); + + *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val); ++ ++ const int MIN_SNR_DVBS = 0; ++ const int MAX_SNR_DVBS = 200; ++ if (*snr < MIN_SNR_DVBS) ++ *snr = 0; ++ else if(*snr > MAX_SNR_DVBS) ++ *snr = 0xFFFF; ++ else ++ *snr = (*snr - MIN_SNR_DVBS) * 0xFFFF / (MAX_SNR_DVBS - MIN_SNR_DVBS); ++ + dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n", + buf[0], buf[1], val, *snr); + } +@@ -1050,6 +1071,16 @@ + val = (quantn - estn) / 10; + } + *snr = val; ++ ++ const int MIN_SNR_DVBS2 = 10; ++ const int MAX_SNR_DVBS2 = 70; ++ if (*snr < MIN_SNR_DVBS2) ++ *snr = 0; ++ else if(*snr > MAX_SNR_DVBS2) ++ *snr = 0xFFFF; ++ else ++ *snr = (*snr - MIN_SNR_DVBS2) * 0xFFFF / (MAX_SNR_DVBS2 - MIN_SNR_DVBS2); ++ + dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm", + quant, quantn, est, estn, val); + } +@@ -1591,7 +1622,7 @@ + .frequency_max = 2150000, + .frequency_stepsize = 0, + .frequency_tolerance = 0, +- .symbol_rate_min = 5000000, ++ .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + + .caps = FE_CAN_INVERSION_AUTO | +diff -Nur linux-3.14.36/drivers/media/dvb-frontends/stb0899_drv.c.orig linux-openelec/drivers/media/dvb-frontends/stb0899_drv.c.orig +--- linux-3.14.36/drivers/media/dvb-frontends/stb0899_drv.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/dvb-frontends/stb0899_drv.c.orig 2015-07-24 18:03:30.072842002 -0500 +@@ -0,0 +1,1661 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "dvb_frontend.h" ++ ++#include "stb0899_drv.h" ++#include "stb0899_priv.h" ++#include "stb0899_reg.h" ++ ++/* Max transfer size done by I2C transfer functions */ ++#define MAX_XFER_SIZE 64 ++ ++static unsigned int verbose = 0;//1; ++module_param(verbose, int, 0644); ++ ++/* C/N in dB/10, NIRM/NIRL */ ++static const struct stb0899_tab stb0899_cn_tab[] = { ++ { 200, 2600 }, ++ { 190, 2700 }, ++ { 180, 2860 }, ++ { 170, 3020 }, ++ { 160, 3210 }, ++ { 150, 3440 }, ++ { 140, 3710 }, ++ { 130, 4010 }, ++ { 120, 4360 }, ++ { 110, 4740 }, ++ { 100, 5190 }, ++ { 90, 5670 }, ++ { 80, 6200 }, ++ { 70, 6770 }, ++ { 60, 7360 }, ++ { 50, 7970 }, ++ { 40, 8250 }, ++ { 30, 9000 }, ++ { 20, 9450 }, ++ { 15, 9600 }, ++}; ++ ++/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10. ++ * As measured, connected to a modulator. ++ * -8.0 to -50.0 dBm directly connected, ++ * -52.0 to -74.8 with extra attenuation. ++ * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm. ++ * Crude linear extrapolation below -84.8dBm and above -8.0dBm. ++ */ ++static const struct stb0899_tab stb0899_dvbsrf_tab[] = { ++ { -750, -128 }, ++ { -748, -94 }, ++ { -745, -92 }, ++ { -735, -90 }, ++ { -720, -87 }, ++ { -670, -77 }, ++ { -640, -70 }, ++ { -610, -62 }, ++ { -600, -60 }, ++ { -590, -56 }, ++ { -560, -41 }, ++ { -540, -25 }, ++ { -530, -17 }, ++ { -520, -11 }, ++ { -500, 1 }, ++ { -490, 6 }, ++ { -480, 10 }, ++ { -440, 22 }, ++ { -420, 27 }, ++ { -400, 31 }, ++ { -380, 34 }, ++ { -340, 40 }, ++ { -320, 43 }, ++ { -280, 48 }, ++ { -250, 52 }, ++ { -230, 55 }, ++ { -180, 61 }, ++ { -140, 66 }, ++ { -90, 73 }, ++ { -80, 74 }, ++ { 500, 127 } ++}; ++ ++/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10. ++ * As measured, connected to a modulator. ++ * -8.0 to -50.1 dBm directly connected, ++ * -53.0 to -76.6 with extra attenuation. ++ * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm. ++ * Crude linear extrapolation below -76.6dBm and above -8.0dBm. ++ */ ++static const struct stb0899_tab stb0899_dvbs2rf_tab[] = { ++ { 700, 0 }, ++ { -80, 3217 }, ++ { -150, 3893 }, ++ { -190, 4217 }, ++ { -240, 4621 }, ++ { -280, 4945 }, ++ { -320, 5273 }, ++ { -350, 5545 }, ++ { -370, 5741 }, ++ { -410, 6147 }, ++ { -450, 6671 }, ++ { -490, 7413 }, ++ { -501, 7665 }, ++ { -530, 8767 }, ++ { -560, 10219 }, ++ { -580, 10939 }, ++ { -590, 11518 }, ++ { -600, 11723 }, ++ { -650, 12659 }, ++ { -690, 13219 }, ++ { -730, 13645 }, ++ { -750, 13909 }, ++ { -766, 14153 }, ++ { -950, 16383 } ++}; ++ ++/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/ ++static struct stb0899_tab stb0899_quant_tab[] = { ++ { 0, 0 }, ++ { 0, 100 }, ++ { 600, 200 }, ++ { 950, 299 }, ++ { 1200, 398 }, ++ { 1400, 501 }, ++ { 1560, 603 }, ++ { 1690, 700 }, ++ { 1810, 804 }, ++ { 1910, 902 }, ++ { 2000, 1000 }, ++ { 2080, 1096 }, ++ { 2160, 1202 }, ++ { 2230, 1303 }, ++ { 2350, 1496 }, ++ { 2410, 1603 }, ++ { 2460, 1698 }, ++ { 2510, 1799 }, ++ { 2600, 1995 }, ++ { 2650, 2113 }, ++ { 2690, 2213 }, ++ { 2720, 2291 }, ++ { 2760, 2399 }, ++ { 2800, 2512 }, ++ { 2860, 2692 }, ++ { 2930, 2917 }, ++ { 2960, 3020 }, ++ { 3010, 3199 }, ++ { 3040, 3311 }, ++ { 3060, 3388 }, ++ { 3120, 3631 }, ++ { 3190, 3936 }, ++ { 3400, 5012 }, ++ { 3610, 6383 }, ++ { 3800, 7943 }, ++ { 4210, 12735 }, ++ { 4500, 17783 }, ++ { 4690, 22131 }, ++ { 4810, 25410 } ++}; ++ ++/* DVB-S2 Es/N0 estimate in dB/100 vs read value */ ++static struct stb0899_tab stb0899_est_tab[] = { ++ { 0, 0 }, ++ { 0, 1 }, ++ { 301, 2 }, ++ { 1204, 16 }, ++ { 1806, 64 }, ++ { 2408, 256 }, ++ { 2709, 512 }, ++ { 3010, 1023 }, ++ { 3311, 2046 }, ++ { 3612, 4093 }, ++ { 3823, 6653 }, ++ { 3913, 8185 }, ++ { 4010, 10233 }, ++ { 4107, 12794 }, ++ { 4214, 16368 }, ++ { 4266, 18450 }, ++ { 4311, 20464 }, ++ { 4353, 22542 }, ++ { 4391, 24604 }, ++ { 4425, 26607 }, ++ { 4457, 28642 }, ++ { 4487, 30690 }, ++ { 4515, 32734 }, ++ { 4612, 40926 }, ++ { 4692, 49204 }, ++ { 4816, 65464 }, ++ { 4913, 81846 }, ++ { 4993, 98401 }, ++ { 5060, 114815 }, ++ { 5118, 131220 }, ++ { 5200, 158489 }, ++ { 5300, 199526 }, ++ { 5400, 251189 }, ++ { 5500, 316228 }, ++ { 5600, 398107 }, ++ { 5720, 524807 }, ++ { 5721, 526017 }, ++}; ++ ++static int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg) ++{ ++ int ret; ++ ++ u8 b0[] = { reg >> 8, reg & 0xff }; ++ u8 buf; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 2 ++ },{ ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = &buf, ++ .len = 1 ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret != 2) { ++ if (ret != -ERESTARTSYS) ++ dprintk(state->verbose, FE_ERROR, 1, ++ "Read error, Reg=[0x%02x], Status=%d", ++ reg, ret); ++ ++ return ret < 0 ? ret : -EREMOTEIO; ++ } ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) ++ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x", ++ reg, buf); ++ ++ return (unsigned int)buf; ++} ++ ++int stb0899_read_reg(struct stb0899_state *state, unsigned int reg) ++{ ++ int result; ++ ++ result = _stb0899_read_reg(state, reg); ++ /* ++ * Bug ID 9: ++ * access to 0xf2xx/0xf6xx ++ * must be followed by read from 0xf2ff/0xf6ff. ++ */ ++ if ((reg != 0xf2ff) && (reg != 0xf6ff) && ++ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) ++ _stb0899_read_reg(state, (reg | 0x00ff)); ++ ++ return result; ++} ++ ++u32 _stb0899_read_s2reg(struct stb0899_state *state, ++ u32 stb0899_i2cdev, ++ u32 stb0899_base_addr, ++ u16 stb0899_reg_offset) ++{ ++ int status; ++ u32 data; ++ u8 buf[7] = { 0 }; ++ u16 tmpaddr; ++ ++ u8 buf_0[] = { ++ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ ++ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ ++ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ ++ }; ++ u8 buf_1[] = { ++ 0x00, /* 0xf3 Reg Offset */ ++ 0x00, /* 0x44 Reg Offset */ ++ }; ++ ++ struct i2c_msg msg_0 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_0, ++ .len = 6 ++ }; ++ ++ struct i2c_msg msg_1 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_1, ++ .len = 2 ++ }; ++ ++ struct i2c_msg msg_r = { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = buf, ++ .len = 4 ++ }; ++ ++ tmpaddr = stb0899_reg_offset & 0xff00; ++ if (!(stb0899_reg_offset & 0x8)) ++ tmpaddr = stb0899_reg_offset | 0x20; ++ ++ buf_1[0] = GETBYTE(tmpaddr, BYTE1); ++ buf_1[1] = GETBYTE(tmpaddr, BYTE0); ++ ++ status = i2c_transfer(state->i2c, &msg_0, 1); ++ if (status < 1) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); ++ ++ goto err; ++ } ++ ++ /* Dummy */ ++ status = i2c_transfer(state->i2c, &msg_1, 1); ++ if (status < 1) ++ goto err; ++ ++ status = i2c_transfer(state->i2c, &msg_r, 1); ++ if (status < 1) ++ goto err; ++ ++ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); ++ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); ++ ++ /* Actual */ ++ status = i2c_transfer(state->i2c, &msg_1, 1); ++ if (status < 1) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); ++ goto err; ++ } ++ ++ status = i2c_transfer(state->i2c, &msg_r, 1); ++ if (status < 1) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); ++ return status < 0 ? status : -EREMOTEIO; ++ } ++ ++ data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]); ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) ++ printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data); ++ ++ return data; ++ ++err: ++ return status < 0 ? status : -EREMOTEIO; ++} ++ ++int stb0899_write_s2reg(struct stb0899_state *state, ++ u32 stb0899_i2cdev, ++ u32 stb0899_base_addr, ++ u16 stb0899_reg_offset, ++ u32 stb0899_data) ++{ ++ int status; ++ ++ /* Base Address Setup */ ++ u8 buf_0[] = { ++ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ ++ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ ++ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ ++ }; ++ u8 buf_1[] = { ++ 0x00, /* 0xf3 Reg Offset */ ++ 0x00, /* 0x44 Reg Offset */ ++ 0x00, /* data */ ++ 0x00, /* data */ ++ 0x00, /* data */ ++ 0x00, /* data */ ++ }; ++ ++ struct i2c_msg msg_0 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_0, ++ .len = 6 ++ }; ++ ++ struct i2c_msg msg_1 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_1, ++ .len = 6 ++ }; ++ ++ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); ++ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); ++ buf_1[2] = GETBYTE(stb0899_data, BYTE0); ++ buf_1[3] = GETBYTE(stb0899_data, BYTE1); ++ buf_1[4] = GETBYTE(stb0899_data, BYTE2); ++ buf_1[5] = GETBYTE(stb0899_data, BYTE3); ++ ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) ++ printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data); ++ ++ status = i2c_transfer(state->i2c, &msg_0, 1); ++ if (unlikely(status < 1)) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); ++ goto err; ++ } ++ status = i2c_transfer(state->i2c, &msg_1, 1); ++ if (unlikely(status < 1)) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); ++ ++ return status < 0 ? status : -EREMOTEIO; ++ } ++ ++ return 0; ++ ++err: ++ return status < 0 ? status : -EREMOTEIO; ++} ++ ++int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count) ++{ ++ int status; ++ ++ u8 b0[] = { reg >> 8, reg & 0xff }; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 2 ++ },{ ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = buf, ++ .len = count ++ } ++ }; ++ ++ status = i2c_transfer(state->i2c, msg, 2); ++ if (status != 2) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n", ++ __func__, reg, count, status); ++ goto err; ++ } ++ /* ++ * Bug ID 9: ++ * access to 0xf2xx/0xf6xx ++ * must be followed by read from 0xf2ff/0xf6ff. ++ */ ++ if ((reg != 0xf2ff) && (reg != 0xf6ff) && ++ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) ++ _stb0899_read_reg(state, (reg | 0x00ff)); ++ ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) { ++ int i; ++ ++ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); ++ for (i = 0; i < count; i++) { ++ printk(" %02x", buf[i]); ++ } ++ printk("\n"); ++ } ++ ++ return 0; ++err: ++ return status < 0 ? status : -EREMOTEIO; ++} ++ ++int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count) ++{ ++ int ret; ++ u8 buf[MAX_XFER_SIZE]; ++ struct i2c_msg i2c_msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 + count ++ }; ++ ++ if (2 + count > sizeof(buf)) { ++ printk(KERN_WARNING ++ "%s: i2c wr reg=%04x: len=%d is too big!\n", ++ KBUILD_MODNAME, reg, count); ++ return -EINVAL; ++ } ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ memcpy(&buf[2], data, count); ++ ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) { ++ int i; ++ ++ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); ++ for (i = 0; i < count; i++) ++ printk(" %02x", data[i]); ++ printk("\n"); ++ } ++ ret = i2c_transfer(state->i2c, &i2c_msg, 1); ++ ++ /* ++ * Bug ID 9: ++ * access to 0xf2xx/0xf6xx ++ * must be followed by read from 0xf2ff/0xf6ff. ++ */ ++ if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) ++ stb0899_read_reg(state, (reg | 0x00ff)); ++ ++ if (ret != 1) { ++ if (ret != -ERESTARTSYS) ++ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", ++ reg, data[0], count, ret); ++ return ret < 0 ? ret : -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data) ++{ ++ return stb0899_write_regs(state, reg, &data, 1); ++} ++ ++/* ++ * stb0899_get_mclk ++ * Get STB0899 master clock frequency ++ * ExtClk: external clock frequency (Hz) ++ */ ++static u32 stb0899_get_mclk(struct stb0899_state *state) ++{ ++ u32 mclk = 0, div = 0; ++ ++ div = stb0899_read_reg(state, STB0899_NCOARSE); ++ mclk = (div + 1) * state->config->xtal_freq / 6; ++ dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk); ++ ++ return mclk; ++} ++ ++/* ++ * stb0899_set_mclk ++ * Set STB0899 master Clock frequency ++ * Mclk: demodulator master clock ++ * ExtClk: external clock frequency (Hz) ++ */ ++static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u8 mdiv = 0; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config); ++ mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1; ++ dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv); ++ ++ stb0899_write_reg(state, STB0899_NCOARSE, mdiv); ++ internal->master_clk = stb0899_get_mclk(state); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk); ++} ++ ++static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable) ++{ ++ struct stb0899_config *config = state->config; ++ const struct stb0899_postproc *postproc = config->postproc; ++ ++ /* post process event */ ++ if (postproc) { ++ if (enable) { ++ if (postproc[ctl].level == STB0899_GPIOPULLUP) ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x02); ++ else ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x82); ++ } else { ++ if (postproc[ctl].level == STB0899_GPIOPULLUP) ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x82); ++ else ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x02); ++ } ++ } ++ return 0; ++} ++ ++static void stb0899_release(struct dvb_frontend *fe) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend"); ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); ++ kfree(state); ++} ++ ++/* ++ * stb0899_get_alpha ++ * return: rolloff ++ */ ++static int stb0899_get_alpha(struct stb0899_state *state) ++{ ++ u8 mode_coeff; ++ ++ mode_coeff = stb0899_read_reg(state, STB0899_DEMOD); ++ ++ if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1) ++ return 20; ++ else ++ return 35; ++} ++ ++/* ++ * stb0899_init_calc ++ */ ++static void stb0899_init_calc(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ int master_clk; ++ u8 agc[2]; ++ u32 reg; ++ ++ /* Read registers (in burst mode) */ ++ stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */ ++ ++ /* Initial calculations */ ++ master_clk = stb0899_get_mclk(state); ++ internal->t_agc1 = 0; ++ internal->t_agc2 = 0; ++ internal->master_clk = master_clk; ++ internal->mclk = master_clk / 65536L; ++ internal->rolloff = stb0899_get_alpha(state); ++ ++ /* DVBS2 Initial calculations */ ++ /* Set AGC value to the middle */ ++ internal->agc_gain = 8154; ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); ++ STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA); ++ internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg); ++ ++ internal->center_freq = 0; ++ internal->av_frame_coarse = 10; ++ internal->av_frame_fine = 20; ++ internal->step_size = 2; ++/* ++ if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO)) ++ pParams->IQLocked = 0; ++ else ++ pParams->IQLocked = 1; ++*/ ++} ++ ++static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout) ++{ ++ u8 reg = 0; ++ unsigned long start = jiffies; ++ ++ while (1) { ++ reg = stb0899_read_reg(state, STB0899_DISSTATUS); ++ if (!STB0899_GETFIELD(FIFOFULL, reg)) ++ break; ++ if ((jiffies - start) > timeout) { ++ dprintk(state->verbose, FE_ERROR, 1, "timed out !!"); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ return 0; ++} ++ ++static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ u8 reg, i; ++ ++ if (cmd->msg_len > 8) ++ return -EINVAL; ++ ++ /* enable FIFO precharge */ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ for (i = 0; i < cmd->msg_len; i++) { ++ /* wait for FIFO empty */ ++ if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]); ++ } ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ msleep(100); ++ return 0; ++} ++ ++static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout) ++{ ++ u8 reg = 0; ++ unsigned long start = jiffies; ++ ++ while (!STB0899_GETFIELD(RXEND, reg)) { ++ reg = stb0899_read_reg(state, STB0899_DISRX_ST0); ++ if (jiffies - start > timeout) { ++ dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); ++ return -ETIMEDOUT; ++ } ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ u8 reg, length = 0, i; ++ int result; ++ ++ if (stb0899_wait_diseqc_rxidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ reg = stb0899_read_reg(state, STB0899_DISRX_ST0); ++ if (STB0899_GETFIELD(RXEND, reg)) { ++ ++ reg = stb0899_read_reg(state, STB0899_DISRX_ST1); ++ length = STB0899_GETFIELD(FIFOBYTENBR, reg); ++ ++ if (length > sizeof (reply->msg)) { ++ result = -EOVERFLOW; ++ goto exit; ++ } ++ reply->msg_len = length; ++ ++ /* extract data */ ++ for (i = 0; i < length; i++) ++ reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO); ++ } ++ ++ return 0; ++exit: ++ ++ return result; ++} ++ ++static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout) ++{ ++ u8 reg = 0; ++ unsigned long start = jiffies; ++ ++ while (!STB0899_GETFIELD(TXIDLE, reg)) { ++ reg = stb0899_read_reg(state, STB0899_DISSTATUS); ++ if (jiffies - start > timeout) { ++ dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); ++ return -ETIMEDOUT; ++ } ++ msleep(10); ++ } ++ return 0; ++} ++ ++static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ u8 reg, old_state; ++ ++ /* wait for diseqc idle */ ++ if (stb0899_wait_diseqc_txidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ old_state = reg; ++ /* set to burst mode */ ++ STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x03); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ switch (burst) { ++ case SEC_MINI_A: ++ /* unmodulated */ ++ stb0899_write_reg(state, STB0899_DISFIFO, 0x00); ++ break; ++ case SEC_MINI_B: ++ /* modulated */ ++ stb0899_write_reg(state, STB0899_DISFIFO, 0xff); ++ break; ++ } ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ /* wait for diseqc idle */ ++ if (stb0899_wait_diseqc_txidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ /* restore state */ ++ stb0899_write_reg(state, STB0899_DISCNTRL1, old_state); ++ ++ return 0; ++} ++ ++static int stb0899_diseqc_init(struct stb0899_state *state) ++{ ++/* ++ struct dvb_diseqc_slave_reply rx_data; ++*/ ++ u8 f22_tx, reg; ++ ++ u32 mclk, tx_freq = 22000;/* count = 0, i; */ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL2); ++ STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0); ++ stb0899_write_reg(state, STB0899_DISCNTRL2, reg); ++ ++ /* disable Tx spy */ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ ++ mclk = stb0899_get_mclk(state); ++ f22_tx = mclk / (tx_freq * 32); ++ stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */ ++ state->rx_freq = 20000; ++ ++ return 0; ++} ++ ++static int stb0899_sleep(struct dvb_frontend *fe) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++/* ++ u8 reg; ++*/ ++ dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))"); ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); ++ ++ return 0; ++} ++ ++static int stb0899_wakeup(struct dvb_frontend *fe) ++{ ++ int rc; ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI))) ++ return rc; ++ /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */ ++ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00))) ++ return rc; ++ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00))) ++ return rc; ++ ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1); ++ ++ return 0; ++} ++ ++static int stb0899_init(struct dvb_frontend *fe) ++{ ++ int i; ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_config *config = state->config; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... "); ++ ++ /* init device */ ++ dprintk(state->verbose, FE_DEBUG, 1, "init device"); ++ for (i = 0; config->init_dev[i].address != 0xffff; i++) ++ stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod"); ++ /* init S2 demod */ ++ for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++) ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, ++ config->init_s2_demod[i].base_address, ++ config->init_s2_demod[i].offset, ++ config->init_s2_demod[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod"); ++ /* init S1 demod */ ++ for (i = 0; config->init_s1_demod[i].address != 0xffff; i++) ++ stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC"); ++ /* init S2 fec */ ++ for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++) ++ stb0899_write_s2reg(state, STB0899_S2FEC, ++ config->init_s2_fec[i].base_address, ++ config->init_s2_fec[i].offset, ++ config->init_s2_fec[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init TST"); ++ /* init test */ ++ for (i = 0; config->init_tst[i].address != 0xffff; i++) ++ stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data); ++ ++ stb0899_init_calc(state); ++ stb0899_diseqc_init(state); ++ ++ return 0; ++} ++ ++static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val) ++{ ++ int res = 0; ++ int min = 0, med; ++ ++ if (val < tab[min].read) ++ res = tab[min].real; ++ else if (val >= tab[max].read) ++ res = tab[max].real; ++ else { ++ while ((max - min) > 1) { ++ med = (max + min) / 2; ++ if (val >= tab[min].read && val < tab[med].read) ++ max = med; ++ else ++ min = med; ++ } ++ res = ((val - tab[min].read) * ++ (tab[max].real - tab[min].real) / ++ (tab[max].read - tab[min].read)) + ++ tab[min].real; ++ } ++ ++ return res; ++} ++ ++static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ int val; ++ u32 reg; ++ *strength = 0; ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ if (internal->lock) { ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { ++ ++ reg = stb0899_read_reg(state, STB0899_AGCIQIN); ++ val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg); ++ ++ *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val); ++ *strength += 750; ++ dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm", ++ val & 0xff, *strength); ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ if (internal->lock) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN); ++ val = STB0899_GETFIELD(IF_AGC_GAIN, reg); ++ ++ *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val); ++ *strength += 950; ++ dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm", ++ val & 0x3fff, *strength); ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ unsigned int val, quant, quantn = -1, est, estn = -1; ++ u8 buf[2]; ++ u32 reg; ++ ++ *snr = 0; ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ if (internal->lock) { ++ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { ++ ++ stb0899_read_regs(state, STB0899_NIRM, buf, 2); ++ val = MAKEWORD16(buf[0], buf[1]); ++ ++ *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val); ++ dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n", ++ buf[0], buf[1], val, *snr); ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ if (internal->lock) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); ++ quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); ++ est = STB0899_GETFIELD(ESN0_EST, reg); ++ if (est == 1) ++ val = 301; /* C/N = 30.1 dB */ ++ else if (est == 2) ++ val = 270; /* C/N = 27.0 dB */ ++ else { ++ /* quantn = 100 * log(quant^2) */ ++ quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100); ++ /* estn = 100 * log(est) */ ++ estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est); ++ /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */ ++ val = (quantn - estn) / 10; ++ } ++ *snr = val; ++ dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm", ++ quant, quantn, est, estn, val); ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ u8 reg; ++ *status = 0; ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS"); ++ if (internal->lock) { ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { ++ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK"); ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; ++ ++ reg = stb0899_read_reg(state, STB0899_PLPARM); ++ if (STB0899_GETFIELD(VITCURPUN, reg)) { ++ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC"); ++ *status |= FE_HAS_VITERBI | FE_HAS_SYNC; ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); ++ } ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2"); ++ if (internal->lock) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); ++ if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) { ++ *status |= FE_HAS_CARRIER; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER"); ++ ++ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); ++ if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) { ++ *status |= FE_HAS_LOCK; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK"); ++ ++ } ++ if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) { ++ *status |= FE_HAS_VITERBI; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI"); ++ } ++ if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) { ++ *status |= FE_HAS_SYNC; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC"); ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); ++ } ++ } ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ++ * stb0899_get_error ++ * viterbi error for DVB-S/DSS ++ * packet error for DVB-S2 ++ * Bit Error Rate or Packet Error Rate * 10 ^ 7 ++ */ ++static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ u8 lsb, msb; ++ ++ *ber = 0; ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ if (internal->lock) { ++ lsb = stb0899_read_reg(state, STB0899_ECNT1L); ++ msb = stb0899_read_reg(state, STB0899_ECNT1M); ++ *ber = MAKEWORD16(msb, lsb); ++ /* Viterbi Check */ ++ if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) { ++ /* Error Rate */ ++ *ber *= 9766; ++ /* ber = ber * 10 ^ 7 */ ++ *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); ++ *ber /= 8; ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ if (internal->lock) { ++ lsb = stb0899_read_reg(state, STB0899_ECNT1L); ++ msb = stb0899_read_reg(state, STB0899_ECNT1M); ++ *ber = MAKEWORD16(msb, lsb); ++ /* ber = ber * 10 ^ 7 */ ++ *ber *= 10000000; ++ *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); ++ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); ++ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00); ++ break; ++ case SEC_VOLTAGE_18: ++ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02); ++ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); ++ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); ++ break; ++ case SEC_VOLTAGE_OFF: ++ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); ++ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82); ++ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ u8 div, reg; ++ ++ /* wait for diseqc idle */ ++ if (stb0899_wait_diseqc_txidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ div = (internal->master_clk / 100) / 5632; ++ div = (div + 5) / 10; ++ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66); ++ reg = stb0899_read_reg(state, STB0899_ACRPRESC); ++ STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03); ++ stb0899_write_reg(state, STB0899_ACRPRESC, reg); ++ stb0899_write_reg(state, STB0899_ACRDIV1, div); ++ break; ++ case SEC_TONE_OFF: ++ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ int i2c_stat; ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT); ++ if (i2c_stat < 0) ++ goto err; ++ ++ if (enable) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ..."); ++ i2c_stat |= STB0899_I2CTON; ++ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) ++ goto err; ++ } else { ++ dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ..."); ++ i2c_stat &= ~STB0899_I2CTON; ++ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) ++ goto err; ++ } ++ return 0; ++err: ++ dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed"); ++ return -EREMOTEIO; ++} ++ ++ ++static inline void CONVERT32(u32 x, char *str) ++{ ++ *str++ = (x >> 24) & 0xff; ++ *str++ = (x >> 16) & 0xff; ++ *str++ = (x >> 8) & 0xff; ++ *str++ = (x >> 0) & 0xff; ++ *str = '\0'; ++} ++ ++static int stb0899_get_dev_id(struct stb0899_state *state) ++{ ++ u8 chip_id, release; ++ u16 id; ++ u32 demod_ver = 0, fec_ver = 0; ++ char demod_str[5] = { 0 }; ++ char fec_str[5] = { 0 }; ++ ++ id = stb0899_read_reg(state, STB0899_DEV_ID); ++ dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id); ++ chip_id = STB0899_GETFIELD(CHIP_ID, id); ++ release = STB0899_GETFIELD(CHIP_REL, id); ++ ++ dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]", ++ chip_id, release); ++ ++ CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str); ++ ++ demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID); ++ dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); ++ CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); ++ fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); ++ if (! (chip_id > 0)) { ++ dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); ++ ++ return -ENODEV; ++ } ++ dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver); ++ ++ return 0; ++} ++ ++static void stb0899_set_delivery(struct stb0899_state *state) ++{ ++ u8 reg; ++ u8 stop_clk[2]; ++ ++ stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1); ++ stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2); ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S"); ++ /* FECM/Viterbi ON */ ++ reg = stb0899_read_reg(state, STB0899_FECM); ++ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); ++ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_FECM, reg); ++ ++ stb0899_write_reg(state, STB0899_RSULC, 0xb1); ++ stb0899_write_reg(state, STB0899_TSULC, 0x40); ++ stb0899_write_reg(state, STB0899_RSLLC, 0x42); ++ stb0899_write_reg(state, STB0899_TSLPL, 0x12); ++ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); ++ break; ++ case SYS_DVBS2: ++ /* FECM/Viterbi OFF */ ++ reg = stb0899_read_reg(state, STB0899_FECM); ++ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); ++ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0); ++ stb0899_write_reg(state, STB0899_FECM, reg); ++ ++ stb0899_write_reg(state, STB0899_RSULC, 0xb1); ++ stb0899_write_reg(state, STB0899_TSULC, 0x42); ++ stb0899_write_reg(state, STB0899_RSLLC, 0x40); ++ stb0899_write_reg(state, STB0899_TSLPL, 0x02); ++ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESLDPC, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0); ++ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0); ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0); ++ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0); ++ break; ++ case SYS_DSS: ++ /* FECM/Viterbi ON */ ++ reg = stb0899_read_reg(state, STB0899_FECM); ++ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1); ++ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_FECM, reg); ++ ++ stb0899_write_reg(state, STB0899_RSULC, 0xa1); ++ stb0899_write_reg(state, STB0899_TSULC, 0x61); ++ stb0899_write_reg(state, STB0899_RSLLC, 0x42); ++ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); ++ break; ++ default: ++ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); ++ break; ++ } ++ STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0); ++ stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2); ++} ++ ++/* ++ * stb0899_set_iterations ++ * set the LDPC iteration scale function ++ */ ++static void stb0899_set_iterations(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ s32 iter_scale; ++ u32 reg; ++ ++ iter_scale = 17 * (internal->master_clk / 1000); ++ iter_scale += 410000; ++ iter_scale /= (internal->srate / 1000000); ++ iter_scale /= 1000; ++ ++ if (iter_scale > config->ldpc_max_iter) ++ iter_scale = config->ldpc_max_iter; ++ ++ reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER); ++ STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale); ++ stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg); ++} ++ ++static enum dvbfe_search stb0899_search(struct dvb_frontend *fe) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_params *i_params = &state->params; ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ struct dtv_frontend_properties *props = &fe->dtv_property_cache; ++ ++ u32 SearchRange, gain; ++ ++ i_params->freq = props->frequency; ++ i_params->srate = props->symbol_rate; ++ state->delsys = props->delivery_system; ++ dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys); ++ ++ SearchRange = 10000000; ++ dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate); ++ /* checking Search Range is meaningless for a fixed 3 Mhz */ ++ if (INRANGE(i_params->srate, 1000000, 45000000)) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE"); ++ stb0899_set_delivery(state); ++ ++ if (state->config->tuner_set_rfsiggain) { ++ if (internal->srate > 15000000) ++ gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */ ++ else if (internal->srate > 5000000) ++ gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */ ++ else ++ gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */ ++ state->config->tuner_set_rfsiggain(fe, gain); ++ } ++ ++ if (i_params->srate <= 5000000) ++ stb0899_set_mclk(state, config->lo_clk); ++ else ++ stb0899_set_mclk(state, config->hi_clk); ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system"); ++ internal->freq = i_params->freq; ++ internal->srate = i_params->srate; ++ /* ++ * search = user search range + ++ * 500Khz + ++ * 2 * Tuner_step_size + ++ * 10% of the symbol rate ++ */ ++ internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5); ++ internal->derot_percent = 30; ++ ++ /* What to do for tuners having no bandwidth setup ? */ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ if (state->config->tuner_set_bandwidth) ++ state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10); ++ if (state->config->tuner_get_bandwidth) ++ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++ /* Set DVB-S1 AGC */ ++ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11); ++ ++ /* Run the search algorithm */ ++ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo .."); ++ if (stb0899_dvbs_algo(state) == RANGEOK) { ++ internal->lock = 1; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "-------------------------------------> DVB-S LOCK !"); ++ ++// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */ ++// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); ++// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); ++// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status); ++// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl); ++ ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } else { ++ internal->lock = 0; ++ ++ return DVBFE_ALGO_SEARCH_FAILED; ++ } ++ break; ++ case SYS_DVBS2: ++ internal->freq = i_params->freq; ++ internal->srate = i_params->srate; ++ internal->srch_range = SearchRange; ++ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ if (state->config->tuner_set_bandwidth) ++ state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange)); ++ if (state->config->tuner_get_bandwidth) ++ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++// pParams->SpectralInv = pSearch->IQ_Inversion; ++ ++ /* Set DVB-S2 AGC */ ++ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c); ++ ++ /* Set IterScale =f(MCLK,SYMB) */ ++ stb0899_set_iterations(state); ++ ++ /* Run the search algorithm */ ++ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo .."); ++ if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) { ++ internal->lock = 1; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "-------------------------------------> DVB-S2 LOCK !"); ++ ++// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */ ++// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); ++// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); ++ ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } else { ++ internal->lock = 0; ++ ++ return DVBFE_ALGO_SEARCH_FAILED; ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); ++ return DVBFE_ALGO_SEARCH_INVALID; ++ } ++ } ++ ++ return DVBFE_ALGO_SEARCH_ERROR; ++} ++ ++static int stb0899_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Get params"); ++ p->symbol_rate = internal->srate; ++ p->frequency = internal->freq; ++ ++ return 0; ++} ++ ++static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_CUSTOM; ++} ++ ++static struct dvb_frontend_ops stb0899_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, ++ .info = { ++ .name = "STB0899 Multistandard", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 0, ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_2G_MODULATION | ++ FE_CAN_QPSK ++ }, ++ ++ .release = stb0899_release, ++ .init = stb0899_init, ++ .sleep = stb0899_sleep, ++// .wakeup = stb0899_wakeup, ++ ++ .i2c_gate_ctrl = stb0899_i2c_gate_ctrl, ++ ++ .get_frontend_algo = stb0899_frontend_algo, ++ .search = stb0899_search, ++ .get_frontend = stb0899_get_frontend, ++ ++ ++ .read_status = stb0899_read_status, ++ .read_snr = stb0899_read_snr, ++ .read_signal_strength = stb0899_read_signal_strength, ++ .read_ber = stb0899_read_ber, ++ ++ .set_voltage = stb0899_set_voltage, ++ .set_tone = stb0899_set_tone, ++ ++ .diseqc_send_master_cmd = stb0899_send_diseqc_msg, ++ .diseqc_recv_slave_reply = stb0899_recv_slave_reply, ++ .diseqc_send_burst = stb0899_send_diseqc_burst, ++}; ++ ++struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c) ++{ ++ struct stb0899_state *state = NULL; ++ ++ state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ state->verbose = &verbose; ++ state->config = config; ++ state->i2c = i2c; ++ state->frontend.ops = stb0899_ops; ++ state->frontend.demodulator_priv = state; ++ /* use configured inversion as default -- we'll later autodetect inversion */ ++ state->internal.inversion = config->inversion; ++ ++ stb0899_wakeup(&state->frontend); ++ if (stb0899_get_dev_id(state) == -ENODEV) { ++ printk("%s: Exiting .. !\n", __func__); ++ goto error; ++ } ++ ++ printk("%s: Attaching STB0899 \n", __func__); ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(stb0899_attach); ++MODULE_PARM_DESC(verbose, "Set Verbosity level"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_DESCRIPTION("STB0899 Multi-Std frontend"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/cimax2.c linux-openelec/drivers/media/pci/cx23885/cimax2.c +--- linux-3.14.36/drivers/media/pci/cx23885/cimax2.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/cimax2.c 2015-07-24 18:03:30.120842002 -0500 +@@ -426,7 +426,7 @@ + return state->status; + } + +-int netup_ci_init(struct cx23885_tsport *port) ++int netup_ci_init(struct cx23885_tsport *port, bool isDVBSky) + { + struct netup_ci_state *state; + u8 cimax_init[34] = { +@@ -475,6 +475,11 @@ + goto err; + } + ++ if(isDVBSky) { ++ cimax_init[32] = 0x22; ++ cimax_init[33] = 0x00; ++ } ++ + port->port_priv = state; + + switch (port->nr) { +@@ -548,3 +553,19 @@ + dvb_ca_en50221_release(&state->ca); + kfree(state); + } ++ ++/* CI irq handler for DVBSky board*/ ++int dvbsky_ci_slot_status(struct cx23885_dev *dev) ++{ ++ struct cx23885_tsport *port = NULL; ++ struct netup_ci_state *state = NULL; ++ ++ ci_dbg_print("%s:\n", __func__); ++ ++ port = &dev->ts1; ++ state = port->port_priv; ++ schedule_work(&state->work); ++ ci_dbg_print("%s: Wakeup CI0\n", __func__); ++ ++ return 1; ++} +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/cimax2.h linux-openelec/drivers/media/pci/cx23885/cimax2.h +--- linux-3.14.36/drivers/media/pci/cx23885/cimax2.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/cimax2.h 2015-07-24 18:03:30.120842002 -0500 +@@ -41,7 +41,9 @@ + extern int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status); + extern int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open); +-extern int netup_ci_init(struct cx23885_tsport *port); ++extern int netup_ci_init(struct cx23885_tsport *port, bool isDVBSky); + extern void netup_ci_exit(struct cx23885_tsport *port); + ++extern int dvbsky_ci_slot_status(struct cx23885_dev *dev); ++ + #endif +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/cx23885-cards.c linux-openelec/drivers/media/pci/cx23885/cx23885-cards.c +--- linux-3.14.36/drivers/media/pci/cx23885/cx23885-cards.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/cx23885-cards.c 2015-07-24 18:03:30.124842002 -0500 +@@ -613,6 +613,34 @@ + .name = "TeVii S471", + .portb = CX23885_MPEG_DVB, + }, ++ [CX23885_BOARD_BST_PS8512] = { ++ .name = "Bestunar PS8512", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVBSKY_S950] = { ++ .name = "DVBSKY S950", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVBSKY_S952] = { ++ .name = "DVBSKY S952", ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVBSKY_S950_CI] = { ++ .ci_type = 3, ++ .name = "DVBSKY S950CI DVB-S2 CI", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVBSKY_C2800E_CI] = { ++ .ci_type = 3, ++ .name = "DVBSKY C2800E DVB-C CI", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVBSKY_T9580] = { ++ .name = "DVBSKY T9580", ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ }, + [CX23885_BOARD_PROF_8000] = { + .name = "Prof Revolution DVB-S2 8000", + .portb = CX23885_MPEG_DVB, +@@ -874,6 +902,30 @@ + .subdevice = 0x9022, + .card = CX23885_BOARD_TEVII_S471, + }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0x8512, ++ .card = CX23885_BOARD_BST_PS8512, ++ }, { ++ .subvendor = 0x4254, ++ .subdevice = 0x0950, ++ .card = CX23885_BOARD_DVBSKY_S950, ++ }, { ++ .subvendor = 0x4254, ++ .subdevice = 0x0952, ++ .card = CX23885_BOARD_DVBSKY_S952, ++ }, { ++ .subvendor = 0x4254, ++ .subdevice = 0x950C, ++ .card = CX23885_BOARD_DVBSKY_S950_CI, ++ }, { ++ .subvendor = 0x4254, ++ .subdevice = 0x2800, ++ .card = CX23885_BOARD_DVBSKY_C2800E_CI, ++ }, { ++ .subvendor = 0x4254, ++ .subdevice = 0x9580, ++ .card = CX23885_BOARD_DVBSKY_T9580, ++ }, { + .subvendor = 0x8000, + .subdevice = 0x3034, + .card = CX23885_BOARD_PROF_8000, +@@ -1483,9 +1535,84 @@ + cx_set(GP0_IO, 0x00040004); + mdelay(60); + break; ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_BST_PS8512: ++ cx23885_gpio_enable(dev, GPIO_2, 1); ++ cx23885_gpio_clear(dev, GPIO_2); ++ msleep(100); ++ cx23885_gpio_set(dev, GPIO_2); ++ break; ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_T9580: ++ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */ ++ ++ cx23885_gpio_enable(dev, GPIO_2, 1); ++ cx23885_gpio_enable(dev, GPIO_11, 1); ++ ++ cx23885_gpio_clear(dev, GPIO_2); ++ cx23885_gpio_clear(dev, GPIO_11); ++ msleep(100); ++ cx23885_gpio_set(dev, GPIO_2); ++ cx23885_gpio_set(dev, GPIO_11); ++ break; ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ /* GPIO-0 INTA from CiMax, input ++ GPIO-1 reset CiMax, output, high active ++ GPIO-2 reset demod, output, low active ++ GPIO-3 to GPIO-10 data/addr for CAM ++ GPIO-11 ~CS0 to CiMax1 ++ GPIO-12 ~CS1 to CiMax2 ++ GPIO-13 ADL0 load LSB addr ++ GPIO-14 ADL1 load MSB addr ++ GPIO-15 ~RDY from CiMax ++ GPIO-17 ~RD to CiMax ++ GPIO-18 ~WR to CiMax ++ */ ++ cx_set(GP0_IO, 0x00060002); /* GPIO 1/2 as output */ ++ cx_clear(GP0_IO, 0x00010004); /*GPIO 0 as input*/ ++ mdelay(100);/* reset delay */ ++ cx_set(GP0_IO, 0x00060004); /* GPIO as out, reset high */ ++ cx_clear(GP0_IO, 0x00010002); ++ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */ ++ /* GPIO-15 IN as ~ACK, rest as OUT */ ++ cx_write(MC417_OEN, 0x00001000); ++ /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */ ++ cx_write(MC417_RWD, 0x0000c300); ++ /* enable irq */ ++ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ ++ break; + } + } + ++static int cx23885_ir_patch(struct i2c_adapter *i2c, u8 reg, u8 mask) ++{ ++ struct i2c_msg msgs[2]; ++ u8 tx_buf[2], rx_buf[1]; ++ /* Write register address */ ++ tx_buf[0] = reg; ++ msgs[0].addr = 0x4c; ++ msgs[0].flags = 0; ++ msgs[0].len = 1; ++ msgs[0].buf = (char *) tx_buf; ++ /* Read data from register */ ++ msgs[1].addr = 0x4c; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = 1; ++ msgs[1].buf = (char *) rx_buf; ++ ++ i2c_transfer(i2c, msgs, 2); ++ ++ tx_buf[0] = reg; ++ tx_buf[1] = rx_buf[0] | mask; ++ msgs[0].addr = 0x4c; ++ msgs[0].flags = 0; ++ msgs[0].len = 2; ++ msgs[0].buf = (char *) tx_buf; ++ ++ return i2c_transfer(i2c, msgs, 1); ++} ++ + int cx23885_ir_init(struct cx23885_dev *dev) + { + static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = { +@@ -1573,6 +1700,23 @@ + v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, + ir_rx_pin_cfg_count, ir_rx_pin_cfg); + break; ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ case CX23885_BOARD_DVBSKY_T9580: ++ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); ++ if (dev->sd_ir == NULL) { ++ ret = -ENODEV; ++ break; ++ } ++ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, ++ ir_rx_pin_cfg_count, ir_rx_pin_cfg); ++ ++ cx23885_ir_patch(&(dev->i2c_bus[2].i2c_adap),0x1f,0x80); ++ cx23885_ir_patch(&(dev->i2c_bus[2].i2c_adap),0x23,0x80); ++ break; + case CX23885_BOARD_HAUPPAUGE_HVR1250: + if (!enable_885_ir) + break; +@@ -1602,6 +1746,12 @@ + cx23888_ir_remove(dev); + dev->sd_ir = NULL; + break; ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ case CX23885_BOARD_DVBSKY_T9580: + case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_HAUPPAUGE_HVR1250: +@@ -1649,6 +1799,12 @@ + if (dev->sd_ir) + cx23885_irq_add_enable(dev, PCI_MSK_IR); + break; ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ case CX23885_BOARD_DVBSKY_T9580: + case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_HAUPPAUGE_HVR1250: +@@ -1752,6 +1908,10 @@ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_TEVII_S471: + case CX23885_BOARD_DVBWORLD_2005: +@@ -1800,6 +1960,22 @@ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; ++ case CX23885_BOARD_DVBSKY_S952: ++ ts1->gen_ctrl_val = 0x5; /* Parallel */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ ts2->gen_ctrl_val = 0xe; /* Serial bus + punctured clock */ ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_DVBSKY_T9580: ++ ts1->gen_ctrl_val = 0x5; /* Parallel */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ ts2->gen_ctrl_val = 0x8; /* Serial bus */ ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; + case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_HAUPPAUGE_HVR1500: + case CX23885_BOARD_HAUPPAUGE_HVR1500Q: +@@ -1857,6 +2033,12 @@ + case CX23885_BOARD_MPX885: + case CX23885_BOARD_MYGICA_X8507: + case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ case CX23885_BOARD_DVBSKY_T9580: + case CX23885_BOARD_AVERMEDIA_HC81R: + case CX23885_BOARD_TBS_6980: + case CX23885_BOARD_TBS_6981: +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/cx23885-core.c linux-openelec/drivers/media/pci/cx23885/cx23885-core.c +--- linux-3.14.36/drivers/media/pci/cx23885/cx23885-core.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/cx23885-core.c 2015-07-24 18:03:30.124842002 -0500 +@@ -1909,6 +1909,10 @@ + (pci_status & PCI_MSK_GPIO0)) + handled += altera_ci_irq(dev); + ++ if (cx23885_boards[dev->board].ci_type == 3 && ++ (pci_status & PCI_MSK_GPIO0)) ++ handled += dvbsky_ci_slot_status(dev); ++ + if (ts1_status) { + if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) + handled += cx23885_irq_ts(ts1, ts1_status); +@@ -2141,6 +2145,8 @@ + cx23885_irq_add_enable(dev, PCI_MSK_GPIO1 | PCI_MSK_GPIO0); + break; + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: + cx23885_irq_add_enable(dev, PCI_MSK_GPIO0); + break; + } +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/cx23885-dvb.c linux-openelec/drivers/media/pci/cx23885/cx23885-dvb.c +--- linux-3.14.36/drivers/media/pci/cx23885/cx23885-dvb.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/cx23885-dvb.c 2015-07-24 18:03:30.128842002 -0500 +@@ -52,6 +52,8 @@ + #include "lnbh24.h" + #include "cx24116.h" + #include "cx24117.h" ++#include "dvbsky_m88ds3103.h" ++#include "dvbsky_m88dc2800.h" + #include "cimax2.h" + #include "lgs8gxx.h" + #include "netup-eeprom.h" +@@ -507,6 +509,93 @@ + .if_khz = 4000, + }; + ++/* bst control */ ++int bst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct cx23885_tsport *port = fe->dvb->priv; ++ struct cx23885_dev *dev = port->dev; ++ ++ cx23885_gpio_enable(dev, GPIO_1, 1); ++ cx23885_gpio_enable(dev, GPIO_0, 1); ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ cx23885_gpio_set(dev, GPIO_1); ++ cx23885_gpio_clear(dev, GPIO_0); ++ break; ++ case SEC_VOLTAGE_18: ++ cx23885_gpio_set(dev, GPIO_1); ++ cx23885_gpio_set(dev, GPIO_0); ++ break; ++ case SEC_VOLTAGE_OFF: ++ cx23885_gpio_clear(dev, GPIO_1); ++ cx23885_gpio_clear(dev, GPIO_0); ++ break; ++ } ++ return 0; ++} ++ ++int dvbsky_set_voltage_sec(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct cx23885_tsport *port = fe->dvb->priv; ++ struct cx23885_dev *dev = port->dev; ++ ++ cx23885_gpio_enable(dev, GPIO_12, 1); ++ cx23885_gpio_enable(dev, GPIO_13, 1); ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ cx23885_gpio_set(dev, GPIO_13); ++ cx23885_gpio_clear(dev, GPIO_12); ++ break; ++ case SEC_VOLTAGE_18: ++ cx23885_gpio_set(dev, GPIO_13); ++ cx23885_gpio_set(dev, GPIO_12); ++ break; ++ case SEC_VOLTAGE_OFF: ++ cx23885_gpio_clear(dev, GPIO_13); ++ cx23885_gpio_clear(dev, GPIO_12); ++ break; ++ } ++ return 0; ++} ++ ++/* bestunar single dvb-s2 */ ++static struct dvbsky_m88ds3103_config bst_ds3103_config = { ++ .demod_address = 0x68, ++ .ci_mode = 0, ++ .pin_ctrl = 0x82, ++ .ts_mode = 0, ++ .set_voltage = bst_set_voltage, ++}; ++/* DVBSKY dual dvb-s2 */ ++static struct dvbsky_m88ds3103_config dvbsky_ds3103_config_pri = { ++ .demod_address = 0x68, ++ .ci_mode = 0, ++ .pin_ctrl = 0x82, ++ .ts_mode = 0, ++ .set_voltage = bst_set_voltage, ++}; ++static struct dvbsky_m88ds3103_config dvbsky_ds3103_config_sec = { ++ .demod_address = 0x68, ++ .ci_mode = 0, ++ .pin_ctrl = 0x82, ++ .ts_mode = 1, ++ .set_voltage = dvbsky_set_voltage_sec, ++}; ++ ++static struct dvbsky_m88ds3103_config dvbsky_ds3103_ci_config = { ++ .demod_address = 0x68, ++ .ci_mode = 2, ++ .pin_ctrl = 0x82, ++ .ts_mode = 0, ++}; ++ ++static struct dvbsky_m88dc2800_config dvbsky_dc2800_config = { ++ .demod_address = 0x1c, ++ .ts_mode = 3, ++}; ++ + static struct stv090x_config prof_8000_stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, +@@ -1311,6 +1400,57 @@ + &tevii_ts2020_config, &i2c_bus->i2c_adap); + } + break; ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach, ++ &bst_ds3103_config, ++ &i2c_bus->i2c_adap); ++ break; ++ case CX23885_BOARD_DVBSKY_S952: ++ switch (port->nr) { ++ /* port B */ ++ case 1: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach, ++ &dvbsky_ds3103_config_pri, ++ &i2c_bus->i2c_adap); ++ break; ++ /* port C */ ++ case 2: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach, ++ &dvbsky_ds3103_config_sec, ++ &i2c_bus->i2c_adap); ++ break; ++ } ++ break; ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach, ++ &dvbsky_ds3103_ci_config, ++ &i2c_bus->i2c_adap); ++ break; ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(dvbsky_m88dc2800_attach, ++ &dvbsky_dc2800_config, ++ &i2c_bus->i2c_adap); ++ break; ++ case CX23885_BOARD_DVBSKY_T9580: ++ switch (port->nr) { ++ /* port B */ ++ case 1: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach, ++ &dvbsky_ds3103_config_pri, ++ &i2c_bus->i2c_adap); ++ break; ++ /* port C */ ++ case 2: ++ break; ++ } ++ break; + case CX23885_BOARD_PROF_8000: + i2c_bus = &dev->i2c_bus[0]; + +@@ -1386,7 +1526,7 @@ + printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n", + port->nr, port->frontends.adapter.proposed_mac); + +- netup_ci_init(port); ++ netup_ci_init(port, false); + break; + } + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: { +@@ -1413,6 +1553,41 @@ + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); + break; + } ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_T9580:{ ++ u8 eeprom[256]; /* 24C02 i2c eeprom */ ++ ++ if(port->nr > 2) ++ break; ++ ++ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; ++ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom)); ++ printk(KERN_INFO "DVBSKY PCIe MAC= %pM\n", eeprom + 0xc0+(port->nr-1)*8); ++ memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + ++ (port->nr-1)*8, 6); ++ break; ++ } ++ case CX23885_BOARD_DVBSKY_S950_CI: { ++ u8 eeprom[256]; /* 24C02 i2c eeprom */ ++ ++ if(port->nr > 2) ++ break; ++ ++ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; ++ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom)); ++ printk(KERN_INFO "DVBSKY PCIe MAC= %pM\n", eeprom + 0xc0+(port->nr-1)*8); ++ memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + ++ (port->nr-1)*8, 6); ++ ++ netup_ci_init(port, true); ++ break; ++ } ++ case CX23885_BOARD_DVBSKY_C2800E_CI: { ++ netup_ci_init(port, true); ++ break; ++ } + } + + return ret; +@@ -1495,6 +1670,8 @@ + + switch (port->dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: + netup_ci_exit(port); + break; + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/cx23885.h linux-openelec/drivers/media/pci/cx23885/cx23885.h +--- linux-3.14.36/drivers/media/pci/cx23885/cx23885.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/cx23885.h 2015-07-24 18:03:30.128842002 -0500 +@@ -97,6 +97,14 @@ + #define CX23885_BOARD_TBS_6980 41 + #define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42 + ++#define CX23885_BOARD_BASE_INDEX 43 ++#define CX23885_BOARD_BST_PS8512 (CX23885_BOARD_BASE_INDEX) ++#define CX23885_BOARD_DVBSKY_S952 (CX23885_BOARD_BASE_INDEX+1) ++#define CX23885_BOARD_DVBSKY_S950 (CX23885_BOARD_BASE_INDEX+2) ++#define CX23885_BOARD_DVBSKY_S950_CI (CX23885_BOARD_BASE_INDEX+3) ++#define CX23885_BOARD_DVBSKY_C2800E_CI (CX23885_BOARD_BASE_INDEX+4) ++#define CX23885_BOARD_DVBSKY_T9580 (CX23885_BOARD_BASE_INDEX+5) ++ + #define GPIO_0 0x00000001 + #define GPIO_1 0x00000002 + #define GPIO_2 0x00000004 +@@ -234,7 +242,7 @@ + */ + u32 clk_freq; + struct cx23885_input input[MAX_CX23885_INPUT]; +- int ci_type; /* for NetUP */ ++ int ci_type; /* 1 and 2 for NetUP, 3 for DVBSky. */ + /* Force bottom field first during DMA (888 workaround) */ + u32 force_bff; + }; +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/cx23885-input.c linux-openelec/drivers/media/pci/cx23885/cx23885-input.c +--- linux-3.14.36/drivers/media/pci/cx23885/cx23885-input.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/cx23885-input.c 2015-07-24 18:03:30.128842002 -0500 +@@ -89,6 +89,12 @@ + case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ case CX23885_BOARD_DVBSKY_T9580: + case CX23885_BOARD_MYGICA_X8507: + case CX23885_BOARD_TBS_6980: + case CX23885_BOARD_TBS_6981: +@@ -143,6 +149,12 @@ + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ case CX23885_BOARD_DVBSKY_T9580: + case CX23885_BOARD_MYGICA_X8507: + /* + * The IR controller on this board only returns pulse widths. +@@ -295,6 +307,18 @@ + /* A guess at the remote */ + rc_map = RC_MAP_TEVII_NEC; + break; ++ case CX23885_BOARD_BST_PS8512: ++ case CX23885_BOARD_DVBSKY_S950: ++ case CX23885_BOARD_DVBSKY_S952: ++ case CX23885_BOARD_DVBSKY_S950_CI: ++ case CX23885_BOARD_DVBSKY_C2800E_CI: ++ case CX23885_BOARD_DVBSKY_T9580: ++ /* Integrated CX2388[58] IR controller */ ++ driver_type = RC_DRIVER_IR_RAW; ++ allowed_protos = RC_BIT_ALL; ++ /* A guess at the remote */ ++ rc_map = RC_MAP_DVBSKY; ++ break; + case CX23885_BOARD_MYGICA_X8507: + /* Integrated CX23885 IR controller */ + driver_type = RC_DRIVER_IR_RAW; +diff -Nur linux-3.14.36/drivers/media/pci/cx23885/Kconfig linux-openelec/drivers/media/pci/cx23885/Kconfig +--- linux-3.14.36/drivers/media/pci/cx23885/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx23885/Kconfig 2015-07-24 18:03:30.120842002 -0500 +@@ -23,6 +23,8 @@ + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DVBSKY_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DVBSKY_M88DC2800 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24117 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT +diff -Nur linux-3.14.36/drivers/media/pci/cx88/cx88-cards.c linux-openelec/drivers/media/pci/cx88/cx88-cards.c +--- linux-3.14.36/drivers/media/pci/cx88/cx88-cards.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx88/cx88-cards.c 2015-07-24 18:03:30.128842002 -0500 +@@ -2314,6 +2314,18 @@ + } }, + .mpeg = CX88_MPEG_DVB, + }, ++ [CX88_BOARD_BST_PS8312] = { ++ .name = "Bestunar PS8312 DVB-S/S2", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, + }; + + /* ------------------------------------------------------------------ */ +@@ -2818,6 +2830,10 @@ + .subvendor = 0x1822, + .subdevice = 0x0023, + .card = CX88_BOARD_TWINHAN_VP1027_DVBS, ++ }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0x8312, ++ .card = CX88_BOARD_BST_PS8312, + }, + }; + +@@ -3551,6 +3567,12 @@ + cx_write(MO_SRST_IO, 1); + msleep(100); + break; ++ case CX88_BOARD_BST_PS8312: ++ cx_write(MO_GP1_IO, 0x808000); ++ msleep(100); ++ cx_write(MO_GP1_IO, 0x808080); ++ msleep(100); ++ break; + } /*end switch() */ + + +diff -Nur linux-3.14.36/drivers/media/pci/cx88/cx88-dvb.c linux-openelec/drivers/media/pci/cx88/cx88-dvb.c +--- linux-3.14.36/drivers/media/pci/cx88/cx88-dvb.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx88/cx88-dvb.c 2015-07-24 18:03:30.128842002 -0500 +@@ -54,6 +54,7 @@ + #include "stv0288.h" + #include "stb6000.h" + #include "cx24116.h" ++#include "dvbsky_m88ds3103.h" + #include "stv0900.h" + #include "stb6100.h" + #include "stb6100_proc.h" +@@ -459,6 +460,56 @@ + return core->prev_set_voltage(fe, voltage); + return 0; + } ++/*CX88_BOARD_BST_PS8312*/ ++static int bst_dvbs_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ cx_write(MO_GP1_IO, 0x111111); ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ cx_write(MO_GP1_IO, 0x020200); ++ break; ++ case SEC_VOLTAGE_18: ++ cx_write(MO_GP1_IO, 0x020202); ++ break; ++ case SEC_VOLTAGE_OFF: ++ cx_write(MO_GP1_IO, 0x111100); ++ break; ++ } ++ ++ if (core->prev_set_voltage) ++ return core->prev_set_voltage(fe, voltage); ++ return 0; ++} ++ ++static int bst_dvbs_set_voltage_v2(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ cx_write(MO_GP1_IO, 0x111101); ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ cx_write(MO_GP1_IO, 0x020200); ++ break; ++ case SEC_VOLTAGE_18: ++ ++ cx_write(MO_GP1_IO, 0x020202); ++ break; ++ case SEC_VOLTAGE_OFF: ++ ++ cx_write(MO_GP1_IO, 0x111110); ++ break; ++ } ++ ++ if (core->prev_set_voltage) ++ return core->prev_set_voltage(fe, voltage); ++ return 0; ++} + + static int vp1027_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +@@ -706,6 +757,11 @@ + .clk_out_div = 1, + }; + ++static struct dvbsky_m88ds3103_config dvbsky_ds3103_config = { ++ .demod_address = 0x68, ++ .set_ts_params = ds3000_set_ts_param, ++}; ++ + static const struct stv0900_config prof_7301_stv0900_config = { + .demod_address = 0x6a, + /* demod_mode = 0,*/ +@@ -1487,6 +1543,35 @@ + tevii_dvbs_set_voltage; + } + break; ++ case CX88_BOARD_BST_PS8312: ++ fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach, ++ &dvbsky_ds3103_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL){ ++ int ret; ++ u8 b0[] = { 0x60 }; ++ u8 b1[2] = { 0 }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = 0x50, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, { ++ .addr = 0x50, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 2 ++ } ++ }; ++ ret = i2c_transfer(&core->i2c_adap, msg, 2); ++ printk("PS8312: config = %02x, %02x", b1[0],b1[1]); ++ if(b1[0] == 0xaa) ++ fe0->dvb.frontend->ops.set_voltage = bst_dvbs_set_voltage_v2; ++ else ++ fe0->dvb.frontend->ops.set_voltage = bst_dvbs_set_voltage; ++ } ++ break; + case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_TBS_8920: + case CX88_BOARD_PROF_7300: +diff -Nur linux-3.14.36/drivers/media/pci/cx88/cx88.h linux-openelec/drivers/media/pci/cx88/cx88.h +--- linux-3.14.36/drivers/media/pci/cx88/cx88.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx88/cx88.h 2015-07-24 18:03:30.128842002 -0500 +@@ -237,6 +237,7 @@ + #define CX88_BOARD_WINFAST_DTV1800H_XC4000 88 + #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89 + #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90 ++#define CX88_BOARD_BST_PS8312 91 + + enum cx88_itype { + CX88_VMUX_COMPOSITE1 = 1, +diff -Nur linux-3.14.36/drivers/media/pci/cx88/cx88-input.c linux-openelec/drivers/media/pci/cx88/cx88-input.c +--- linux-3.14.36/drivers/media/pci/cx88/cx88-input.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx88/cx88-input.c 2015-07-24 18:03:30.128842002 -0500 +@@ -419,6 +419,10 @@ + rc_type = RC_BIT_NEC; + ir->sampling = 0xff00; /* address */ + break; ++ case CX88_BOARD_BST_PS8312: ++ ir_codes = RC_MAP_DVBSKY; ++ ir->sampling = 0xff00; /* address */ ++ break; + } + + if (!ir_codes) { +diff -Nur linux-3.14.36/drivers/media/pci/cx88/Kconfig linux-openelec/drivers/media/pci/cx88/Kconfig +--- linux-3.14.36/drivers/media/pci/cx88/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/pci/cx88/Kconfig 2015-07-24 18:03:30.128842002 -0500 +@@ -57,6 +57,7 @@ + select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DVBSKY_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT +diff -Nur linux-3.14.36/drivers/media/platform/Kconfig linux-openelec/drivers/media/platform/Kconfig +--- linux-3.14.36/drivers/media/platform/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/platform/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -115,6 +115,21 @@ + To compile this driver as a module, choose M here: the module + will be called s3c-camif. + ++config VIDEO_MXC_OUTPUT ++ tristate "MXC Video For Linux Video Output" ++ depends on VIDEO_DEV && ARCH_MXC && FB_MXC ++ select VIDEOBUF_DMA_CONTIG ++ ---help--- ++ This is the video4linux2 output driver based on MXC module. ++ ++config VIDEO_MXC_CAPTURE ++ tristate "MXC Video For Linux Video Capture" ++ depends on VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE ++ ---help--- ++ This is the video4linux2 capture driver based on i.MX video-in module. ++ ++source "drivers/media/platform/mxc/capture/Kconfig" ++source "drivers/media/platform/mxc/output/Kconfig" + source "drivers/media/platform/soc_camera/Kconfig" + source "drivers/media/platform/exynos4-is/Kconfig" + source "drivers/media/platform/s5p-tv/Kconfig" +diff -Nur linux-3.14.36/drivers/media/platform/Makefile linux-openelec/drivers/media/platform/Makefile +--- linux-3.14.36/drivers/media/platform/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/platform/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -51,4 +51,7 @@ + + obj-$(CONFIG_ARCH_OMAP) += omap/ + ++obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/capture/ ++obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/ ++ + ccflags-y += -I$(srctree)/drivers/media/i2c +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/adv7180.c linux-openelec/drivers/media/platform/mxc/capture/adv7180.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/adv7180.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/adv7180.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1344 @@ ++/* ++ * Copyright 2005-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file adv7180.c ++ * ++ * @brief Analog Device ADV7180 video decoder functions ++ * ++ * @ingroup Camera ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++ ++#define ADV7180_VOLTAGE_ANALOG 1800000 ++#define ADV7180_VOLTAGE_DIGITAL_CORE 1800000 ++#define ADV7180_VOLTAGE_DIGITAL_IO 3300000 ++#define ADV7180_VOLTAGE_PLL 1800000 ++ ++static struct regulator *dvddio_regulator; ++static struct regulator *dvdd_regulator; ++static struct regulator *avdd_regulator; ++static struct regulator *pvdd_regulator; ++static int pwn_gpio; ++ ++static int adv7180_probe(struct i2c_client *adapter, ++ const struct i2c_device_id *id); ++static int adv7180_detach(struct i2c_client *client); ++ ++static const struct i2c_device_id adv7180_id[] = { ++ {"adv7180", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, adv7180_id); ++ ++static struct i2c_driver adv7180_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "adv7180", ++ }, ++ .probe = adv7180_probe, ++ .remove = adv7180_detach, ++ .id_table = adv7180_id, ++}; ++ ++/*! ++ * Maintains the information on the current state of the sensor. ++ */ ++struct sensor { ++ struct sensor_data sen; ++ v4l2_std_id std_id; ++} adv7180_data; ++ ++ ++/*! List of input video formats supported. The video formats is corresponding ++ * with v4l2 id in video_fmt_t ++ */ ++typedef enum { ++ ADV7180_NTSC = 0, /*!< Locked on (M) NTSC video signal. */ ++ ADV7180_PAL, /*!< (B, G, H, I, N)PAL video signal. */ ++ ADV7180_NOT_LOCKED, /*!< Not locked on a signal. */ ++} video_fmt_idx; ++ ++/*! Number of video standards supported (including 'not locked' signal). */ ++#define ADV7180_STD_MAX (ADV7180_PAL + 1) ++ ++/*! Video format structure. */ ++typedef struct { ++ int v4l2_id; /*!< Video for linux ID. */ ++ char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */ ++ u16 raw_width; /*!< Raw width. */ ++ u16 raw_height; /*!< Raw height. */ ++ u16 active_width; /*!< Active width. */ ++ u16 active_height; /*!< Active height. */ ++} video_fmt_t; ++ ++/*! Description of video formats supported. ++ * ++ * PAL: raw=720x625, active=720x576. ++ * NTSC: raw=720x525, active=720x480. ++ */ ++static video_fmt_t video_fmts[] = { ++ { /*! NTSC */ ++ .v4l2_id = V4L2_STD_NTSC, ++ .name = "NTSC", ++ .raw_width = 720, /* SENS_FRM_WIDTH */ ++ .raw_height = 525, /* SENS_FRM_HEIGHT */ ++ .active_width = 720, /* ACT_FRM_WIDTH plus 1 */ ++ .active_height = 480, /* ACT_FRM_WIDTH plus 1 */ ++ }, ++ { /*! (B, G, H, I, N) PAL */ ++ .v4l2_id = V4L2_STD_PAL, ++ .name = "PAL", ++ .raw_width = 720, ++ .raw_height = 625, ++ .active_width = 720, ++ .active_height = 576, ++ }, ++ { /*! Unlocked standard */ ++ .v4l2_id = V4L2_STD_ALL, ++ .name = "Autodetect", ++ .raw_width = 720, ++ .raw_height = 625, ++ .active_width = 720, ++ .active_height = 576, ++ }, ++}; ++ ++/*!* Standard index of ADV7180. */ ++static video_fmt_idx video_idx = ADV7180_PAL; ++ ++/*! @brief This mutex is used to provide mutual exclusion. ++ * ++ * Create a mutex that can be used to provide mutually exclusive ++ * read/write access to the globally accessible data structures ++ * and variables that were defined above. ++ */ ++static DEFINE_MUTEX(mutex); ++ ++#define IF_NAME "adv7180" ++#define ADV7180_INPUT_CTL 0x00 /* Input Control */ ++#define ADV7180_STATUS_1 0x10 /* Status #1 */ ++#define ADV7180_BRIGHTNESS 0x0a /* Brightness */ ++#define ADV7180_IDENT 0x11 /* IDENT */ ++#define ADV7180_VSYNC_FIELD_CTL_1 0x31 /* VSYNC Field Control #1 */ ++#define ADV7180_MANUAL_WIN_CTL 0x3d /* Manual Window Control */ ++#define ADV7180_SD_SATURATION_CB 0xe3 /* SD Saturation Cb */ ++#define ADV7180_SD_SATURATION_CR 0xe4 /* SD Saturation Cr */ ++#define ADV7180_PWR_MNG 0x0f /* Power Management */ ++ ++/* supported controls */ ++/* This hasn't been fully implemented yet. ++ * This is how it should work, though. */ ++static struct v4l2_queryctrl adv7180_qctrl[] = { ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .minimum = 0, /* check this value */ ++ .maximum = 255, /* check this value */ ++ .step = 1, /* check this value */ ++ .default_value = 127, /* check this value */ ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .minimum = 0, /* check this value */ ++ .maximum = 255, /* check this value */ ++ .step = 0x1, /* check this value */ ++ .default_value = 127, /* check this value */ ++ .flags = 0, ++ } ++}; ++ ++static inline void adv7180_power_down(int enable) ++{ ++ gpio_set_value_cansleep(pwn_gpio, !enable); ++ msleep(2); ++} ++ ++static int adv7180_regulator_enable(struct device *dev) ++{ ++ int ret = 0; ++ ++ dvddio_regulator = devm_regulator_get(dev, "DOVDD"); ++ ++ if (!IS_ERR(dvddio_regulator)) { ++ regulator_set_voltage(dvddio_regulator, ++ ADV7180_VOLTAGE_DIGITAL_IO, ++ ADV7180_VOLTAGE_DIGITAL_IO); ++ ret = regulator_enable(dvddio_regulator); ++ if (ret) { ++ dev_err(dev, "set io voltage failed\n"); ++ return ret; ++ } else { ++ dev_dbg(dev, "set io voltage ok\n"); ++ } ++ } else { ++ dev_warn(dev, "cannot get io voltage\n"); ++ } ++ ++ dvdd_regulator = devm_regulator_get(dev, "DVDD"); ++ if (!IS_ERR(dvdd_regulator)) { ++ regulator_set_voltage(dvdd_regulator, ++ ADV7180_VOLTAGE_DIGITAL_CORE, ++ ADV7180_VOLTAGE_DIGITAL_CORE); ++ ret = regulator_enable(dvdd_regulator); ++ if (ret) { ++ dev_err(dev, "set core voltage failed\n"); ++ return ret; ++ } else { ++ dev_dbg(dev, "set core voltage ok\n"); ++ } ++ } else { ++ dev_warn(dev, "cannot get core voltage\n"); ++ } ++ ++ avdd_regulator = devm_regulator_get(dev, "AVDD"); ++ if (!IS_ERR(avdd_regulator)) { ++ regulator_set_voltage(avdd_regulator, ++ ADV7180_VOLTAGE_ANALOG, ++ ADV7180_VOLTAGE_ANALOG); ++ ret = regulator_enable(avdd_regulator); ++ if (ret) { ++ dev_err(dev, "set analog voltage failed\n"); ++ return ret; ++ } else { ++ dev_dbg(dev, "set analog voltage ok\n"); ++ } ++ } else { ++ dev_warn(dev, "cannot get analog voltage\n"); ++ } ++ ++ pvdd_regulator = devm_regulator_get(dev, "PVDD"); ++ if (!IS_ERR(pvdd_regulator)) { ++ regulator_set_voltage(pvdd_regulator, ++ ADV7180_VOLTAGE_PLL, ++ ADV7180_VOLTAGE_PLL); ++ ret = regulator_enable(pvdd_regulator); ++ if (ret) { ++ dev_err(dev, "set pll voltage failed\n"); ++ return ret; ++ } else { ++ dev_dbg(dev, "set pll voltage ok\n"); ++ } ++ } else { ++ dev_warn(dev, "cannot get pll voltage\n"); ++ } ++ ++ return ret; ++} ++ ++ ++/*********************************************************************** ++ * I2C transfert. ++ ***********************************************************************/ ++ ++/*! Read one register from a ADV7180 i2c slave device. ++ * ++ * @param *reg register in the device we wish to access. ++ * ++ * @return 0 if success, an error code otherwise. ++ */ ++static inline int adv7180_read(u8 reg) ++{ ++ int val; ++ ++ val = i2c_smbus_read_byte_data(adv7180_data.sen.i2c_client, reg); ++ if (val < 0) { ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ "%s:read reg error: reg=%2x\n", __func__, reg); ++ return -1; ++ } ++ return val; ++} ++ ++/*! Write one register of a ADV7180 i2c slave device. ++ * ++ * @param *reg register in the device we wish to access. ++ * ++ * @return 0 if success, an error code otherwise. ++ */ ++static int adv7180_write_reg(u8 reg, u8 val) ++{ ++ s32 ret; ++ ++ ret = i2c_smbus_write_byte_data(adv7180_data.sen.i2c_client, reg, val); ++ if (ret < 0) { ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ "%s:write reg error:reg=%2x,val=%2x\n", __func__, ++ reg, val); ++ return -1; ++ } ++ return 0; ++} ++ ++/*********************************************************************** ++ * mxc_v4l2_capture interface. ++ ***********************************************************************/ ++ ++/*! ++ * Return attributes of current video standard. ++ * Since this device autodetects the current standard, this function also ++ * sets the values that need to be changed if the standard changes. ++ * There is no set std equivalent function. ++ * ++ * @return None. ++ */ ++static void adv7180_get_std(v4l2_std_id *std) ++{ ++ int tmp; ++ int idx; ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_get_std\n"); ++ ++ /* Read the AD_RESULT to get the detect output video standard */ ++ tmp = adv7180_read(ADV7180_STATUS_1) & 0x70; ++ ++ mutex_lock(&mutex); ++ if (tmp == 0x40) { ++ /* PAL */ ++ *std = V4L2_STD_PAL; ++ idx = ADV7180_PAL; ++ } else if (tmp == 0) { ++ /*NTSC*/ ++ *std = V4L2_STD_NTSC; ++ idx = ADV7180_NTSC; ++ } else { ++ *std = V4L2_STD_ALL; ++ idx = ADV7180_NOT_LOCKED; ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ "Got invalid video standard!\n"); ++ } ++ mutex_unlock(&mutex); ++ ++ /* This assumes autodetect which this device uses. */ ++ if (*std != adv7180_data.std_id) { ++ video_idx = idx; ++ adv7180_data.std_id = *std; ++ adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width; ++ adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height; ++ } ++} ++ ++/*********************************************************************** ++ * IOCTL Functions from v4l2_int_ioctl_desc. ++ ***********************************************************************/ ++ ++/*! ++ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num ++ * s: pointer to standard V4L2 device structure ++ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure ++ * ++ * Gets slave interface parameters. ++ * Calculates the required xclk value to support the requested ++ * clock parameters in p. This value is returned in the p ++ * parameter. ++ * ++ * vidioc_int_g_ifparm returns platform-specific information about the ++ * interface settings used by the sensor. ++ * ++ * Called on open. ++ */ ++static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) ++{ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_ifparm\n"); ++ ++ if (s == NULL) { ++ pr_err(" ERROR!! no slave device set!\n"); ++ return -1; ++ } ++ ++ /* Initialize structure to 0s then set any non-0 values. */ ++ memset(p, 0, sizeof(*p)); ++ p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */ ++ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; ++ p->u.bt656.nobt_hs_inv = 1; ++ p->u.bt656.bt_sync_correct = 1; ++ ++ /* ADV7180 has a dedicated clock so no clock settings needed. */ ++ ++ return 0; ++} ++ ++/*! ++ * Sets the camera power. ++ * ++ * s pointer to the camera device ++ * on if 1, power is to be turned on. 0 means power is to be turned off ++ * ++ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num ++ * @s: pointer to standard V4L2 device structure ++ * @on: power state to which device is to be set ++ * ++ * Sets devices power state to requrested state, if possible. ++ * This is called on open, close, suspend and resume. ++ */ ++static int ioctl_s_power(struct v4l2_int_device *s, int on) ++{ ++ struct sensor *sensor = s->priv; ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_s_power\n"); ++ ++ if (on && !sensor->sen.on) { ++ if (adv7180_write_reg(ADV7180_PWR_MNG, 0x04) != 0) ++ return -EIO; ++ ++ /* ++ * FIXME:Additional 400ms to wait the chip to be stable? ++ * This is a workaround for preview scrolling issue. ++ */ ++ msleep(400); ++ } else if (!on && sensor->sen.on) { ++ if (adv7180_write_reg(ADV7180_PWR_MNG, 0x24) != 0) ++ return -EIO; ++ } ++ ++ sensor->sen.on = on; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure ++ * ++ * Returns the sensor's video CAPTURE parameters. ++ */ ++static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ struct sensor *sensor = s->priv; ++ struct v4l2_captureparm *cparm = &a->parm.capture; ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_parm\n"); ++ ++ switch (a->type) { ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); ++ memset(a, 0, sizeof(*a)); ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cparm->capability = sensor->sen.streamcap.capability; ++ cparm->timeperframe = sensor->sen.streamcap.timeperframe; ++ cparm->capturemode = sensor->sen.streamcap.capturemode; ++ break; ++ ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ break; ++ ++ default: ++ pr_debug("ioctl_g_parm:type is unknown %d\n", a->type); ++ break; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure ++ * ++ * Configures the sensor to use the input parameters, if possible. If ++ * not possible, reverts to the old parameters and returns the ++ * appropriate error code. ++ * ++ * This driver cannot change these settings. ++ */ ++static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_parm\n"); ++ ++ switch (a->type) { ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ break; ++ ++ default: ++ pr_debug(" type is unknown - %d\n", a->type); ++ break; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap ++ * @s: pointer to standard V4L2 device structure ++ * @f: pointer to standard V4L2 v4l2_format structure ++ * ++ * Returns the sensor's current pixel format in the v4l2_format ++ * parameter. ++ */ ++static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) ++{ ++ struct sensor *sensor = s->priv; ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_fmt_cap\n"); ++ ++ switch (f->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ pr_debug(" Returning size of %dx%d\n", ++ sensor->sen.pix.width, sensor->sen.pix.height); ++ f->fmt.pix = sensor->sen.pix; ++ break; ++ ++ case V4L2_BUF_TYPE_PRIVATE: { ++ v4l2_std_id std; ++ adv7180_get_std(&std); ++ f->fmt.pix.pixelformat = (u32)std; ++ } ++ break; ++ ++ default: ++ f->fmt.pix = sensor->sen.pix; ++ break; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure ++ * ++ * If the requested control is supported, returns the control information ++ * from the video_control[] array. Otherwise, returns -EINVAL if the ++ * control is not supported. ++ */ ++static int ioctl_queryctrl(struct v4l2_int_device *s, ++ struct v4l2_queryctrl *qc) ++{ ++ int i; ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_queryctrl\n"); ++ ++ for (i = 0; i < ARRAY_SIZE(adv7180_qctrl); i++) ++ if (qc->id && qc->id == adv7180_qctrl[i].id) { ++ memcpy(qc, &(adv7180_qctrl[i]), ++ sizeof(*qc)); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/*! ++ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure ++ * ++ * If the requested control is supported, returns the control's current ++ * value from the video_control[] array. Otherwise, returns -EINVAL ++ * if the control is not supported. ++ */ ++static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int ret = 0; ++ int sat = 0; ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_ctrl\n"); ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_BRIGHTNESS\n"); ++ adv7180_data.sen.brightness = adv7180_read(ADV7180_BRIGHTNESS); ++ vc->value = adv7180_data.sen.brightness; ++ break; ++ case V4L2_CID_CONTRAST: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_CONTRAST\n"); ++ vc->value = adv7180_data.sen.contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_SATURATION\n"); ++ sat = adv7180_read(ADV7180_SD_SATURATION_CB); ++ adv7180_data.sen.saturation = sat; ++ vc->value = adv7180_data.sen.saturation; ++ break; ++ case V4L2_CID_HUE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_HUE\n"); ++ vc->value = adv7180_data.sen.hue; ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_AUTO_WHITE_BALANCE\n"); ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_DO_WHITE_BALANCE\n"); ++ break; ++ case V4L2_CID_RED_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_RED_BALANCE\n"); ++ vc->value = adv7180_data.sen.red; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_BLUE_BALANCE\n"); ++ vc->value = adv7180_data.sen.blue; ++ break; ++ case V4L2_CID_GAMMA: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_GAMMA\n"); ++ break; ++ case V4L2_CID_EXPOSURE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_EXPOSURE\n"); ++ vc->value = adv7180_data.sen.ae_mode; ++ break; ++ case V4L2_CID_AUTOGAIN: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_AUTOGAIN\n"); ++ break; ++ case V4L2_CID_GAIN: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_GAIN\n"); ++ break; ++ case V4L2_CID_HFLIP: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_HFLIP\n"); ++ break; ++ case V4L2_CID_VFLIP: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_VFLIP\n"); ++ break; ++ default: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " Default case\n"); ++ vc->value = 0; ++ ret = -EPERM; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure ++ * ++ * If the requested control is supported, sets the control's current ++ * value in HW (and updates the video_control[] array). Otherwise, ++ * returns -EINVAL if the control is not supported. ++ */ ++static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int retval = 0; ++ u8 tmp; ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_ctrl\n"); ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_BRIGHTNESS\n"); ++ tmp = vc->value; ++ adv7180_write_reg(ADV7180_BRIGHTNESS, tmp); ++ adv7180_data.sen.brightness = vc->value; ++ break; ++ case V4L2_CID_CONTRAST: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_CONTRAST\n"); ++ break; ++ case V4L2_CID_SATURATION: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_SATURATION\n"); ++ tmp = vc->value; ++ adv7180_write_reg(ADV7180_SD_SATURATION_CB, tmp); ++ adv7180_write_reg(ADV7180_SD_SATURATION_CR, tmp); ++ adv7180_data.sen.saturation = vc->value; ++ break; ++ case V4L2_CID_HUE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_HUE\n"); ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_AUTO_WHITE_BALANCE\n"); ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_DO_WHITE_BALANCE\n"); ++ break; ++ case V4L2_CID_RED_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_RED_BALANCE\n"); ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_BLUE_BALANCE\n"); ++ break; ++ case V4L2_CID_GAMMA: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_GAMMA\n"); ++ break; ++ case V4L2_CID_EXPOSURE: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_EXPOSURE\n"); ++ break; ++ case V4L2_CID_AUTOGAIN: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_AUTOGAIN\n"); ++ break; ++ case V4L2_CID_GAIN: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_GAIN\n"); ++ break; ++ case V4L2_CID_HFLIP: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_HFLIP\n"); ++ break; ++ case V4L2_CID_VFLIP: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " V4L2_CID_VFLIP\n"); ++ break; ++ default: ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ " Default case\n"); ++ retval = -EPERM; ++ break; ++ } ++ ++ return retval; ++} ++ ++/*! ++ * ioctl_enum_framesizes - V4L2 sensor interface handler for ++ * VIDIOC_ENUM_FRAMESIZES ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure ++ * ++ * Return 0 if successful, otherwise -EINVAL. ++ */ ++static int ioctl_enum_framesizes(struct v4l2_int_device *s, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ if (fsize->index >= 1) ++ return -EINVAL; ++ ++ fsize->discrete.width = video_fmts[video_idx].active_width; ++ fsize->discrete.height = video_fmts[video_idx].active_height; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_chip_ident - V4L2 sensor interface handler for ++ * VIDIOC_DBG_G_CHIP_IDENT ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @id: pointer to int ++ * ++ * Return 0. ++ */ ++static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) ++{ ++ ((struct v4l2_dbg_chip_ident *)id)->match.type = ++ V4L2_CHIP_MATCH_I2C_DRIVER; ++ strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, ++ "adv7180_decoder"); ++ ((struct v4l2_dbg_chip_ident *)id)->ident = V4L2_IDENT_ADV7180; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT ++ * @s: pointer to standard V4L2 device structure ++ */ ++static int ioctl_init(struct v4l2_int_device *s) ++{ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_init\n"); ++ return 0; ++} ++ ++/*! ++ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num ++ * @s: pointer to standard V4L2 device structure ++ * ++ * Initialise the device when slave attaches to the master. ++ */ ++static int ioctl_dev_init(struct v4l2_int_device *s) ++{ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_dev_init\n"); ++ return 0; ++} ++ ++/*! ++ * This structure defines all the ioctls for this module. ++ */ ++static struct v4l2_int_ioctl_desc adv7180_ioctl_desc[] = { ++ ++ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*)ioctl_dev_init}, ++ ++ /*! ++ * Delinitialise the dev. at slave detach. ++ * The complement of ioctl_dev_init. ++ */ ++/* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit}, */ ++ ++ {vidioc_int_s_power_num, (v4l2_int_ioctl_func*)ioctl_s_power}, ++ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*)ioctl_g_ifparm}, ++/* {vidioc_int_g_needs_reset_num, ++ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */ ++/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */ ++ {vidioc_int_init_num, (v4l2_int_ioctl_func*)ioctl_init}, ++ ++ /*! ++ * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. ++ */ ++/* {vidioc_int_enum_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */ ++ ++ /*! ++ * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. ++ * This ioctl is used to negotiate the image capture size and ++ * pixel format without actually making it take effect. ++ */ ++/* {vidioc_int_try_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */ ++ ++ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap}, ++ ++ /*! ++ * If the requested format is supported, configures the HW to use that ++ * format, returns error code if format not supported or HW can't be ++ * correctly configured. ++ */ ++/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */ ++ ++ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func*)ioctl_g_parm}, ++ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func*)ioctl_s_parm}, ++ {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func*)ioctl_queryctrl}, ++ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func*)ioctl_g_ctrl}, ++ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func*)ioctl_s_ctrl}, ++ {vidioc_int_enum_framesizes_num, ++ (v4l2_int_ioctl_func *) ioctl_enum_framesizes}, ++ {vidioc_int_g_chip_ident_num, ++ (v4l2_int_ioctl_func *)ioctl_g_chip_ident}, ++}; ++ ++static struct v4l2_int_slave adv7180_slave = { ++ .ioctls = adv7180_ioctl_desc, ++ .num_ioctls = ARRAY_SIZE(adv7180_ioctl_desc), ++}; ++ ++static struct v4l2_int_device adv7180_int_device = { ++ .module = THIS_MODULE, ++ .name = "adv7180", ++ .type = v4l2_int_type_slave, ++ .u = { ++ .slave = &adv7180_slave, ++ }, ++}; ++ ++ ++/*********************************************************************** ++ * I2C client and driver. ++ ***********************************************************************/ ++ ++/*! ADV7180 Reset function. ++ * ++ * @return None. ++ */ ++static void adv7180_hard_reset(bool cvbs) ++{ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ "In adv7180:adv7180_hard_reset\n"); ++ ++ if (cvbs) { ++ /* Set CVBS input on AIN1 */ ++ adv7180_write_reg(ADV7180_INPUT_CTL, 0x00); ++ } else { ++ /* ++ * Set YPbPr input on AIN1,4,5 and normal ++ * operations(autodection of all stds). ++ */ ++ adv7180_write_reg(ADV7180_INPUT_CTL, 0x09); ++ } ++ ++ /* Datasheet recommends */ ++ adv7180_write_reg(0x01, 0xc8); ++ adv7180_write_reg(0x02, 0x04); ++ adv7180_write_reg(0x03, 0x00); ++ adv7180_write_reg(0x04, 0x45); ++ adv7180_write_reg(0x05, 0x00); ++ adv7180_write_reg(0x06, 0x02); ++ adv7180_write_reg(0x07, 0x7F); ++ adv7180_write_reg(0x08, 0x80); ++ adv7180_write_reg(0x0A, 0x00); ++ adv7180_write_reg(0x0B, 0x00); ++ adv7180_write_reg(0x0C, 0x36); ++ adv7180_write_reg(0x0D, 0x7C); ++ adv7180_write_reg(0x0E, 0x00); ++ adv7180_write_reg(0x0F, 0x00); ++ adv7180_write_reg(0x13, 0x00); ++ adv7180_write_reg(0x14, 0x12); ++ adv7180_write_reg(0x15, 0x00); ++ adv7180_write_reg(0x16, 0x00); ++ adv7180_write_reg(0x17, 0x01); ++ adv7180_write_reg(0x18, 0x93); ++ adv7180_write_reg(0xF1, 0x19); ++ adv7180_write_reg(0x1A, 0x00); ++ adv7180_write_reg(0x1B, 0x00); ++ adv7180_write_reg(0x1C, 0x00); ++ adv7180_write_reg(0x1D, 0x40); ++ adv7180_write_reg(0x1E, 0x00); ++ adv7180_write_reg(0x1F, 0x00); ++ adv7180_write_reg(0x20, 0x00); ++ adv7180_write_reg(0x21, 0x00); ++ adv7180_write_reg(0x22, 0x00); ++ adv7180_write_reg(0x23, 0xC0); ++ adv7180_write_reg(0x24, 0x00); ++ adv7180_write_reg(0x25, 0x00); ++ adv7180_write_reg(0x26, 0x00); ++ adv7180_write_reg(0x27, 0x58); ++ adv7180_write_reg(0x28, 0x00); ++ adv7180_write_reg(0x29, 0x00); ++ adv7180_write_reg(0x2A, 0x00); ++ adv7180_write_reg(0x2B, 0xE1); ++ adv7180_write_reg(0x2C, 0xAE); ++ adv7180_write_reg(0x2D, 0xF4); ++ adv7180_write_reg(0x2E, 0x00); ++ adv7180_write_reg(0x2F, 0xF0); ++ adv7180_write_reg(0x30, 0x00); ++ adv7180_write_reg(0x31, 0x12); ++ adv7180_write_reg(0x32, 0x41); ++ adv7180_write_reg(0x33, 0x84); ++ adv7180_write_reg(0x34, 0x00); ++ adv7180_write_reg(0x35, 0x02); ++ adv7180_write_reg(0x36, 0x00); ++ adv7180_write_reg(0x37, 0x01); ++ adv7180_write_reg(0x38, 0x80); ++ adv7180_write_reg(0x39, 0xC0); ++ adv7180_write_reg(0x3A, 0x10); ++ adv7180_write_reg(0x3B, 0x05); ++ adv7180_write_reg(0x3C, 0x58); ++ adv7180_write_reg(0x3D, 0xB2); ++ adv7180_write_reg(0x3E, 0x64); ++ adv7180_write_reg(0x3F, 0xE4); ++ adv7180_write_reg(0x40, 0x90); ++ adv7180_write_reg(0x41, 0x01); ++ adv7180_write_reg(0x42, 0x7E); ++ adv7180_write_reg(0x43, 0xA4); ++ adv7180_write_reg(0x44, 0xFF); ++ adv7180_write_reg(0x45, 0xB6); ++ adv7180_write_reg(0x46, 0x12); ++ adv7180_write_reg(0x48, 0x00); ++ adv7180_write_reg(0x49, 0x00); ++ adv7180_write_reg(0x4A, 0x00); ++ adv7180_write_reg(0x4B, 0x00); ++ adv7180_write_reg(0x4C, 0x00); ++ adv7180_write_reg(0x4D, 0xEF); ++ adv7180_write_reg(0x4E, 0x08); ++ adv7180_write_reg(0x4F, 0x08); ++ adv7180_write_reg(0x50, 0x08); ++ adv7180_write_reg(0x51, 0x24); ++ adv7180_write_reg(0x52, 0x0B); ++ adv7180_write_reg(0x53, 0x4E); ++ adv7180_write_reg(0x54, 0x80); ++ adv7180_write_reg(0x55, 0x00); ++ adv7180_write_reg(0x56, 0x10); ++ adv7180_write_reg(0x57, 0x00); ++ adv7180_write_reg(0x58, 0x00); ++ adv7180_write_reg(0x59, 0x00); ++ adv7180_write_reg(0x5A, 0x00); ++ adv7180_write_reg(0x5B, 0x00); ++ adv7180_write_reg(0x5C, 0x00); ++ adv7180_write_reg(0x5D, 0x00); ++ adv7180_write_reg(0x5E, 0x00); ++ adv7180_write_reg(0x5F, 0x00); ++ adv7180_write_reg(0x60, 0x00); ++ adv7180_write_reg(0x61, 0x00); ++ adv7180_write_reg(0x62, 0x20); ++ adv7180_write_reg(0x63, 0x00); ++ adv7180_write_reg(0x64, 0x00); ++ adv7180_write_reg(0x65, 0x00); ++ adv7180_write_reg(0x66, 0x00); ++ adv7180_write_reg(0x67, 0x03); ++ adv7180_write_reg(0x68, 0x01); ++ adv7180_write_reg(0x69, 0x00); ++ adv7180_write_reg(0x6A, 0x00); ++ adv7180_write_reg(0x6B, 0xC0); ++ adv7180_write_reg(0x6C, 0x00); ++ adv7180_write_reg(0x6D, 0x00); ++ adv7180_write_reg(0x6E, 0x00); ++ adv7180_write_reg(0x6F, 0x00); ++ adv7180_write_reg(0x70, 0x00); ++ adv7180_write_reg(0x71, 0x00); ++ adv7180_write_reg(0x72, 0x00); ++ adv7180_write_reg(0x73, 0x10); ++ adv7180_write_reg(0x74, 0x04); ++ adv7180_write_reg(0x75, 0x01); ++ adv7180_write_reg(0x76, 0x00); ++ adv7180_write_reg(0x77, 0x3F); ++ adv7180_write_reg(0x78, 0xFF); ++ adv7180_write_reg(0x79, 0xFF); ++ adv7180_write_reg(0x7A, 0xFF); ++ adv7180_write_reg(0x7B, 0x1E); ++ adv7180_write_reg(0x7C, 0xC0); ++ adv7180_write_reg(0x7D, 0x00); ++ adv7180_write_reg(0x7E, 0x00); ++ adv7180_write_reg(0x7F, 0x00); ++ adv7180_write_reg(0x80, 0x00); ++ adv7180_write_reg(0x81, 0xC0); ++ adv7180_write_reg(0x82, 0x04); ++ adv7180_write_reg(0x83, 0x00); ++ adv7180_write_reg(0x84, 0x0C); ++ adv7180_write_reg(0x85, 0x02); ++ adv7180_write_reg(0x86, 0x03); ++ adv7180_write_reg(0x87, 0x63); ++ adv7180_write_reg(0x88, 0x5A); ++ adv7180_write_reg(0x89, 0x08); ++ adv7180_write_reg(0x8A, 0x10); ++ adv7180_write_reg(0x8B, 0x00); ++ adv7180_write_reg(0x8C, 0x40); ++ adv7180_write_reg(0x8D, 0x00); ++ adv7180_write_reg(0x8E, 0x40); ++ adv7180_write_reg(0x8F, 0x00); ++ adv7180_write_reg(0x90, 0x00); ++ adv7180_write_reg(0x91, 0x50); ++ adv7180_write_reg(0x92, 0x00); ++ adv7180_write_reg(0x93, 0x00); ++ adv7180_write_reg(0x94, 0x00); ++ adv7180_write_reg(0x95, 0x00); ++ adv7180_write_reg(0x96, 0x00); ++ adv7180_write_reg(0x97, 0xF0); ++ adv7180_write_reg(0x98, 0x00); ++ adv7180_write_reg(0x99, 0x00); ++ adv7180_write_reg(0x9A, 0x00); ++ adv7180_write_reg(0x9B, 0x00); ++ adv7180_write_reg(0x9C, 0x00); ++ adv7180_write_reg(0x9D, 0x00); ++ adv7180_write_reg(0x9E, 0x00); ++ adv7180_write_reg(0x9F, 0x00); ++ adv7180_write_reg(0xA0, 0x00); ++ adv7180_write_reg(0xA1, 0x00); ++ adv7180_write_reg(0xA2, 0x00); ++ adv7180_write_reg(0xA3, 0x00); ++ adv7180_write_reg(0xA4, 0x00); ++ adv7180_write_reg(0xA5, 0x00); ++ adv7180_write_reg(0xA6, 0x00); ++ adv7180_write_reg(0xA7, 0x00); ++ adv7180_write_reg(0xA8, 0x00); ++ adv7180_write_reg(0xA9, 0x00); ++ adv7180_write_reg(0xAA, 0x00); ++ adv7180_write_reg(0xAB, 0x00); ++ adv7180_write_reg(0xAC, 0x00); ++ adv7180_write_reg(0xAD, 0x00); ++ adv7180_write_reg(0xAE, 0x60); ++ adv7180_write_reg(0xAF, 0x00); ++ adv7180_write_reg(0xB0, 0x00); ++ adv7180_write_reg(0xB1, 0x60); ++ adv7180_write_reg(0xB2, 0x1C); ++ adv7180_write_reg(0xB3, 0x54); ++ adv7180_write_reg(0xB4, 0x00); ++ adv7180_write_reg(0xB5, 0x00); ++ adv7180_write_reg(0xB6, 0x00); ++ adv7180_write_reg(0xB7, 0x13); ++ adv7180_write_reg(0xB8, 0x03); ++ adv7180_write_reg(0xB9, 0x33); ++ adv7180_write_reg(0xBF, 0x02); ++ adv7180_write_reg(0xC0, 0x00); ++ adv7180_write_reg(0xC1, 0x00); ++ adv7180_write_reg(0xC2, 0x00); ++ adv7180_write_reg(0xC3, 0x00); ++ adv7180_write_reg(0xC4, 0x00); ++ adv7180_write_reg(0xC5, 0x81); ++ adv7180_write_reg(0xC6, 0x00); ++ adv7180_write_reg(0xC7, 0x00); ++ adv7180_write_reg(0xC8, 0x00); ++ adv7180_write_reg(0xC9, 0x04); ++ adv7180_write_reg(0xCC, 0x69); ++ adv7180_write_reg(0xCD, 0x00); ++ adv7180_write_reg(0xCE, 0x01); ++ adv7180_write_reg(0xCF, 0xB4); ++ adv7180_write_reg(0xD0, 0x00); ++ adv7180_write_reg(0xD1, 0x10); ++ adv7180_write_reg(0xD2, 0xFF); ++ adv7180_write_reg(0xD3, 0xFF); ++ adv7180_write_reg(0xD4, 0x7F); ++ adv7180_write_reg(0xD5, 0x7F); ++ adv7180_write_reg(0xD6, 0x3E); ++ adv7180_write_reg(0xD7, 0x08); ++ adv7180_write_reg(0xD8, 0x3C); ++ adv7180_write_reg(0xD9, 0x08); ++ adv7180_write_reg(0xDA, 0x3C); ++ adv7180_write_reg(0xDB, 0x9B); ++ adv7180_write_reg(0xDC, 0xAC); ++ adv7180_write_reg(0xDD, 0x4C); ++ adv7180_write_reg(0xDE, 0x00); ++ adv7180_write_reg(0xDF, 0x00); ++ adv7180_write_reg(0xE0, 0x14); ++ adv7180_write_reg(0xE1, 0x80); ++ adv7180_write_reg(0xE2, 0x80); ++ adv7180_write_reg(0xE3, 0x80); ++ adv7180_write_reg(0xE4, 0x80); ++ adv7180_write_reg(0xE5, 0x25); ++ adv7180_write_reg(0xE6, 0x44); ++ adv7180_write_reg(0xE7, 0x63); ++ adv7180_write_reg(0xE8, 0x65); ++ adv7180_write_reg(0xE9, 0x14); ++ adv7180_write_reg(0xEA, 0x63); ++ adv7180_write_reg(0xEB, 0x55); ++ adv7180_write_reg(0xEC, 0x55); ++ adv7180_write_reg(0xEE, 0x00); ++ adv7180_write_reg(0xEF, 0x4A); ++ adv7180_write_reg(0xF0, 0x44); ++ adv7180_write_reg(0xF1, 0x0C); ++ adv7180_write_reg(0xF2, 0x32); ++ adv7180_write_reg(0xF3, 0x00); ++ adv7180_write_reg(0xF4, 0x3F); ++ adv7180_write_reg(0xF5, 0xE0); ++ adv7180_write_reg(0xF6, 0x69); ++ adv7180_write_reg(0xF7, 0x10); ++ adv7180_write_reg(0xF8, 0x00); ++ adv7180_write_reg(0xF9, 0x03); ++ adv7180_write_reg(0xFA, 0xFA); ++ adv7180_write_reg(0xFB, 0x40); ++} ++ ++/*! ADV7180 I2C attach function. ++ * ++ * @param *adapter struct i2c_adapter *. ++ * ++ * @return Error code indicating success or failure. ++ */ ++ ++/*! ++ * ADV7180 I2C probe function. ++ * Function set in i2c_driver struct. ++ * Called by insmod. ++ * ++ * @param *adapter I2C adapter descriptor. ++ * ++ * @return Error code indicating success or failure. ++ */ ++static int adv7180_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int rev_id; ++ int ret = 0; ++ u32 cvbs = true; ++ struct pinctrl *pinctrl; ++ struct device *dev = &client->dev; ++ ++ printk(KERN_ERR"DBG sensor data is at %p\n", &adv7180_data); ++ ++ /* ov5640 pinctrl */ ++ pinctrl = devm_pinctrl_get_select_default(dev); ++ if (IS_ERR(pinctrl)) { ++ dev_err(dev, "setup pinctrl failed\n"); ++ return PTR_ERR(pinctrl); ++ } ++ ++ /* request power down pin */ ++ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); ++ if (!gpio_is_valid(pwn_gpio)) { ++ dev_err(dev, "no sensor pwdn pin available\n"); ++ return -ENODEV; ++ } ++ ret = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, ++ "adv7180_pwdn"); ++ if (ret < 0) { ++ dev_err(dev, "no power pin available!\n"); ++ return ret; ++ } ++ ++ adv7180_regulator_enable(dev); ++ ++ adv7180_power_down(0); ++ ++ msleep(1); ++ ++ /* Set initial values for the sensor struct. */ ++ memset(&adv7180_data, 0, sizeof(adv7180_data)); ++ adv7180_data.sen.i2c_client = client; ++ adv7180_data.sen.streamcap.timeperframe.denominator = 30; ++ adv7180_data.sen.streamcap.timeperframe.numerator = 1; ++ adv7180_data.std_id = V4L2_STD_ALL; ++ video_idx = ADV7180_NOT_LOCKED; ++ adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width; ++ adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height; ++ adv7180_data.sen.pix.pixelformat = V4L2_PIX_FMT_UYVY; /* YUV422 */ ++ adv7180_data.sen.pix.priv = 1; /* 1 is used to indicate TV in */ ++ adv7180_data.sen.on = true; ++ ++ adv7180_data.sen.sensor_clk = devm_clk_get(dev, "csi_mclk"); ++ if (IS_ERR(adv7180_data.sen.sensor_clk)) { ++ dev_err(dev, "get mclk failed\n"); ++ return PTR_ERR(adv7180_data.sen.sensor_clk); ++ } ++ ++ ret = of_property_read_u32(dev->of_node, "mclk", ++ &adv7180_data.sen.mclk); ++ if (ret) { ++ dev_err(dev, "mclk frequency is invalid\n"); ++ return ret; ++ } ++ ++ ret = of_property_read_u32( ++ dev->of_node, "mclk_source", ++ (u32 *) &(adv7180_data.sen.mclk_source)); ++ if (ret) { ++ dev_err(dev, "mclk_source invalid\n"); ++ return ret; ++ } ++ ++ ret = of_property_read_u32(dev->of_node, "csi_id", ++ &(adv7180_data.sen.csi)); ++ if (ret) { ++ dev_err(dev, "csi_id invalid\n"); ++ return ret; ++ } ++ ++ clk_prepare_enable(adv7180_data.sen.sensor_clk); ++ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ "%s:adv7180 probe i2c address is 0x%02X\n", ++ __func__, adv7180_data.sen.i2c_client->addr); ++ ++ /*! Read the revision ID of the tvin chip */ ++ rev_id = adv7180_read(ADV7180_IDENT); ++ dev_dbg(dev, ++ "%s:Analog Device adv7%2X0 detected!\n", __func__, ++ rev_id); ++ ++ ret = of_property_read_u32(dev->of_node, "cvbs", &(cvbs)); ++ if (ret) { ++ dev_err(dev, "cvbs setting is not found\n"); ++ cvbs = true; ++ } ++ ++ /*! ADV7180 initialization. */ ++ adv7180_hard_reset(cvbs); ++ ++ pr_debug(" type is %d (expect %d)\n", ++ adv7180_int_device.type, v4l2_int_type_slave); ++ pr_debug(" num ioctls is %d\n", ++ adv7180_int_device.u.slave->num_ioctls); ++ ++ /* This function attaches this structure to the /dev/video0 device. ++ * The pointer in priv points to the adv7180_data structure here.*/ ++ adv7180_int_device.priv = &adv7180_data; ++ ret = v4l2_int_device_register(&adv7180_int_device); ++ ++ clk_disable_unprepare(adv7180_data.sen.sensor_clk); ++ ++ return ret; ++} ++ ++/*! ++ * ADV7180 I2C detach function. ++ * Called on rmmod. ++ * ++ * @param *client struct i2c_client*. ++ * ++ * @return Error code indicating success or failure. ++ */ ++static int adv7180_detach(struct i2c_client *client) ++{ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, ++ "%s:Removing %s video decoder @ 0x%02X from adapter %s\n", ++ __func__, IF_NAME, client->addr << 1, client->adapter->name); ++ ++ /* Power down via i2c */ ++ adv7180_write_reg(ADV7180_PWR_MNG, 0x24); ++ ++ if (dvddio_regulator) ++ regulator_disable(dvddio_regulator); ++ ++ if (dvdd_regulator) ++ regulator_disable(dvdd_regulator); ++ ++ if (avdd_regulator) ++ regulator_disable(avdd_regulator); ++ ++ if (pvdd_regulator) ++ regulator_disable(pvdd_regulator); ++ ++ v4l2_int_device_unregister(&adv7180_int_device); ++ ++ return 0; ++} ++ ++/*! ++ * ADV7180 init function. ++ * Called on insmod. ++ * ++ * @return Error code indicating success or failure. ++ */ ++static __init int adv7180_init(void) ++{ ++ u8 err = 0; ++ ++ pr_debug("In adv7180_init\n"); ++ ++ /* Tells the i2c driver what functions to call for this driver. */ ++ err = i2c_add_driver(&adv7180_i2c_driver); ++ if (err != 0) ++ pr_err("%s:driver registration failed, error=%d\n", ++ __func__, err); ++ ++ return err; ++} ++ ++/*! ++ * ADV7180 cleanup function. ++ * Called on rmmod. ++ * ++ * @return Error code indicating success or failure. ++ */ ++static void __exit adv7180_clean(void) ++{ ++ dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_clean\n"); ++ i2c_del_driver(&adv7180_i2c_driver); ++} ++ ++module_init(adv7180_init); ++module_exit(adv7180_clean); ++ ++MODULE_AUTHOR("Freescale Semiconductor"); ++MODULE_DESCRIPTION("Anolog Device ADV7180 video decoder driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/csi_v4l2_capture.c linux-openelec/drivers/media/platform/mxc/capture/csi_v4l2_capture.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/csi_v4l2_capture.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/csi_v4l2_capture.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,2047 @@ ++/* ++ * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file drivers/media/video/mxc/capture/csi_v4l2_capture.c ++ * This file is derived from mxc_v4l2_capture.c ++ * ++ * @brief Video For Linux 2 capture driver ++ * ++ * @ingroup MXC_V4L2_CAPTURE ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "fsl_csi.h" ++ ++static int video_nr = -1; ++static cam_data *g_cam; ++static int req_buf_number; ++ ++static int csi_v4l2_master_attach(struct v4l2_int_device *slave); ++static void csi_v4l2_master_detach(struct v4l2_int_device *slave); ++static u8 camera_power(cam_data *cam, bool cameraOn); ++struct v4l2_crop crop_current; ++struct v4l2_window win_current; ++ ++/*! Information about this driver. */ ++static struct v4l2_int_master csi_v4l2_master = { ++ .attach = csi_v4l2_master_attach, ++ .detach = csi_v4l2_master_detach, ++}; ++ ++static struct v4l2_int_device csi_v4l2_int_device = { ++ .module = THIS_MODULE, ++ .name = "csi_v4l2_cap", ++ .type = v4l2_int_type_master, ++ .u = { ++ .master = &csi_v4l2_master, ++ }, ++}; ++ ++static struct v4l2_queryctrl pxp_controls[] = { ++ { ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Horizontal Flip", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 1, ++ .default_value = 0, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Vertical Flip", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 1, ++ .default_value = 0, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_PRIVATE_BASE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Rotation", ++ .minimum = 0, ++ .maximum = 270, ++ .step = 90, ++ .default_value = 0, ++ .flags = 0, ++ }, ++}; ++ ++/* Callback function triggered after PxP receives an EOF interrupt */ ++static void pxp_dma_done(void *arg) ++{ ++ struct pxp_tx_desc *tx_desc = to_tx_desc(arg); ++ struct dma_chan *chan = tx_desc->txd.chan; ++ struct pxp_channel *pxp_chan = to_pxp_channel(chan); ++ cam_data *cam = pxp_chan->client; ++ ++ /* This call will signal wait_for_completion_timeout() */ ++ complete(&cam->pxp_tx_cmpl); ++} ++ ++static bool chan_filter(struct dma_chan *chan, void *arg) ++{ ++ if (imx_dma_is_pxp(chan)) ++ return true; ++ else ++ return false; ++} ++ ++/* Function to request PXP DMA channel */ ++static int pxp_chan_init(cam_data *cam) ++{ ++ dma_cap_mask_t mask; ++ struct dma_chan *chan; ++ ++ /* Request a free channel */ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ dma_cap_set(DMA_PRIVATE, mask); ++ chan = dma_request_channel(mask, chan_filter, NULL); ++ if (!chan) { ++ pr_err("Unsuccessfully request channel!\n"); ++ return -EBUSY; ++ } ++ ++ cam->pxp_chan = to_pxp_channel(chan); ++ cam->pxp_chan->client = cam; ++ ++ init_completion(&cam->pxp_tx_cmpl); ++ ++ return 0; ++} ++ ++/* ++ * Function to call PxP DMA driver and send our new V4L2 buffer ++ * through the PxP. ++ * Note: This is a blocking call, so upon return the PxP tx should be complete. ++ */ ++static int pxp_process_update(cam_data *cam) ++{ ++ dma_cookie_t cookie; ++ struct scatterlist *sg = cam->sg; ++ struct dma_chan *dma_chan; ++ struct pxp_tx_desc *desc; ++ struct dma_async_tx_descriptor *txd; ++ struct pxp_config_data *pxp_conf = &cam->pxp_conf; ++ struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; ++ int i, ret; ++ int length; ++ ++ pr_debug("Starting PxP Send Buffer\n"); ++ ++ /* First, check to see that we have acquired a PxP Channel object */ ++ if (cam->pxp_chan == NULL) { ++ /* ++ * PxP Channel has not yet been created and initialized, ++ * so let's go ahead and try ++ */ ++ ret = pxp_chan_init(cam); ++ if (ret) { ++ /* ++ * PxP channel init failed, and we can't use the ++ * PxP until the PxP DMA driver has loaded, so we abort ++ */ ++ pr_err("PxP chan init failed\n"); ++ return -ENODEV; ++ } ++ } ++ ++ /* ++ * Init completion, so that we can be properly informed of ++ * the completion of the PxP task when it is done. ++ */ ++ init_completion(&cam->pxp_tx_cmpl); ++ ++ dma_chan = &cam->pxp_chan->dma_chan; ++ ++ txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg, 2, ++ DMA_TO_DEVICE, ++ DMA_PREP_INTERRUPT, ++ NULL); ++ if (!txd) { ++ pr_err("Error preparing a DMA transaction descriptor.\n"); ++ return -EIO; ++ } ++ ++ txd->callback_param = txd; ++ txd->callback = pxp_dma_done; ++ ++ /* ++ * Configure PxP for processing of new v4l2 buf ++ */ ++ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_UYVY; ++ pxp_conf->s0_param.color_key = -1; ++ pxp_conf->s0_param.color_key_enable = false; ++ pxp_conf->s0_param.width = cam->v2f.fmt.pix.width; ++ pxp_conf->s0_param.height = cam->v2f.fmt.pix.height; ++ ++ pxp_conf->ol_param[0].combine_enable = false; ++ ++ proc_data->srect.top = 0; ++ proc_data->srect.left = 0; ++ proc_data->srect.width = pxp_conf->s0_param.width; ++ proc_data->srect.height = pxp_conf->s0_param.height; ++ ++ if (crop_current.c.top != 0) ++ proc_data->srect.top = crop_current.c.top; ++ if (crop_current.c.left != 0) ++ proc_data->srect.left = crop_current.c.left; ++ if (crop_current.c.width != 0) ++ proc_data->srect.width = crop_current.c.width; ++ if (crop_current.c.height != 0) ++ proc_data->srect.height = crop_current.c.height; ++ ++ proc_data->drect.left = 0; ++ proc_data->drect.top = 0; ++ proc_data->drect.width = proc_data->srect.width; ++ proc_data->drect.height = proc_data->srect.height; ++ ++ if (win_current.w.left != 0) ++ proc_data->drect.left = win_current.w.left; ++ if (win_current.w.top != 0) ++ proc_data->drect.top = win_current.w.top; ++ if (win_current.w.width != 0) ++ proc_data->drect.width = win_current.w.width; ++ if (win_current.w.height != 0) ++ proc_data->drect.height = win_current.w.height; ++ ++ pr_debug("srect l: %d, t: %d, w: %d, h: %d; " ++ "drect l: %d, t: %d, w: %d, h: %d\n", ++ proc_data->srect.left, proc_data->srect.top, ++ proc_data->srect.width, proc_data->srect.height, ++ proc_data->drect.left, proc_data->drect.top, ++ proc_data->drect.width, proc_data->drect.height); ++ ++ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_RGB565; ++ pxp_conf->out_param.width = proc_data->drect.width; ++ pxp_conf->out_param.height = proc_data->drect.height; ++ ++ if (cam->rotation % 180) ++ pxp_conf->out_param.stride = pxp_conf->out_param.height; ++ else ++ pxp_conf->out_param.stride = pxp_conf->out_param.width; ++ ++ desc = to_tx_desc(txd); ++ length = desc->len; ++ for (i = 0; i < length; i++) { ++ if (i == 0) {/* S0 */ ++ memcpy(&desc->proc_data, proc_data, ++ sizeof(struct pxp_proc_data)); ++ pxp_conf->s0_param.paddr = sg_dma_address(&sg[0]); ++ memcpy(&desc->layer_param.s0_param, &pxp_conf->s0_param, ++ sizeof(struct pxp_layer_param)); ++ } else if (i == 1) { ++ pxp_conf->out_param.paddr = sg_dma_address(&sg[1]); ++ memcpy(&desc->layer_param.out_param, ++ &pxp_conf->out_param, ++ sizeof(struct pxp_layer_param)); ++ } ++ ++ desc = desc->next; ++ } ++ ++ /* Submitting our TX starts the PxP processing task */ ++ cookie = txd->tx_submit(txd); ++ if (cookie < 0) { ++ pr_err("Error sending FB through PxP\n"); ++ return -EIO; ++ } ++ ++ cam->txd = txd; ++ ++ /* trigger PxP */ ++ dma_async_issue_pending(dma_chan); ++ ++ return 0; ++} ++ ++static int pxp_complete_update(cam_data *cam) ++{ ++ int ret; ++ /* ++ * Wait for completion event, which will be set ++ * through our TX callback function. ++ */ ++ ret = wait_for_completion_timeout(&cam->pxp_tx_cmpl, HZ / 10); ++ if (ret <= 0) { ++ pr_warning("PxP operation failed due to %s\n", ++ ret < 0 ? "user interrupt" : "timeout"); ++ dma_release_channel(&cam->pxp_chan->dma_chan); ++ cam->pxp_chan = NULL; ++ return ret ? : -ETIMEDOUT; ++ } ++ ++ dma_release_channel(&cam->pxp_chan->dma_chan); ++ cam->pxp_chan = NULL; ++ ++ pr_debug("TX completed\n"); ++ ++ return 0; ++} ++ ++/*! ++ * Camera V4l2 callback function. ++ * ++ * @param mask u32 ++ * @param dev void device structure ++ * ++ * @return none ++ */ ++static void camera_callback(u32 mask, void *dev) ++{ ++ struct mxc_v4l_frame *done_frame; ++ struct mxc_v4l_frame *ready_frame; ++ cam_data *cam; ++ ++ cam = (cam_data *) dev; ++ if (cam == NULL) ++ return; ++ ++ spin_lock(&cam->queue_int_lock); ++ spin_lock(&cam->dqueue_int_lock); ++ if (!list_empty(&cam->working_q)) { ++ done_frame = list_entry(cam->working_q.next, ++ struct mxc_v4l_frame, queue); ++ ++ if (done_frame->csi_buf_num != cam->ping_pong_csi) ++ goto next; ++ ++ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { ++ done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE; ++ done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; ++ ++ /* Added to the done queue */ ++ list_del(cam->working_q.next); ++ list_add_tail(&done_frame->queue, &cam->done_q); ++ cam->enc_counter++; ++ wake_up_interruptible(&cam->enc_queue); ++ } else { ++ pr_err("ERROR: v4l2 capture: %s: " ++ "buffer not queued\n", __func__); ++ } ++ } ++ ++next: ++ if (!list_empty(&cam->ready_q)) { ++ ready_frame = list_entry(cam->ready_q.next, ++ struct mxc_v4l_frame, queue); ++ list_del(cam->ready_q.next); ++ list_add_tail(&ready_frame->queue, &cam->working_q); ++ ++ __raw_writel(ready_frame->paddress, ++ cam->ping_pong_csi == 1 ? CSI_CSIDMASA_FB1 : ++ CSI_CSIDMASA_FB2); ++ ready_frame->csi_buf_num = cam->ping_pong_csi; ++ } else { ++ __raw_writel(cam->dummy_frame.paddress, ++ cam->ping_pong_csi == 1 ? CSI_CSIDMASA_FB1 : ++ CSI_CSIDMASA_FB2); ++ } ++ spin_unlock(&cam->dqueue_int_lock); ++ spin_unlock(&cam->queue_int_lock); ++ ++ return; ++} ++ ++/*! ++ * Make csi ready for capture image. ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 success ++ */ ++static int csi_cap_image(cam_data *cam) ++{ ++ unsigned int value; ++ ++ value = __raw_readl(CSI_CSICR3); ++ __raw_writel(value | BIT_FRMCNT_RST, CSI_CSICR3); ++ value = __raw_readl(CSI_CSISR); ++ __raw_writel(value, CSI_CSISR); ++ ++ return 0; ++} ++ ++/*************************************************************************** ++ * Functions for handling Frame buffers. ++ **************************************************************************/ ++ ++/*! ++ * Free frame buffers ++ * ++ * @param cam Structure cam_data * ++ * ++ * @return status 0 success. ++ */ ++static int csi_free_frame_buf(cam_data *cam) ++{ ++ int i; ++ ++ pr_debug("MVC: In %s\n", __func__); ++ ++ for (i = 0; i < FRAME_NUM; i++) { ++ if (cam->frame[i].vaddress != 0) { ++ dma_free_coherent(0, cam->frame[i].buffer.length, ++ cam->frame[i].vaddress, ++ cam->frame[i].paddress); ++ cam->frame[i].vaddress = 0; ++ } ++ } ++ ++ if (cam->dummy_frame.vaddress != 0) { ++ dma_free_coherent(0, cam->dummy_frame.buffer.length, ++ cam->dummy_frame.vaddress, ++ cam->dummy_frame.paddress); ++ cam->dummy_frame.vaddress = 0; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * Allocate frame buffers ++ * ++ * @param cam Structure cam_data * ++ * @param count int number of buffer need to allocated ++ * ++ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. ++ */ ++static int csi_allocate_frame_buf(cam_data *cam, int count) ++{ ++ int i; ++ ++ pr_debug("In MVC:%s- size=%d\n", ++ __func__, cam->v2f.fmt.pix.sizeimage); ++ for (i = 0; i < count; i++) { ++ cam->frame[i].vaddress = dma_alloc_coherent(0, PAGE_ALIGN ++ (cam->v2f.fmt. ++ pix.sizeimage), ++ &cam->frame[i]. ++ paddress, ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->frame[i].vaddress == 0) { ++ pr_err("ERROR: v4l2 capture: " ++ "%s failed.\n", __func__); ++ csi_free_frame_buf(cam); ++ return -ENOBUFS; ++ } ++ cam->frame[i].buffer.index = i; ++ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; ++ cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cam->frame[i].buffer.length = cam->v2f.fmt.pix.sizeimage; ++ cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; ++ cam->frame[i].buffer.m.offset = cam->frame[i].paddress; ++ cam->frame[i].index = i; ++ cam->frame[i].csi_buf_num = 0; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * Free frame buffers status ++ * ++ * @param cam Structure cam_data * ++ * ++ * @return none ++ */ ++static void csi_free_frames(cam_data *cam) ++{ ++ int i; ++ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ for (i = 0; i < FRAME_NUM; i++) ++ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; ++ ++ cam->enc_counter = 0; ++ INIT_LIST_HEAD(&cam->ready_q); ++ INIT_LIST_HEAD(&cam->working_q); ++ INIT_LIST_HEAD(&cam->done_q); ++ ++ return; ++} ++ ++/*! ++ * Return the buffer status ++ * ++ * @param cam Structure cam_data * ++ * @param buf Structure v4l2_buffer * ++ * ++ * @return status 0 success, EINVAL failed. ++ */ ++static int csi_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf) ++{ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ if (buf->index < 0 || buf->index >= FRAME_NUM) { ++ pr_err("ERROR: v4l2 capture: %s buffers " ++ "not allocated\n", __func__); ++ return -EINVAL; ++ } ++ ++ memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf)); ++ ++ return 0; ++} ++ ++static int csi_v4l2_release_bufs(cam_data *cam) ++{ ++ pr_debug("In MVC:csi_v4l2_release_bufs\n"); ++ return 0; ++} ++ ++static int csi_v4l2_prepare_bufs(cam_data *cam, struct v4l2_buffer *buf) ++{ ++ pr_debug("In MVC:csi_v4l2_prepare_bufs\n"); ++ ++ if (buf->index < 0 || buf->index >= FRAME_NUM || buf->length < ++ cam->v2f.fmt.pix.sizeimage) { ++ pr_err("ERROR: v4l2 capture: csi_v4l2_prepare_bufs buffers " ++ "not allocated,index=%d, length=%d\n", buf->index, ++ buf->length); ++ return -EINVAL; ++ } ++ ++ cam->frame[buf->index].buffer.index = buf->index; ++ cam->frame[buf->index].buffer.flags = V4L2_BUF_FLAG_MAPPED; ++ cam->frame[buf->index].buffer.length = buf->length; ++ cam->frame[buf->index].buffer.m.offset = cam->frame[buf->index].paddress ++ = buf->m.offset; ++ cam->frame[buf->index].buffer.type = buf->type; ++ cam->frame[buf->index].buffer.memory = V4L2_MEMORY_USERPTR; ++ cam->frame[buf->index].index = buf->index; ++ ++ return 0; ++} ++ ++/*! ++ * Indicates whether the palette is supported. ++ * ++ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_UYVY or V4L2_PIX_FMT_YUV420 ++ * ++ * @return 0 if failed ++ */ ++static inline int valid_mode(u32 palette) ++{ ++ return (palette == V4L2_PIX_FMT_RGB565) || ++ (palette == V4L2_PIX_FMT_YUYV) || ++ (palette == V4L2_PIX_FMT_UYVY) || (palette == V4L2_PIX_FMT_YUV420); ++} ++ ++/*! ++ * Start stream I/O ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int csi_streamon(cam_data *cam) ++{ ++ struct mxc_v4l_frame *frame; ++ unsigned long flags; ++ unsigned long val; ++ int timeout, timeout2; ++ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ if (NULL == cam) { ++ pr_err("ERROR: v4l2 capture: %s cam parameter is NULL\n", ++ __func__); ++ return -1; ++ } ++ cam->dummy_frame.vaddress = dma_alloc_coherent(0, ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), ++ &cam->dummy_frame.paddress, ++ GFP_DMA | GFP_KERNEL); ++ if (cam->dummy_frame.vaddress == 0) { ++ pr_err("ERROR: v4l2 capture: Allocate dummy frame " ++ "failed.\n"); ++ return -ENOBUFS; ++ } ++ cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; ++ cam->dummy_frame.buffer.length = cam->v2f.fmt.pix.sizeimage; ++ cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; ++ ++ spin_lock_irqsave(&cam->queue_int_lock, flags); ++ /* move the frame from readyq to workingq */ ++ if (list_empty(&cam->ready_q)) { ++ pr_err("ERROR: v4l2 capture: %s: " ++ "ready_q queue empty\n", __func__); ++ spin_unlock_irqrestore(&cam->queue_int_lock, flags); ++ return -1; ++ } ++ frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); ++ list_del(cam->ready_q.next); ++ list_add_tail(&frame->queue, &cam->working_q); ++ __raw_writel(frame->paddress, CSI_CSIDMASA_FB1); ++ frame->csi_buf_num = 1; ++ ++ if (list_empty(&cam->ready_q)) { ++ pr_err("ERROR: v4l2 capture: %s: " ++ "ready_q queue empty\n", __func__); ++ spin_unlock_irqrestore(&cam->queue_int_lock, flags); ++ return -1; ++ } ++ frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); ++ list_del(cam->ready_q.next); ++ list_add_tail(&frame->queue, &cam->working_q); ++ __raw_writel(frame->paddress, CSI_CSIDMASA_FB2); ++ frame->csi_buf_num = 2; ++ spin_unlock_irqrestore(&cam->queue_int_lock, flags); ++ ++ cam->capture_pid = current->pid; ++ cam->capture_on = true; ++ csi_cap_image(cam); ++ ++ local_irq_save(flags); ++ for (timeout = 1000000; timeout > 0; timeout--) { ++ if (__raw_readl(CSI_CSISR) & BIT_SOF_INT) { ++ val = __raw_readl(CSI_CSICR3); ++ __raw_writel(val | BIT_DMA_REFLASH_RFF, CSI_CSICR3); ++ for (timeout2 = 1000000; timeout2 > 0; timeout2--) { ++ if (__raw_readl(CSI_CSICR3) & ++ BIT_DMA_REFLASH_RFF) ++ cpu_relax(); ++ else ++ break; ++ } ++ if (timeout2 <= 0) { ++ pr_err("timeout when wait for reflash done.\n"); ++ local_irq_restore(flags); ++ return -ETIME; ++ } ++ ++ csi_dmareq_rff_enable(); ++ csi_enable_int(1); ++ break; ++ } else ++ cpu_relax(); ++ } ++ if (timeout <= 0) { ++ pr_err("timeout when wait for SOF\n"); ++ local_irq_restore(flags); ++ return -ETIME; ++ } ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/*! ++ * Stop stream I/O ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int csi_streamoff(cam_data *cam) ++{ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ if (cam->capture_on == false) ++ return 0; ++ ++ csi_dmareq_rff_disable(); ++ csi_disable_int(); ++ cam->capture_on = false; ++ ++ /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */ ++ __raw_writel(0, CSI_CSIDMASA_FB1); ++ __raw_writel(0, CSI_CSIDMASA_FB2); ++ ++ csi_free_frames(cam); ++ csi_free_frame_buf(cam); ++ ++ return 0; ++} ++ ++/*! ++ * start the viewfinder job ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int start_preview(cam_data *cam) ++{ ++ unsigned long fb_addr = (unsigned long)cam->v4l2_fb.base; ++ ++ __raw_writel(fb_addr, CSI_CSIDMASA_FB1); ++ __raw_writel(fb_addr, CSI_CSIDMASA_FB2); ++ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3); ++ ++ csi_enable_int(0); ++ ++ return 0; ++} ++ ++/*! ++ * shut down the viewfinder job ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int stop_preview(cam_data *cam) ++{ ++ csi_disable_int(); ++ ++ /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */ ++ __raw_writel(0, CSI_CSIDMASA_FB1); ++ __raw_writel(0, CSI_CSIDMASA_FB2); ++ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3); ++ ++ return 0; ++} ++ ++/*************************************************************************** ++ * VIDIOC Functions. ++ **************************************************************************/ ++ ++/*! ++ * ++ * @param cam structure cam_data * ++ * ++ * @param f structure v4l2_format * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int csi_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f) ++{ ++ int retval = 0; ++ ++ switch (f->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); ++ f->fmt.pix = cam->v2f.fmt.pix; ++ break; ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); ++ f->fmt.win = cam->win; ++ break; ++ default: ++ pr_debug(" type is invalid\n"); ++ retval = -EINVAL; ++ } ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ ++ return retval; ++} ++ ++/*! ++ * V4L2 - csi_v4l2_s_fmt function ++ * ++ * @param cam structure cam_data * ++ * ++ * @param f structure v4l2_format * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) ++{ ++ int retval = 0; ++ int size = 0; ++ int bytesperline = 0; ++ int *width, *height; ++ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ switch (f->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); ++ if (!valid_mode(f->fmt.pix.pixelformat)) { ++ pr_err("ERROR: v4l2 capture: %s: format " ++ "not supported\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* Handle case where size requested is larger than cuurent ++ * camera setting. */ ++ if ((f->fmt.pix.width > cam->crop_bounds.width) ++ || (f->fmt.pix.height > cam->crop_bounds.height)) { ++ /* Need the logic here, calling vidioc_s_param if ++ * camera can change. */ ++ pr_debug("csi_v4l2_s_fmt size changed\n"); ++ } ++ if (cam->rotation % 180) { ++ height = &f->fmt.pix.width; ++ width = &f->fmt.pix.height; ++ } else { ++ width = &f->fmt.pix.width; ++ height = &f->fmt.pix.height; ++ } ++ ++ if ((cam->crop_bounds.width / *width > 8) || ++ ((cam->crop_bounds.width / *width == 8) && ++ (cam->crop_bounds.width % *width))) { ++ *width = cam->crop_bounds.width / 8; ++ if (*width % 8) ++ *width += 8 - *width % 8; ++ pr_err("ERROR: v4l2 capture: width exceeds limit " ++ "resize to %d.\n", *width); ++ } ++ ++ if ((cam->crop_bounds.height / *height > 8) || ++ ((cam->crop_bounds.height / *height == 8) && ++ (cam->crop_bounds.height % *height))) { ++ *height = cam->crop_bounds.height / 8; ++ if (*height % 8) ++ *height += 8 - *height % 8; ++ pr_err("ERROR: v4l2 capture: height exceeds limit " ++ "resize to %d.\n", *height); ++ } ++ ++ switch (f->fmt.pix.pixelformat) { ++ case V4L2_PIX_FMT_RGB565: ++ size = f->fmt.pix.width * f->fmt.pix.height * 2; ++ csi_init_format(V4L2_PIX_FMT_UYVY); ++ csi_set_16bit_imagpara(f->fmt.pix.width, ++ f->fmt.pix.height); ++ bytesperline = f->fmt.pix.width * 2; ++ break; ++ case V4L2_PIX_FMT_UYVY: ++ size = f->fmt.pix.width * f->fmt.pix.height * 2; ++ csi_init_format(f->fmt.pix.pixelformat); ++ csi_set_16bit_imagpara(f->fmt.pix.width, ++ f->fmt.pix.height); ++ bytesperline = f->fmt.pix.width * 2; ++ break; ++ case V4L2_PIX_FMT_YUYV: ++ size = f->fmt.pix.width * f->fmt.pix.height * 2; ++ csi_init_format(f->fmt.pix.pixelformat); ++ csi_set_16bit_imagpara(f->fmt.pix.width, ++ f->fmt.pix.height); ++ bytesperline = f->fmt.pix.width * 2; ++ break; ++ case V4L2_PIX_FMT_YUV420: ++ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; ++ csi_set_12bit_imagpara(f->fmt.pix.width, ++ f->fmt.pix.height); ++ bytesperline = f->fmt.pix.width; ++ break; ++ case V4L2_PIX_FMT_YUV422P: ++ case V4L2_PIX_FMT_RGB24: ++ case V4L2_PIX_FMT_BGR24: ++ case V4L2_PIX_FMT_BGR32: ++ case V4L2_PIX_FMT_RGB32: ++ case V4L2_PIX_FMT_NV12: ++ default: ++ pr_debug(" case not supported\n"); ++ break; ++ } ++ ++ if (f->fmt.pix.bytesperline < bytesperline) ++ f->fmt.pix.bytesperline = bytesperline; ++ else ++ bytesperline = f->fmt.pix.bytesperline; ++ ++ if (f->fmt.pix.sizeimage < size) ++ f->fmt.pix.sizeimage = size; ++ else ++ size = f->fmt.pix.sizeimage; ++ ++ cam->v2f.fmt.pix = f->fmt.pix; ++ ++ if (cam->v2f.fmt.pix.priv != 0) { ++ if (copy_from_user(&cam->offset, ++ (void *)cam->v2f.fmt.pix.priv, ++ sizeof(cam->offset))) { ++ retval = -EFAULT; ++ break; ++ } ++ } ++ break; ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); ++ cam->win = f->fmt.win; ++ win_current = f->fmt.win; ++ size = win_current.w.width * win_current.w.height * 2; ++ if (cam->v2f.fmt.pix.sizeimage < size) ++ cam->v2f.fmt.pix.sizeimage = size; ++ ++ break; ++ default: ++ retval = -EINVAL; ++ } ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ ++ return retval; ++} ++ ++/*! ++ * V4L2 - csi_v4l2_s_param function ++ * Allows setting of capturemode and frame rate. ++ * ++ * @param cam structure cam_data * ++ * @param parm structure v4l2_streamparm * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int csi_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) ++{ ++ struct v4l2_ifparm ifparm; ++ struct v4l2_format cam_fmt; ++ struct v4l2_streamparm currentparm; ++ int err = 0; ++ ++ pr_debug("In %s\n", __func__); ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ pr_err(KERN_ERR "%s invalid type\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* Stop the viewfinder */ ++ if (cam->overlay_on == true) ++ stop_preview(cam); ++ ++ currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ /* First check that this device can support the changes requested. */ ++ err = vidioc_int_g_parm(cam->sensor, ¤tparm); ++ if (err) { ++ pr_err("%s: vidioc_int_g_parm returned an error %d\n", ++ __func__, err); ++ goto exit; ++ } ++ ++ pr_debug(" Current capabilities are %x\n", ++ currentparm.parm.capture.capability); ++ pr_debug(" Current capturemode is %d change to %d\n", ++ currentparm.parm.capture.capturemode, ++ parm->parm.capture.capturemode); ++ pr_debug(" Current framerate is %d change to %d\n", ++ currentparm.parm.capture.timeperframe.denominator, ++ parm->parm.capture.timeperframe.denominator); ++ ++ err = vidioc_int_s_parm(cam->sensor, parm); ++ if (err) { ++ pr_err("%s: vidioc_int_s_parm returned an error %d\n", ++ __func__, err); ++ goto exit; ++ } ++ ++ vidioc_int_g_ifparm(cam->sensor, &ifparm); ++ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); ++ pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", ++ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); ++ ++ cam->crop_bounds.top = cam->crop_bounds.left = 0; ++ cam->crop_bounds.width = cam_fmt.fmt.pix.width; ++ cam->crop_bounds.height = cam_fmt.fmt.pix.height; ++ cam->crop_current.width = cam->crop_bounds.width; ++ cam->crop_current.height = cam->crop_bounds.height; ++ ++exit: ++ return err; ++} ++ ++static int pxp_set_cstate(cam_data *cam, struct v4l2_control *vc) ++{ ++ struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; ++ ++ if (vc->id == V4L2_CID_HFLIP) { ++ proc_data->hflip = vc->value; ++ } else if (vc->id == V4L2_CID_VFLIP) { ++ proc_data->vflip = vc->value; ++ } else if (vc->id == V4L2_CID_PRIVATE_BASE) { ++ if (vc->value % 90) ++ return -ERANGE; ++ proc_data->rotate = vc->value; ++ cam->rotation = vc->value; ++ } ++ ++ return 0; ++} ++ ++static int pxp_get_cstate(cam_data *cam, struct v4l2_control *vc) ++{ ++ struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; ++ ++ if (vc->id == V4L2_CID_HFLIP) ++ vc->value = proc_data->hflip; ++ else if (vc->id == V4L2_CID_VFLIP) ++ vc->value = proc_data->vflip; ++ else if (vc->id == V4L2_CID_PRIVATE_BASE) ++ vc->value = proc_data->rotate; ++ ++ return 0; ++} ++ ++ ++/*! ++ * Dequeue one V4L capture buffer ++ * ++ * @param cam structure cam_data * ++ * @param buf structure v4l2_buffer * ++ * ++ * @return status 0 success, EINVAL invalid frame number ++ * ETIME timeout, ERESTARTSYS interrupted by user ++ */ ++static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) ++{ ++ int retval = 0; ++ struct mxc_v4l_frame *frame; ++ unsigned long lock_flags; ++ ++ if (!wait_event_interruptible_timeout(cam->enc_queue, ++ cam->enc_counter != 0, 10 * HZ)) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout " ++ "enc_counter %x\n", cam->enc_counter); ++ return -ETIME; ++ } else if (signal_pending(current)) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() " ++ "interrupt received\n"); ++ return -ERESTARTSYS; ++ } ++ ++ if (down_interruptible(&cam->busy_lock)) ++ return -EBUSY; ++ ++ spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags); ++ ++ if (list_empty(&cam->done_q)) { ++ spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags); ++ up(&cam->busy_lock); ++ return -EINVAL; ++ } ++ ++ cam->enc_counter--; ++ ++ frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue); ++ list_del(cam->done_q.next); ++ ++ if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) { ++ frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE; ++ } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " ++ "Buffer not filled.\n"); ++ frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; ++ retval = -EINVAL; ++ } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " ++ "Buffer not queued.\n"); ++ retval = -EINVAL; ++ } ++ ++ spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags); ++ ++ buf->bytesused = cam->v2f.fmt.pix.sizeimage; ++ buf->index = frame->index; ++ buf->flags = frame->buffer.flags; ++ buf->m = cam->frame[frame->index].buffer.m; ++ ++ /* ++ * Note: ++ * If want to do preview on LCD, use PxP CSC to convert from UYVY ++ * to RGB565; but for encoding, usually we don't use RGB format. ++ */ ++ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { ++ sg_dma_address(&cam->sg[0]) = buf->m.offset; ++ sg_dma_address(&cam->sg[1]) = ++ cam->frame[req_buf_number].paddress; ++ retval = pxp_process_update(cam); ++ if (retval) { ++ pr_err("Unable to submit PxP update task.\n"); ++ return retval; ++ } ++ pxp_complete_update(cam); ++ if (cam->frame[buf->index].vaddress) ++ memcpy(cam->frame[buf->index].vaddress, ++ cam->frame[req_buf_number].vaddress, ++ cam->v2f.fmt.pix.sizeimage); ++ } ++ up(&cam->busy_lock); ++ ++ return retval; ++} ++ ++/*! ++ * V4L interface - open function ++ * ++ * @param file structure file * ++ * ++ * @return status 0 success, ENODEV invalid device instance, ++ * ENODEV timeout, ERESTARTSYS interrupted by user ++ */ ++static int csi_v4l_open(struct file *file) ++{ ++ struct v4l2_ifparm ifparm; ++ struct v4l2_format cam_fmt; ++ struct video_device *dev = video_devdata(file); ++ cam_data *cam = video_get_drvdata(dev); ++ struct sensor_data *sensor; ++ int err = 0; ++ ++ pr_debug(" device name is %s\n", dev->name); ++ ++ if (!cam) { ++ pr_err("%s: Internal error, cam_data not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ if (!cam->sensor) { ++ pr_err("%s: Internal error, camera is not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ sensor = cam->sensor->priv; ++ if (!sensor) { ++ pr_err("%s: Internal error, sensor_data is not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ down(&cam->busy_lock); ++ err = 0; ++ if (signal_pending(current)) ++ goto oops; ++ ++ if (cam->open_count++ == 0) { ++ wait_event_interruptible(cam->power_queue, ++ cam->low_power == false); ++ ++ cam->enc_counter = 0; ++ INIT_LIST_HEAD(&cam->ready_q); ++ INIT_LIST_HEAD(&cam->working_q); ++ INIT_LIST_HEAD(&cam->done_q); ++ ++ vidioc_int_g_ifparm(cam->sensor, &ifparm); ++ ++ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ clk_prepare_enable(sensor->sensor_clk); ++ vidioc_int_s_power(cam->sensor, 1); ++ vidioc_int_init(cam->sensor); ++ vidioc_int_dev_init(cam->sensor); ++ } ++ ++ file->private_data = dev; ++ ++oops: ++ up(&cam->busy_lock); ++ return err; ++} ++ ++/*! ++ * V4L interface - close function ++ * ++ * @param file struct file * ++ * ++ * @return 0 success ++ */ ++static int csi_v4l_close(struct file *file) ++{ ++ struct video_device *dev = video_devdata(file); ++ int err = 0; ++ cam_data *cam = video_get_drvdata(dev); ++ struct sensor_data *sensor; ++ ++ pr_debug("In MVC:%s\n", __func__); ++ ++ if (!cam) { ++ pr_err("%s: Internal error, cam_data not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ if (!cam->sensor) { ++ pr_err("%s: Internal error, camera is not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ sensor = cam->sensor->priv; ++ if (!sensor) { ++ pr_err("%s: Internal error, sensor_data is not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ /* for the case somebody hit the ctrl C */ ++ if (cam->overlay_pid == current->pid) { ++ err = stop_preview(cam); ++ cam->overlay_on = false; ++ } ++ ++ if (--cam->open_count == 0) { ++ wait_event_interruptible(cam->power_queue, ++ cam->low_power == false); ++ file->private_data = NULL; ++ vidioc_int_s_power(cam->sensor, 0); ++ clk_disable_unprepare(sensor->sensor_clk); ++ } ++ ++ return err; ++} ++ ++/* ++ * V4L interface - read function ++ * ++ * @param file struct file * ++ * @param read buf char * ++ * @param count size_t ++ * @param ppos structure loff_t * ++ * ++ * @return bytes read ++ */ ++static ssize_t csi_v4l_read(struct file *file, char *buf, size_t count, ++ loff_t *ppos) ++{ ++ int err = 0; ++ struct video_device *dev = video_devdata(file); ++ cam_data *cam = video_get_drvdata(dev); ++ ++ if (down_interruptible(&cam->busy_lock)) ++ return -EINTR; ++ ++ /* Stop the viewfinder */ ++ if (cam->overlay_on == true) ++ stop_preview(cam); ++ ++ if (cam->still_buf_vaddr == NULL) { ++ cam->still_buf_vaddr = dma_alloc_coherent(0, ++ PAGE_ALIGN ++ (cam->v2f.fmt. ++ pix.sizeimage), ++ &cam-> ++ still_buf[0], ++ GFP_DMA | GFP_KERNEL); ++ if (cam->still_buf_vaddr == NULL) { ++ pr_err("alloc dma memory failed\n"); ++ return -ENOMEM; ++ } ++ cam->still_counter = 0; ++ __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB2); ++ __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB1); ++ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, ++ CSI_CSICR3); ++ __raw_writel(__raw_readl(CSI_CSISR), CSI_CSISR); ++ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, ++ CSI_CSICR3); ++ csi_enable_int(1); ++ } ++ ++ wait_event_interruptible(cam->still_queue, cam->still_counter); ++ csi_disable_int(); ++ err = copy_to_user(buf, cam->still_buf_vaddr, ++ cam->v2f.fmt.pix.sizeimage); ++ ++ if (cam->still_buf_vaddr != NULL) { ++ dma_free_coherent(0, PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), ++ cam->still_buf_vaddr, cam->still_buf[0]); ++ cam->still_buf[0] = 0; ++ cam->still_buf_vaddr = NULL; ++ } ++ ++ if (cam->overlay_on == true) ++ start_preview(cam); ++ ++ up(&cam->busy_lock); ++ if (err < 0) ++ return err; ++ ++ return cam->v2f.fmt.pix.sizeimage - err; ++} ++ ++/*! ++ * V4L interface - ioctl function ++ * ++ * @param file struct file* ++ * ++ * @param ioctlnr unsigned int ++ * ++ * @param arg void* ++ * ++ * @return 0 success, ENODEV for invalid device instance, ++ * -1 for other errors. ++ */ ++static long csi_v4l_do_ioctl(struct file *file, ++ unsigned int ioctlnr, void *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ cam_data *cam = video_get_drvdata(dev); ++ int retval = 0; ++ unsigned long lock_flags; ++ ++ pr_debug("In MVC: %s, %x\n", __func__, ioctlnr); ++ wait_event_interruptible(cam->power_queue, cam->low_power == false); ++ /* make this _really_ smp-safe */ ++ if (ioctlnr != VIDIOC_DQBUF) ++ if (down_interruptible(&cam->busy_lock)) ++ return -EBUSY; ++ ++ switch (ioctlnr) { ++ /*! ++ * V4l2 VIDIOC_G_FMT ioctl ++ */ ++ case VIDIOC_G_FMT:{ ++ struct v4l2_format *gf = arg; ++ pr_debug(" case VIDIOC_G_FMT\n"); ++ retval = csi_v4l2_g_fmt(cam, gf); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_S_FMT ioctl ++ */ ++ case VIDIOC_S_FMT:{ ++ struct v4l2_format *sf = arg; ++ pr_debug(" case VIDIOC_S_FMT\n"); ++ retval = csi_v4l2_s_fmt(cam, sf); ++ vidioc_int_s_fmt_cap(cam->sensor, sf); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_OVERLAY ioctl ++ */ ++ case VIDIOC_OVERLAY:{ ++ int *on = arg; ++ pr_debug(" case VIDIOC_OVERLAY\n"); ++ if (*on) { ++ cam->overlay_on = true; ++ cam->overlay_pid = current->pid; ++ start_preview(cam); ++ } ++ if (!*on) { ++ stop_preview(cam); ++ cam->overlay_on = false; ++ } ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_G_FBUF ioctl ++ */ ++ case VIDIOC_G_FBUF:{ ++ struct v4l2_framebuffer *fb = arg; ++ *fb = cam->v4l2_fb; ++ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY; ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_S_FBUF ioctl ++ */ ++ case VIDIOC_S_FBUF:{ ++ struct v4l2_framebuffer *fb = arg; ++ cam->v4l2_fb = *fb; ++ break; ++ } ++ ++ case VIDIOC_G_PARM:{ ++ struct v4l2_streamparm *parm = arg; ++ pr_debug(" case VIDIOC_G_PARM\n"); ++ vidioc_int_g_parm(cam->sensor, parm); ++ break; ++ } ++ ++ case VIDIOC_S_PARM:{ ++ struct v4l2_streamparm *parm = arg; ++ pr_debug(" case VIDIOC_S_PARM\n"); ++ retval = csi_v4l2_s_param(cam, parm); ++ break; ++ } ++ ++ case VIDIOC_QUERYCAP:{ ++ struct v4l2_capability *cap = arg; ++ pr_debug(" case VIDIOC_QUERYCAP\n"); ++ strcpy(cap->driver, "csi_v4l2"); ++ cap->version = KERNEL_VERSION(0, 1, 11); ++ cap->capabilities = V4L2_CAP_VIDEO_OVERLAY | ++ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | ++ V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_READWRITE; ++ cap->card[0] = '\0'; ++ cap->bus_info[0] = '\0'; ++ break; ++ } ++ ++ case VIDIOC_CROPCAP: ++ { ++ struct v4l2_cropcap *cap = arg; ++ ++ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { ++ retval = -EINVAL; ++ break; ++ } ++ cap->bounds = cam->crop_bounds; ++ cap->defrect = cam->crop_defrect; ++ break; ++ } ++ case VIDIOC_S_CROP: ++ { ++ struct v4l2_crop *crop = arg; ++ struct v4l2_rect *b = &cam->crop_bounds; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ retval = -EINVAL; ++ break; ++ } ++ ++ crop->c.top = (crop->c.top < b->top) ? b->top ++ : crop->c.top; ++ if (crop->c.top > b->top + b->height) ++ crop->c.top = b->top + b->height - 1; ++ if (crop->c.height > b->top + b->height - crop->c.top) ++ crop->c.height = ++ b->top + b->height - crop->c.top; ++ ++ crop->c.left = (crop->c.left < b->left) ? b->left ++ : crop->c.left; ++ if (crop->c.left > b->left + b->width) ++ crop->c.left = b->left + b->width - 1; ++ if (crop->c.width > b->left - crop->c.left + b->width) ++ crop->c.width = ++ b->left - crop->c.left + b->width; ++ ++ crop->c.width -= crop->c.width % 8; ++ crop->c.height -= crop->c.height % 8; ++ ++ crop_current.c = crop->c; ++ ++ break; ++ } ++ case VIDIOC_G_CROP: ++ { ++ struct v4l2_crop *crop = arg; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ retval = -EINVAL; ++ break; ++ } ++ crop->c = crop_current.c; ++ ++ break; ++ ++ } ++ case VIDIOC_REQBUFS: { ++ struct v4l2_requestbuffers *req = arg; ++ pr_debug(" case VIDIOC_REQBUFS\n"); ++ ++ if (req->count > FRAME_NUM) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " ++ "not enough buffers\n"); ++ req->count = FRAME_NUM; ++ } ++ ++ if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " ++ "wrong buffer type\n"); ++ retval = -EINVAL; ++ break; ++ } ++ ++ csi_streamoff(cam); ++ if (req->memory & V4L2_MEMORY_MMAP) { ++ csi_free_frame_buf(cam); ++ retval = csi_allocate_frame_buf(cam, req->count + 1); ++ req_buf_number = req->count; ++ } ++ break; ++ } ++ ++ case VIDIOC_QUERYBUF: { ++ struct v4l2_buffer *buf = arg; ++ int index = buf->index; ++ pr_debug(" case VIDIOC_QUERYBUF\n"); ++ ++ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ retval = -EINVAL; ++ break; ++ } ++ ++ if (buf->memory & V4L2_MEMORY_MMAP) { ++ memset(buf, 0, sizeof(buf)); ++ buf->index = index; ++ } ++ ++ down(&cam->param_lock); ++ if (buf->memory & V4L2_MEMORY_USERPTR) { ++ csi_v4l2_release_bufs(cam); ++ retval = csi_v4l2_prepare_bufs(cam, buf); ++ } ++ if (buf->memory & V4L2_MEMORY_MMAP) ++ retval = csi_v4l2_buffer_status(cam, buf); ++ up(&cam->param_lock); ++ break; ++ } ++ ++ case VIDIOC_QBUF: { ++ struct v4l2_buffer *buf = arg; ++ int index = buf->index; ++ pr_debug(" case VIDIOC_QBUF\n"); ++ ++ spin_lock_irqsave(&cam->queue_int_lock, lock_flags); ++ cam->frame[index].buffer.m.offset = buf->m.offset; ++ if ((cam->frame[index].buffer.flags & 0x7) == ++ V4L2_BUF_FLAG_MAPPED) { ++ cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED; ++ list_add_tail(&cam->frame[index].queue, &cam->ready_q); ++ } else if (cam->frame[index].buffer.flags & ++ V4L2_BUF_FLAG_QUEUED) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " ++ "buffer already queued\n"); ++ retval = -EINVAL; ++ } else if (cam->frame[index].buffer. ++ flags & V4L2_BUF_FLAG_DONE) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " ++ "overwrite done buffer.\n"); ++ cam->frame[index].buffer.flags &= ++ ~V4L2_BUF_FLAG_DONE; ++ cam->frame[index].buffer.flags |= ++ V4L2_BUF_FLAG_QUEUED; ++ retval = -EINVAL; ++ } ++ buf->flags = cam->frame[index].buffer.flags; ++ spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); ++ ++ break; ++ } ++ ++ case VIDIOC_DQBUF: { ++ struct v4l2_buffer *buf = arg; ++ pr_debug(" case VIDIOC_DQBUF\n"); ++ ++ retval = csi_v4l_dqueue(cam, buf); ++ ++ break; ++ } ++ ++ case VIDIOC_STREAMON: { ++ pr_debug(" case VIDIOC_STREAMON\n"); ++ retval = csi_streamon(cam); ++ break; ++ } ++ ++ case VIDIOC_STREAMOFF: { ++ pr_debug(" case VIDIOC_STREAMOFF\n"); ++ retval = csi_streamoff(cam); ++ break; ++ } ++ case VIDIOC_ENUM_FMT: { ++ struct v4l2_fmtdesc *fmt = arg; ++ if (cam->sensor) ++ retval = vidioc_int_enum_fmt_cap(cam->sensor, fmt); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ case VIDIOC_ENUM_FRAMESIZES: { ++ struct v4l2_frmsizeenum *fsize = arg; ++ if (cam->sensor) ++ retval = vidioc_int_enum_framesizes(cam->sensor, fsize); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ case VIDIOC_ENUM_FRAMEINTERVALS: { ++ struct v4l2_frmivalenum *fival = arg; ++ if (cam->sensor) ++ retval = vidioc_int_enum_frameintervals(cam->sensor, ++ fival); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ case VIDIOC_DBG_G_CHIP_IDENT: { ++ struct v4l2_dbg_chip_ident *p = arg; ++ p->ident = V4L2_IDENT_NONE; ++ p->revision = 0; ++ if (cam->sensor) ++ retval = vidioc_int_g_chip_ident(cam->sensor, (int *)p); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ ++ case VIDIOC_S_CTRL: ++ { ++ struct v4l2_control *vc = arg; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) ++ if (vc->id == pxp_controls[i].id) { ++ if (vc->value < pxp_controls[i].minimum || ++ vc->value > pxp_controls[i].maximum) { ++ retval = -ERANGE; ++ break; ++ } ++ retval = pxp_set_cstate(cam, vc); ++ break; ++ } ++ ++ if (i >= ARRAY_SIZE(pxp_controls)) ++ retval = -EINVAL; ++ break; ++ ++ } ++ case VIDIOC_G_CTRL: ++ { ++ struct v4l2_control *vc = arg; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) ++ if (vc->id == pxp_controls[i].id) { ++ retval = pxp_get_cstate(cam, vc); ++ break; ++ } ++ ++ if (i >= ARRAY_SIZE(pxp_controls)) ++ retval = -EINVAL; ++ break; ++ } ++ case VIDIOC_QUERYCTRL: ++ { ++ struct v4l2_queryctrl *qc = arg; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) ++ if (qc->id && qc->id == pxp_controls[i].id) { ++ memcpy(qc, &(pxp_controls[i]), sizeof(*qc)); ++ break; ++ } ++ ++ if (i >= ARRAY_SIZE(pxp_controls)) ++ retval = -EINVAL; ++ break; ++ } ++ case VIDIOC_G_STD: ++ case VIDIOC_G_OUTPUT: ++ case VIDIOC_S_OUTPUT: ++ case VIDIOC_ENUMSTD: ++ case VIDIOC_S_STD: ++ case VIDIOC_TRY_FMT: ++ case VIDIOC_ENUMINPUT: ++ case VIDIOC_G_INPUT: ++ case VIDIOC_S_INPUT: ++ case VIDIOC_G_TUNER: ++ case VIDIOC_S_TUNER: ++ case VIDIOC_G_FREQUENCY: ++ case VIDIOC_S_FREQUENCY: ++ case VIDIOC_ENUMOUTPUT: ++ default: ++ pr_debug(" case not supported\n"); ++ retval = -EINVAL; ++ break; ++ } ++ ++ if (ioctlnr != VIDIOC_DQBUF) ++ up(&cam->busy_lock); ++ return retval; ++} ++ ++/* ++ * V4L interface - ioctl function ++ * ++ * @return None ++ */ ++static long csi_v4l_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ return video_usercopy(file, cmd, arg, csi_v4l_do_ioctl); ++} ++ ++/*! ++ * V4L interface - mmap function ++ * ++ * @param file structure file * ++ * ++ * @param vma structure vm_area_struct * ++ * ++ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error ++ */ ++static int csi_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct video_device *dev = video_devdata(file); ++ unsigned long size; ++ int res = 0; ++ cam_data *cam = video_get_drvdata(dev); ++ ++ pr_debug("%s\n", __func__); ++ pr_debug("\npgoff=0x%lx, start=0x%lx, end=0x%lx\n", ++ vma->vm_pgoff, vma->vm_start, vma->vm_end); ++ ++ /* make this _really_ smp-safe */ ++ if (down_interruptible(&cam->busy_lock)) ++ return -EINTR; ++ ++ size = vma->vm_end - vma->vm_start; ++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); ++ ++ if (remap_pfn_range(vma, vma->vm_start, ++ vma->vm_pgoff, size, vma->vm_page_prot)) { ++ pr_err("ERROR: v4l2 capture: %s : " ++ "remap_pfn_range failed\n", __func__); ++ res = -ENOBUFS; ++ goto csi_mmap_exit; ++ } ++ ++ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ ++ ++csi_mmap_exit: ++ up(&cam->busy_lock); ++ return res; ++} ++ ++/*! ++ * This structure defines the functions to be called in this driver. ++ */ ++static struct v4l2_file_operations csi_v4l_fops = { ++ .owner = THIS_MODULE, ++ .open = csi_v4l_open, ++ .release = csi_v4l_close, ++ .read = csi_v4l_read, ++ .ioctl = csi_v4l_ioctl, ++ .mmap = csi_mmap, ++}; ++ ++static struct video_device csi_v4l_template = { ++ .name = "Mx25 Camera", ++ .fops = &csi_v4l_fops, ++ .release = video_device_release, ++}; ++ ++/*! ++ * initialize cam_data structure ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static void init_camera_struct(cam_data *cam) ++{ ++ struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; ++ pr_debug("In MVC: %s\n", __func__); ++ ++ proc_data->hflip = 0; ++ proc_data->vflip = 0; ++ proc_data->rotate = 0; ++ proc_data->bgcolor = 0; ++ ++ /* Default everything to 0 */ ++ memset(cam, 0, sizeof(cam_data)); ++ ++ sema_init(&cam->param_lock, 1); ++ sema_init(&cam->busy_lock, 1); ++ ++ cam->video_dev = video_device_alloc(); ++ if (cam->video_dev == NULL) ++ return; ++ ++ *(cam->video_dev) = csi_v4l_template; ++ ++ video_set_drvdata(cam->video_dev, cam); ++ cam->video_dev->minor = -1; ++ ++ init_waitqueue_head(&cam->enc_queue); ++ init_waitqueue_head(&cam->still_queue); ++ ++ cam->streamparm.parm.capture.capturemode = 0; ++ ++ cam->standard.index = 0; ++ cam->standard.id = V4L2_STD_UNKNOWN; ++ cam->standard.frameperiod.denominator = 30; ++ cam->standard.frameperiod.numerator = 1; ++ cam->standard.framelines = 480; ++ cam->standard_autodetect = true; ++ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; ++ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ cam->overlay_on = false; ++ cam->capture_on = false; ++ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY; ++ ++ cam->v2f.fmt.pix.sizeimage = 480 * 640 * 2; ++ cam->v2f.fmt.pix.bytesperline = 640 * 2; ++ cam->v2f.fmt.pix.width = 640; ++ cam->v2f.fmt.pix.height = 480; ++ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; ++ cam->win.w.width = 160; ++ cam->win.w.height = 160; ++ cam->win.w.left = 0; ++ cam->win.w.top = 0; ++ cam->still_counter = 0; ++ /* setup cropping */ ++ cam->crop_bounds.left = 0; ++ cam->crop_bounds.width = 640; ++ cam->crop_bounds.top = 0; ++ cam->crop_bounds.height = 480; ++ cam->crop_current = cam->crop_defrect = cam->crop_bounds; ++ ++ cam->enc_callback = camera_callback; ++ csi_start_callback(cam); ++ init_waitqueue_head(&cam->power_queue); ++ spin_lock_init(&cam->queue_int_lock); ++ spin_lock_init(&cam->dqueue_int_lock); ++} ++ ++/*! ++ * camera_power function ++ * Turns Sensor power On/Off ++ * ++ * @param cam cam data struct ++ * @param cameraOn true to turn camera on, false to turn off power. ++ * ++ * @return status ++ */ ++static u8 camera_power(cam_data *cam, bool cameraOn) ++{ ++ pr_debug("In MVC: %s on=%d\n", __func__, cameraOn); ++ ++ if (cameraOn == true) { ++ vidioc_int_s_power(cam->sensor, 1); ++ } else { ++ vidioc_int_s_power(cam->sensor, 0); ++ } ++ return 0; ++} ++ ++static const struct of_device_id imx_csi_v4l2_dt_ids[] = { ++ { .compatible = "fsl,imx6sl-csi-v4l2", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, imx_csi_v4l2_dt_ids); ++ ++static int csi_v4l2_probe(struct platform_device *pdev) ++{ ++ struct scatterlist *sg; ++ u8 err = 0; ++ ++ /* Create g_cam and initialize it. */ ++ g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL); ++ if (g_cam == NULL) { ++ pr_err("ERROR: v4l2 capture: failed to register camera\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ memset(&crop_current, 0, sizeof(crop_current)); ++ memset(&win_current, 0, sizeof(win_current)); ++ init_camera_struct(g_cam); ++ platform_set_drvdata(pdev, (void *)g_cam); ++ ++ /* Set up the v4l2 device and register it */ ++ csi_v4l2_int_device.priv = g_cam; ++ /* This function contains a bug that won't let this be rmmod'd. */ ++ v4l2_int_device_register(&csi_v4l2_int_device); ++ ++ /* register v4l video device */ ++ if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr) ++ == -1) { ++ kfree(g_cam); ++ g_cam = NULL; ++ pr_err("ERROR: v4l2 capture: video_register_device failed\n"); ++ err = -ENODEV; ++ goto out; ++ } ++ pr_debug(" Video device registered: %s #%d\n", ++ g_cam->video_dev->name, g_cam->video_dev->minor); ++ ++ g_cam->pxp_chan = NULL; ++ /* Initialize Scatter-gather list containing 2 buffer addresses. */ ++ sg = g_cam->sg; ++ sg_init_table(sg, 2); ++ ++out: ++ return err; ++} ++ ++static int csi_v4l2_remove(struct platform_device *pdev) ++{ ++ if (g_cam->open_count) { ++ pr_err("ERROR: v4l2 capture:camera open " ++ "-- setting ops to NULL\n"); ++ } else { ++ pr_info("V4L2 freeing image input device\n"); ++ v4l2_int_device_unregister(&csi_v4l2_int_device); ++ csi_stop_callback(g_cam); ++ video_unregister_device(g_cam->video_dev); ++ platform_set_drvdata(pdev, NULL); ++ ++ kfree(g_cam); ++ g_cam = NULL; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * This function is called to put the sensor in a low power state. ++ * Refer to the document driver-model/driver.txt in the kernel source tree ++ * for more information. ++ * ++ * @param pdev the device structure used to give information on which I2C ++ * to suspend ++ * @param state the power state the device is entering ++ * ++ * @return The function returns 0 on success and -1 on failure. ++ */ ++static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ cam_data *cam = platform_get_drvdata(pdev); ++ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ if (cam == NULL) ++ return -1; ++ ++ cam->low_power = true; ++ ++ if (cam->overlay_on == true) ++ stop_preview(cam); ++ ++ if (cam->capture_on == true || cam->overlay_on == true) ++ camera_power(cam, false); ++ ++ return 0; ++} ++ ++/*! ++ * This function is called to bring the sensor back from a low power state. ++ * Refer to the document driver-model/driver.txt in the kernel source tree ++ * for more information. ++ * ++ * @param pdev the device structure ++ * ++ * @return The function returns 0 on success and -1 on failure ++ */ ++static int csi_v4l2_resume(struct platform_device *pdev) ++{ ++ cam_data *cam = platform_get_drvdata(pdev); ++ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ if (cam == NULL) ++ return -1; ++ ++ cam->low_power = false; ++ wake_up_interruptible(&cam->power_queue); ++ if (cam->capture_on == true || cam->overlay_on == true) ++ camera_power(cam, true); ++ ++ if (cam->overlay_on == true) ++ start_preview(cam); ++ ++ return 0; ++} ++ ++/*! ++ * This structure contains pointers to the power management callback functions. ++ */ ++static struct platform_driver csi_v4l2_driver = { ++ .driver = { ++ .name = "csi_v4l2", ++ .of_match_table = of_match_ptr(imx_csi_v4l2_dt_ids), ++ }, ++ .probe = csi_v4l2_probe, ++ .remove = csi_v4l2_remove, ++#ifdef CONFIG_PM ++ .suspend = csi_v4l2_suspend, ++ .resume = csi_v4l2_resume, ++#endif ++ .shutdown = NULL, ++}; ++ ++/*! ++ * Initializes the camera driver. ++ */ ++static int csi_v4l2_master_attach(struct v4l2_int_device *slave) ++{ ++ cam_data *cam = slave->u.slave->master->priv; ++ struct v4l2_format cam_fmt; ++ ++ pr_debug("In MVC: %s\n", __func__); ++ pr_debug(" slave.name = %s\n", slave->name); ++ pr_debug(" master.name = %s\n", slave->u.slave->master->name); ++ ++ cam->sensor = slave; ++ if (slave == NULL) { ++ pr_err("ERROR: v4l2 capture: slave parameter not valid.\n"); ++ return -1; ++ } ++ ++ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); ++ ++ /* Used to detect TV in (type 1) vs. camera (type 0) */ ++ cam->device_type = cam_fmt.fmt.pix.priv; ++ ++ cam->crop_bounds.top = cam->crop_bounds.left = 0; ++ cam->crop_bounds.width = cam_fmt.fmt.pix.width; ++ cam->crop_bounds.height = cam_fmt.fmt.pix.height; ++ ++ /* This also is the max crop size for this device. */ ++ cam->crop_defrect.top = cam->crop_defrect.left = 0; ++ cam->crop_defrect.width = cam_fmt.fmt.pix.width; ++ cam->crop_defrect.height = cam_fmt.fmt.pix.height; ++ ++ /* At this point, this is also the current image size. */ ++ cam->crop_current.top = cam->crop_current.left = 0; ++ cam->crop_current.width = cam_fmt.fmt.pix.width; ++ cam->crop_current.height = cam_fmt.fmt.pix.height; ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ ++ return 0; ++} ++ ++/*! ++ * Disconnects the camera driver. ++ */ ++static void csi_v4l2_master_detach(struct v4l2_int_device *slave) ++{ ++ pr_debug("In MVC: %s\n", __func__); ++ ++ vidioc_int_dev_exit(slave); ++} ++ ++module_platform_driver(csi_v4l2_driver); ++ ++module_param(video_nr, int, 0444); ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("V4L2 capture driver for Mx25 based cameras"); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("video"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/fsl_csi.c linux-openelec/drivers/media/platform/mxc/capture/fsl_csi.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/fsl_csi.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/fsl_csi.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,302 @@ ++/* ++ * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file fsl_csi.c, this file is derived from mx27_csi.c ++ * ++ * @brief mx25 CMOS Sensor interface functions ++ * ++ * @ingroup CSI ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mxc_v4l2_capture.h" ++#include "fsl_csi.h" ++ ++void __iomem *csi_regbase; ++EXPORT_SYMBOL(csi_regbase); ++static int irq_nr; ++static csi_irq_callback_t g_callback; ++static void *g_callback_data; ++ ++static irqreturn_t csi_irq_handler(int irq, void *data) ++{ ++ cam_data *cam = (cam_data *) data; ++ unsigned long status = __raw_readl(CSI_CSISR); ++ ++ __raw_writel(status, CSI_CSISR); ++ ++ if (status & BIT_HRESP_ERR_INT) ++ pr_warning("Hresponse error is detected.\n"); ++ ++ if (status & BIT_DMA_TSF_DONE_FB1) { ++ if (cam->capture_on) { ++ spin_lock(&cam->queue_int_lock); ++ cam->ping_pong_csi = 1; ++ spin_unlock(&cam->queue_int_lock); ++ cam->enc_callback(0, cam); ++ } else { ++ cam->still_counter++; ++ wake_up_interruptible(&cam->still_queue); ++ } ++ } ++ ++ if (status & BIT_DMA_TSF_DONE_FB2) { ++ if (cam->capture_on) { ++ spin_lock(&cam->queue_int_lock); ++ cam->ping_pong_csi = 2; ++ spin_unlock(&cam->queue_int_lock); ++ cam->enc_callback(0, cam); ++ } else { ++ cam->still_counter++; ++ wake_up_interruptible(&cam->still_queue); ++ } ++ } ++ ++ if (g_callback) ++ g_callback(g_callback_data, status); ++ ++ pr_debug("CSI status = 0x%08lX\n", status); ++ ++ return IRQ_HANDLED; ++} ++ ++static void csihw_reset_frame_count(void) ++{ ++ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3); ++} ++ ++static void csihw_reset(void) ++{ ++ csihw_reset_frame_count(); ++ __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1); ++ __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2); ++ __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3); ++} ++ ++/*! ++ * csi_init_interface ++ * Init csi interface ++ */ ++void csi_init_interface(void) ++{ ++ unsigned int val = 0; ++ unsigned int imag_para; ++ ++ val |= BIT_SOF_POL; ++ val |= BIT_REDGE; ++ val |= BIT_GCLK_MODE; ++ val |= BIT_HSYNC_POL; ++ val |= BIT_PACK_DIR; ++ val |= BIT_FCC; ++ val |= BIT_SWAP16_EN; ++ val |= 1 << SHIFT_MCLKDIV; ++ val |= BIT_MCLKEN; ++ __raw_writel(val, CSI_CSICR1); ++ ++ imag_para = (640 << 16) | 960; ++ __raw_writel(imag_para, CSI_CSIIMAG_PARA); ++ ++ val = 0x1010; ++ val |= BIT_DMA_REFLASH_RFF; ++ __raw_writel(val, CSI_CSICR3); ++} ++EXPORT_SYMBOL(csi_init_interface); ++ ++void csi_init_format(int fmt) ++{ ++ unsigned int val; ++ ++ val = __raw_readl(CSI_CSICR1); ++ if (fmt == V4L2_PIX_FMT_YUYV) { ++ val &= ~BIT_PACK_DIR; ++ val &= ~BIT_SWAP16_EN; ++ } else if (fmt == V4L2_PIX_FMT_UYVY) { ++ val |= BIT_PACK_DIR; ++ val |= BIT_SWAP16_EN; ++ } else ++ pr_warning("unsupported format, old format remains.\n"); ++ ++ __raw_writel(val, CSI_CSICR1); ++} ++EXPORT_SYMBOL(csi_init_format); ++ ++/*! ++ * csi_read_mclk_flag ++ * ++ * @return gcsi_mclk_source ++ */ ++int csi_read_mclk_flag(void) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(csi_read_mclk_flag); ++ ++void csi_start_callback(void *data) ++{ ++ cam_data *cam = (cam_data *) data; ++ ++ if (request_irq(irq_nr, csi_irq_handler, 0, "csi", cam) < 0) ++ pr_debug("CSI error: irq request fail\n"); ++ ++} ++EXPORT_SYMBOL(csi_start_callback); ++ ++void csi_stop_callback(void *data) ++{ ++ cam_data *cam = (cam_data *) data; ++ ++ free_irq(irq_nr, cam); ++} ++EXPORT_SYMBOL(csi_stop_callback); ++ ++void csi_enable_int(int arg) ++{ ++ unsigned long cr1 = __raw_readl(CSI_CSICR1); ++ ++ cr1 |= BIT_SOF_INTEN; ++ if (arg == 1) { ++ /* still capture needs DMA intterrupt */ ++ cr1 |= BIT_FB1_DMA_DONE_INTEN; ++ cr1 |= BIT_FB2_DMA_DONE_INTEN; ++ } ++ __raw_writel(cr1, CSI_CSICR1); ++} ++EXPORT_SYMBOL(csi_enable_int); ++ ++void csi_disable_int(void) ++{ ++ unsigned long cr1 = __raw_readl(CSI_CSICR1); ++ ++ cr1 &= ~BIT_SOF_INTEN; ++ cr1 &= ~BIT_FB1_DMA_DONE_INTEN; ++ cr1 &= ~BIT_FB2_DMA_DONE_INTEN; ++ __raw_writel(cr1, CSI_CSICR1); ++} ++EXPORT_SYMBOL(csi_disable_int); ++ ++void csi_set_16bit_imagpara(int width, int height) ++{ ++ int imag_para = 0; ++ unsigned long cr3 = __raw_readl(CSI_CSICR3); ++ ++ imag_para = (width << 16) | (height * 2); ++ __raw_writel(imag_para, CSI_CSIIMAG_PARA); ++ ++ /* reflash the embeded DMA controller */ ++ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3); ++} ++EXPORT_SYMBOL(csi_set_16bit_imagpara); ++ ++void csi_set_12bit_imagpara(int width, int height) ++{ ++ int imag_para = 0; ++ unsigned long cr3 = __raw_readl(CSI_CSICR3); ++ ++ imag_para = (width << 16) | (height * 3 / 2); ++ __raw_writel(imag_para, CSI_CSIIMAG_PARA); ++ ++ /* reflash the embeded DMA controller */ ++ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3); ++} ++EXPORT_SYMBOL(csi_set_12bit_imagpara); ++ ++void csi_dmareq_rff_enable(void) ++{ ++ unsigned long cr3 = __raw_readl(CSI_CSICR3); ++ ++ cr3 |= BIT_DMA_REQ_EN_RFF; ++ cr3 |= BIT_HRESP_ERR_EN; ++ __raw_writel(cr3, CSI_CSICR3); ++} ++EXPORT_SYMBOL(csi_dmareq_rff_enable); ++ ++void csi_dmareq_rff_disable(void) ++{ ++ unsigned long cr3 = __raw_readl(CSI_CSICR3); ++ ++ cr3 &= ~BIT_DMA_REQ_EN_RFF; ++ cr3 &= ~BIT_HRESP_ERR_EN; ++ __raw_writel(cr3, CSI_CSICR3); ++} ++EXPORT_SYMBOL(csi_dmareq_rff_disable); ++ ++static const struct of_device_id fsl_csi_dt_ids[] = { ++ { .compatible = "fsl,imx6sl-csi", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, fsl_csi_dt_ids); ++ ++static int csi_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "No csi irq found.\n"); ++ ret = -ENODEV; ++ goto err; ++ } ++ irq_nr = res->start; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "No csi base address found.\n"); ++ ret = -ENODEV; ++ goto err; ++ } ++ csi_regbase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); ++ if (!csi_regbase) { ++ dev_err(&pdev->dev, "ioremap failed with csi base\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ csihw_reset(); ++ csi_init_interface(); ++ csi_dmareq_rff_disable(); ++ ++err: ++ return ret; ++} ++ ++static int csi_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static struct platform_driver csi_driver = { ++ .driver = { ++ .name = "fsl_csi", ++ .of_match_table = of_match_ptr(fsl_csi_dt_ids), ++ }, ++ .probe = csi_probe, ++ .remove = csi_remove, ++}; ++ ++module_platform_driver(csi_driver); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("fsl CSI driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/fsl_csi.h linux-openelec/drivers/media/platform/mxc/capture/fsl_csi.h +--- linux-3.14.36/drivers/media/platform/mxc/capture/fsl_csi.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/fsl_csi.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,198 @@ ++/* ++ * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file fsl_csi.h ++ * ++ * @brief mx25 CMOS Sensor interface functions ++ * ++ * @ingroup CSI ++ */ ++ ++#ifndef MX25_CSI_H ++#define MX25_CSI_H ++ ++#include ++ ++/* reset values */ ++#define CSICR1_RESET_VAL 0x40000800 ++#define CSICR2_RESET_VAL 0x0 ++#define CSICR3_RESET_VAL 0x0 ++ ++/* csi control reg 1 */ ++#define BIT_SWAP16_EN (0x1 << 31) ++#define BIT_EXT_VSYNC (0x1 << 30) ++#define BIT_EOF_INT_EN (0x1 << 29) ++#define BIT_PRP_IF_EN (0x1 << 28) ++#define BIT_CCIR_MODE (0x1 << 27) ++#define BIT_COF_INT_EN (0x1 << 26) ++#define BIT_SF_OR_INTEN (0x1 << 25) ++#define BIT_RF_OR_INTEN (0x1 << 24) ++#define BIT_SFF_DMA_DONE_INTEN (0x1 << 22) ++#define BIT_STATFF_INTEN (0x1 << 21) ++#define BIT_FB2_DMA_DONE_INTEN (0x1 << 20) ++#define BIT_FB1_DMA_DONE_INTEN (0x1 << 19) ++#define BIT_RXFF_INTEN (0x1 << 18) ++#define BIT_SOF_POL (0x1 << 17) ++#define BIT_SOF_INTEN (0x1 << 16) ++#define BIT_MCLKDIV (0xF << 12) ++#define BIT_HSYNC_POL (0x1 << 11) ++#define BIT_CCIR_EN (0x1 << 10) ++#define BIT_MCLKEN (0x1 << 9) ++#define BIT_FCC (0x1 << 8) ++#define BIT_PACK_DIR (0x1 << 7) ++#define BIT_CLR_STATFIFO (0x1 << 6) ++#define BIT_CLR_RXFIFO (0x1 << 5) ++#define BIT_GCLK_MODE (0x1 << 4) ++#define BIT_INV_DATA (0x1 << 3) ++#define BIT_INV_PCLK (0x1 << 2) ++#define BIT_REDGE (0x1 << 1) ++#define BIT_PIXEL_BIT (0x1 << 0) ++ ++#define SHIFT_MCLKDIV 12 ++ ++/* control reg 3 */ ++#define BIT_FRMCNT (0xFFFF << 16) ++#define BIT_FRMCNT_RST (0x1 << 15) ++#define BIT_DMA_REFLASH_RFF (0x1 << 14) ++#define BIT_DMA_REFLASH_SFF (0x1 << 13) ++#define BIT_DMA_REQ_EN_RFF (0x1 << 12) ++#define BIT_DMA_REQ_EN_SFF (0x1 << 11) ++#define BIT_STATFF_LEVEL (0x7 << 8) ++#define BIT_HRESP_ERR_EN (0x1 << 7) ++#define BIT_RXFF_LEVEL (0x7 << 4) ++#define BIT_TWO_8BIT_SENSOR (0x1 << 3) ++#define BIT_ZERO_PACK_EN (0x1 << 2) ++#define BIT_ECC_INT_EN (0x1 << 1) ++#define BIT_ECC_AUTO_EN (0x1 << 0) ++ ++#define SHIFT_FRMCNT 16 ++ ++/* csi status reg */ ++#define BIT_SFF_OR_INT (0x1 << 25) ++#define BIT_RFF_OR_INT (0x1 << 24) ++#define BIT_DMA_TSF_DONE_SFF (0x1 << 22) ++#define BIT_STATFF_INT (0x1 << 21) ++#define BIT_DMA_TSF_DONE_FB2 (0x1 << 20) ++#define BIT_DMA_TSF_DONE_FB1 (0x1 << 19) ++#define BIT_RXFF_INT (0x1 << 18) ++#define BIT_EOF_INT (0x1 << 17) ++#define BIT_SOF_INT (0x1 << 16) ++#define BIT_F2_INT (0x1 << 15) ++#define BIT_F1_INT (0x1 << 14) ++#define BIT_COF_INT (0x1 << 13) ++#define BIT_HRESP_ERR_INT (0x1 << 7) ++#define BIT_ECC_INT (0x1 << 1) ++#define BIT_DRDY (0x1 << 0) ++ ++#define CSI_MCLK_VF 1 ++#define CSI_MCLK_ENC 2 ++#define CSI_MCLK_RAW 4 ++#define CSI_MCLK_I2C 8 ++#endif ++ ++extern void __iomem *csi_regbase; ++#define CSI_CSICR1 (csi_regbase) ++#define CSI_CSICR2 (csi_regbase + 0x4) ++#define CSI_CSICR3 (csi_regbase + 0x8) ++#define CSI_STATFIFO (csi_regbase + 0xC) ++#define CSI_CSIRXFIFO (csi_regbase + 0x10) ++#define CSI_CSIRXCNT (csi_regbase + 0x14) ++#define CSI_CSISR (csi_regbase + 0x18) ++ ++#define CSI_CSIDBG (csi_regbase + 0x1C) ++#define CSI_CSIDMASA_STATFIFO (csi_regbase + 0x20) ++#define CSI_CSIDMATS_STATFIFO (csi_regbase + 0x24) ++#define CSI_CSIDMASA_FB1 (csi_regbase + 0x28) ++#define CSI_CSIDMASA_FB2 (csi_regbase + 0x2C) ++#define CSI_CSIFBUF_PARA (csi_regbase + 0x30) ++#define CSI_CSIIMAG_PARA (csi_regbase + 0x34) ++ ++static inline void csi_clear_status(unsigned long status) ++{ ++ __raw_writel(status, CSI_CSISR); ++} ++ ++struct csi_signal_cfg_t { ++ unsigned data_width:3; ++ unsigned clk_mode:2; ++ unsigned ext_vsync:1; ++ unsigned Vsync_pol:1; ++ unsigned Hsync_pol:1; ++ unsigned pixclk_pol:1; ++ unsigned data_pol:1; ++ unsigned sens_clksrc:1; ++}; ++ ++struct csi_config_t { ++ /* control reg 1 */ ++ unsigned int swap16_en:1; ++ unsigned int ext_vsync:1; ++ unsigned int eof_int_en:1; ++ unsigned int prp_if_en:1; ++ unsigned int ccir_mode:1; ++ unsigned int cof_int_en:1; ++ unsigned int sf_or_inten:1; ++ unsigned int rf_or_inten:1; ++ unsigned int sff_dma_done_inten:1; ++ unsigned int statff_inten:1; ++ unsigned int fb2_dma_done_inten:1; ++ unsigned int fb1_dma_done_inten:1; ++ unsigned int rxff_inten:1; ++ unsigned int sof_pol:1; ++ unsigned int sof_inten:1; ++ unsigned int mclkdiv:4; ++ unsigned int hsync_pol:1; ++ unsigned int ccir_en:1; ++ unsigned int mclken:1; ++ unsigned int fcc:1; ++ unsigned int pack_dir:1; ++ unsigned int gclk_mode:1; ++ unsigned int inv_data:1; ++ unsigned int inv_pclk:1; ++ unsigned int redge:1; ++ unsigned int pixel_bit:1; ++ ++ /* control reg 3 */ ++ unsigned int frmcnt:16; ++ unsigned int frame_reset:1; ++ unsigned int dma_reflash_rff:1; ++ unsigned int dma_reflash_sff:1; ++ unsigned int dma_req_en_rff:1; ++ unsigned int dma_req_en_sff:1; ++ unsigned int statff_level:3; ++ unsigned int hresp_err_en:1; ++ unsigned int rxff_level:3; ++ unsigned int two_8bit_sensor:1; ++ unsigned int zero_pack_en:1; ++ unsigned int ecc_int_en:1; ++ unsigned int ecc_auto_en:1; ++ /* fifo counter */ ++ unsigned int rxcnt; ++}; ++ ++typedef void (*csi_irq_callback_t) (void *data, unsigned long status); ++ ++void csi_init_interface(void); ++void csi_init_format(int fmt); ++void csi_set_16bit_imagpara(int width, int height); ++void csi_set_12bit_imagpara(int width, int height); ++int csi_read_mclk_flag(void); ++void csi_start_callback(void *data); ++void csi_stop_callback(void *data); ++void csi_enable_int(int arg); ++void csi_disable_int(void); ++void csi_mclk_enable(void); ++void csi_mclk_disable(void); ++void csi_dmareq_rff_enable(void); ++void csi_dmareq_rff_disable(void); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c linux-openelec/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,546 @@ ++ ++/* ++ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_bg_overlay_sdc_bg.c ++ * ++ * @brief IPU Use case for PRP-VF back-ground ++ * ++ * @ingroup IPU ++ */ ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++static int csi_buffer_num; ++static u32 bpp, csi_mem_bufsize = 3; ++static u32 out_format; ++static struct ipu_soc *disp_ipu; ++static u32 offset; ++ ++static void csi_buf_work_func(struct work_struct *work) ++{ ++ int err = 0; ++ cam_data *cam = ++ container_of(work, struct _cam_data, csi_work_struct); ++ ++ struct ipu_task task; ++ memset(&task, 0, sizeof(task)); ++ ++ if (csi_buffer_num) ++ task.input.paddr = cam->vf_bufs[0]; ++ else ++ task.input.paddr = cam->vf_bufs[1]; ++ task.input.width = cam->crop_current.width; ++ task.input.height = cam->crop_current.height; ++ task.input.format = IPU_PIX_FMT_UYVY; ++ ++ task.output.paddr = offset; ++ task.output.width = cam->overlay_fb->var.xres; ++ task.output.height = cam->overlay_fb->var.yres; ++ task.output.format = out_format; ++ task.output.rotate = cam->rotation; ++ task.output.crop.pos.x = cam->win.w.left; ++ task.output.crop.pos.y = cam->win.w.top; ++ if (cam->win.w.width > 1024 || cam->win.w.height > 1024) { ++ task.output.crop.w = cam->overlay_fb->var.xres; ++ task.output.crop.h = cam->overlay_fb->var.yres; ++ } else { ++ task.output.crop.w = cam->win.w.width; ++ task.output.crop.h = cam->win.w.height; ++ } ++again: ++ err = ipu_check_task(&task); ++ if (err != IPU_CHECK_OK) { ++ if (err > IPU_CHECK_ERR_MIN) { ++ if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) { ++ task.input.crop.w -= 8; ++ goto again; ++ } ++ if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) { ++ task.input.crop.h -= 8; ++ goto again; ++ } ++ if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { ++ task.output.width -= 8; ++ task.output.crop.w = task.output.width; ++ goto again; ++ } ++ if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { ++ task.output.height -= 8; ++ task.output.crop.h = task.output.height; ++ goto again; ++ } ++ printk(KERN_ERR "check ipu taks fail\n"); ++ return; ++ } ++ printk(KERN_ERR "check ipu taks fail\n"); ++ return; ++ } ++ err = ipu_queue_task(&task); ++ if (err < 0) ++ printk(KERN_ERR "queue ipu task error\n"); ++} ++ ++static void get_disp_ipu(cam_data *cam) ++{ ++ if (cam->output > 2) ++ disp_ipu = ipu_get_soc(1); /* using DISP4 */ ++ else ++ disp_ipu = ipu_get_soc(0); ++} ++ ++ ++/*! ++ * csi ENC callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t csi_enc_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = (cam_data *) dev_id; ++ ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, csi_buffer_num); ++ schedule_work(&cam->csi_work_struct); ++ csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0; ++ return IRQ_HANDLED; ++} ++ ++static int csi_enc_setup(cam_data *cam) ++{ ++ ipu_channel_params_t params; ++ u32 pixel_fmt; ++ int err = 0, sensor_protocol = 0; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (!cam) { ++ printk(KERN_ERR "cam private is NULL\n"); ++ return -ENXIO; ++ } ++ ++ memset(¶ms, 0, sizeof(ipu_channel_params_t)); ++ params.csi_mem.csi = cam->csi; ++ ++ sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi); ++ switch (sensor_protocol) { ++ case IPU_CSI_CLK_MODE_GATED_CLK: ++ case IPU_CSI_CLK_MODE_NONGATED_CLK: ++ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: ++ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: ++ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: ++ params.csi_mem.interlaced = false; ++ break; ++ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: ++ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: ++ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: ++ params.csi_mem.interlaced = true; ++ break; ++ default: ++ printk(KERN_ERR "sensor protocol unsupported\n"); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) { ++ params.csi_mem.mipi_en = true; ++ params.csi_mem.mipi_vc = ++ mipi_csi2_get_virtual_channel(mipi_csi2_info); ++ params.csi_mem.mipi_id = ++ mipi_csi2_get_datatype(mipi_csi2_info); ++ ++ mipi_csi2_pixelclk_enable(mipi_csi2_info); ++ } else { ++ params.csi_mem.mipi_en = false; ++ params.csi_mem.mipi_vc = 0; ++ params.csi_mem.mipi_id = 0; ++ } ++ } else { ++ params.csi_mem.mipi_en = false; ++ params.csi_mem.mipi_vc = 0; ++ params.csi_mem.mipi_id = 0; ++ } ++ } ++#endif ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ } ++ csi_mem_bufsize = ++ cam->crop_current.width * cam->crop_current.height * 2; ++ cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize); ++ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[0], ++ (dma_addr_t *) & ++ cam->vf_bufs[0], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[0] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_2; ++ } ++ cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize); ++ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[1], ++ (dma_addr_t *) & ++ cam->vf_bufs[1], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[1] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_1; ++ } ++ pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); ++ ++ err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); ++ if (err != 0) { ++ printk(KERN_ERR "ipu_init_channel %d\n", err); ++ goto out_1; ++ } ++ ++ pixel_fmt = IPU_PIX_FMT_UYVY; ++ err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, ++ pixel_fmt, cam->crop_current.width, ++ cam->crop_current.height, ++ cam->crop_current.width, IPU_ROTATE_NONE, ++ cam->vf_bufs[0], cam->vf_bufs[1], 0, ++ cam->offset.u_offset, cam->offset.u_offset); ++ if (err != 0) { ++ printk(KERN_ERR "CSI_MEM output buffer\n"); ++ goto out_1; ++ } ++ err = ipu_enable_channel(cam->ipu, CSI_MEM); ++ if (err < 0) { ++ printk(KERN_ERR "ipu_enable_channel CSI_MEM\n"); ++ goto out_1; ++ } ++ ++ csi_buffer_num = 0; ++ ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0); ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 1); ++ return err; ++out_1: ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++out_2: ++ return err; ++} ++ ++/*! ++ * Enable encoder task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int csi_enc_enabling_tasks(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++ ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, ++ csi_enc_callback, 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error registering CSI0_OUT_EOF irq\n"); ++ return err; ++ } ++ ++ INIT_WORK(&cam->csi_work_struct, csi_buf_work_func); ++ ++ err = csi_enc_setup(cam); ++ if (err != 0) { ++ printk(KERN_ERR "csi_enc_setup %d\n", err); ++ goto out1; ++ } ++ ++ return err; ++out1: ++ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); ++ return err; ++} ++ ++/*! ++ * bg_overlay_start - start the overlay task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int bg_overlay_start(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++ if (!cam) { ++ printk(KERN_ERR "private is NULL\n"); ++ return -EIO; ++ } ++ ++ if (cam->overlay_active == true) { ++ pr_debug("already start.\n"); ++ return 0; ++ } ++ ++ get_disp_ipu(cam); ++ ++ out_format = cam->v4l2_fb.fmt.pixelformat; ++ if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) { ++ bpp = 3, csi_mem_bufsize = 3; ++ pr_info("BGR24\n"); ++ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) { ++ bpp = 2, csi_mem_bufsize = 2; ++ pr_info("RGB565\n"); ++ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) { ++ bpp = 4, csi_mem_bufsize = 4; ++ pr_info("BGR32\n"); ++ } else { ++ printk(KERN_ERR ++ "unsupported fix format from the framebuffer.\n"); ++ return -EINVAL; ++ } ++ ++ offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top + ++ csi_mem_bufsize * cam->win.w.left; ++ ++ if (cam->v4l2_fb.base == 0) ++ printk(KERN_ERR "invalid frame buffer address.\n"); ++ else ++ offset += (u32) cam->v4l2_fb.base; ++ ++ csi_mem_bufsize = cam->win.w.width * cam->win.w.height ++ * csi_mem_bufsize; ++ ++ err = csi_enc_enabling_tasks(cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error csi enc enable fail\n"); ++ return err; ++ } ++ ++ cam->overlay_active = true; ++ return err; ++} ++ ++/*! ++ * bg_overlay_stop - stop the overlay task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int bg_overlay_stop(void *private) ++{ ++ int err = 0; ++ cam_data *cam = (cam_data *) private; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (cam->overlay_active == false) ++ return 0; ++ ++ err = ipu_disable_channel(cam->ipu, CSI_MEM, true); ++ ++ ipu_uninit_channel(cam->ipu, CSI_MEM); ++ ++ csi_buffer_num = 0; ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) ++ mipi_csi2_pixelclk_disable(mipi_csi2_info); ++ } ++ } ++#endif ++ ++ flush_work(&cam->csi_work_struct); ++ cancel_work_sync(&cam->csi_work_struct); ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++ if (cam->rot_vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->rot_vf_buf_size[0], ++ cam->rot_vf_bufs_vaddr[0], ++ cam->rot_vf_bufs[0]); ++ cam->rot_vf_bufs_vaddr[0] = NULL; ++ cam->rot_vf_bufs[0] = 0; ++ } ++ if (cam->rot_vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->rot_vf_buf_size[1], ++ cam->rot_vf_bufs_vaddr[1], ++ cam->rot_vf_bufs[1]); ++ cam->rot_vf_bufs_vaddr[1] = NULL; ++ cam->rot_vf_bufs[1] = 0; ++ } ++ ++ cam->overlay_active = false; ++ return err; ++} ++ ++/*! ++ * Enable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int bg_overlay_enable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ return ipu_enable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * Disable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int bg_overlay_disable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ /* free csi eof irq firstly. ++ * when disable csi, wait for idmac eof. ++ * it requests eof irq again */ ++ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); ++ ++ return ipu_disable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * function to select bg as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return status ++ */ ++int bg_overlay_sdc_select(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ if (cam) { ++ cam->vf_start_sdc = bg_overlay_start; ++ cam->vf_stop_sdc = bg_overlay_stop; ++ cam->vf_enable_csi = bg_overlay_enable_csi; ++ cam->vf_disable_csi = bg_overlay_disable_csi; ++ cam->overlay_active = false; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(bg_overlay_sdc_select); ++ ++/*! ++ * function to de-select bg as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return status ++ */ ++int bg_overlay_sdc_deselect(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ if (cam) { ++ cam->vf_start_sdc = NULL; ++ cam->vf_stop_sdc = NULL; ++ cam->vf_enable_csi = NULL; ++ cam->vf_disable_csi = NULL; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(bg_overlay_sdc_deselect); ++ ++/*! ++ * Init background overlay task. ++ * ++ * @return Error code indicating success or failure ++ */ ++__init int bg_overlay_sdc_init(void) ++{ ++ return 0; ++} ++ ++/*! ++ * Deinit background overlay task. ++ * ++ * @return Error code indicating success or failure ++ */ ++void __exit bg_overlay_sdc_exit(void) ++{ ++} ++ ++module_init(bg_overlay_sdc_init); ++module_exit(bg_overlay_sdc_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_csi_enc.c linux-openelec/drivers/media/platform/mxc/capture/ipu_csi_enc.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_csi_enc.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_csi_enc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,418 @@ ++/* ++ * Copyright 2009-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_csi_enc.c ++ * ++ * @brief CSI Use case for video capture ++ * ++ * @ingroup IPU ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++#ifdef CAMERA_DBG ++ #define CAMERA_TRACE(x) (printk)x ++#else ++ #define CAMERA_TRACE(x) ++#endif ++ ++/* ++ * Function definitions ++ */ ++ ++/*! ++ * csi ENC callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t csi_enc_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = (cam_data *) dev_id; ++ ++ if (cam->enc_callback == NULL) ++ return IRQ_HANDLED; ++ ++ cam->enc_callback(irq, dev_id); ++ return IRQ_HANDLED; ++} ++ ++/*! ++ * CSI ENC enable channel setup function ++ * ++ * @param cam struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int csi_enc_setup(cam_data *cam) ++{ ++ ipu_channel_params_t params; ++ u32 pixel_fmt; ++ int err = 0, sensor_protocol = 0; ++ dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ CAMERA_TRACE("In csi_enc_setup\n"); ++ if (!cam) { ++ printk(KERN_ERR "cam private is NULL\n"); ++ return -ENXIO; ++ } ++ ++ memset(¶ms, 0, sizeof(ipu_channel_params_t)); ++ params.csi_mem.csi = cam->csi; ++ ++ sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi); ++ switch (sensor_protocol) { ++ case IPU_CSI_CLK_MODE_GATED_CLK: ++ case IPU_CSI_CLK_MODE_NONGATED_CLK: ++ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: ++ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: ++ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: ++ params.csi_mem.interlaced = false; ++ break; ++ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: ++ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: ++ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: ++ params.csi_mem.interlaced = true; ++ break; ++ default: ++ printk(KERN_ERR "sensor protocol unsupported\n"); ++ return -EINVAL; ++ } ++ ++ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ++ pixel_fmt = IPU_PIX_FMT_YUV420P; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) ++ pixel_fmt = IPU_PIX_FMT_YVU420P; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) ++ pixel_fmt = IPU_PIX_FMT_YUV422P; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) ++ pixel_fmt = IPU_PIX_FMT_UYVY; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ++ pixel_fmt = IPU_PIX_FMT_YUYV; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) ++ pixel_fmt = IPU_PIX_FMT_NV12; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) ++ pixel_fmt = IPU_PIX_FMT_BGR24; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) ++ pixel_fmt = IPU_PIX_FMT_RGB24; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) ++ pixel_fmt = IPU_PIX_FMT_RGB565; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) ++ pixel_fmt = IPU_PIX_FMT_BGR32; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) ++ pixel_fmt = IPU_PIX_FMT_RGB32; ++ else { ++ printk(KERN_ERR "format not supported\n"); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) { ++ params.csi_mem.mipi_en = true; ++ params.csi_mem.mipi_vc = ++ mipi_csi2_get_virtual_channel(mipi_csi2_info); ++ params.csi_mem.mipi_id = ++ mipi_csi2_get_datatype(mipi_csi2_info); ++ ++ mipi_csi2_pixelclk_enable(mipi_csi2_info); ++ } else { ++ params.csi_mem.mipi_en = false; ++ params.csi_mem.mipi_vc = 0; ++ params.csi_mem.mipi_id = 0; ++ } ++ } else { ++ params.csi_mem.mipi_en = false; ++ params.csi_mem.mipi_vc = 0; ++ params.csi_mem.mipi_id = 0; ++ } ++ } ++#endif ++ ++ err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); ++ if (err != 0) { ++ printk(KERN_ERR "ipu_init_channel %d\n", err); ++ return err; ++ } ++ ++ err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, ++ pixel_fmt, cam->v2f.fmt.pix.width, ++ cam->v2f.fmt.pix.height, ++ cam->v2f.fmt.pix.bytesperline, ++ IPU_ROTATE_NONE, ++ dummy, dummy, 0, ++ cam->offset.u_offset, ++ cam->offset.v_offset); ++ if (err != 0) { ++ printk(KERN_ERR "CSI_MEM output buffer\n"); ++ return err; ++ } ++ err = ipu_enable_channel(cam->ipu, CSI_MEM); ++ if (err < 0) { ++ printk(KERN_ERR "ipu_enable_channel CSI_MEM\n"); ++ return err; ++ } ++ ++ return err; ++} ++ ++/*! ++ * function to update physical buffer address for encorder IDMA channel ++ * ++ * @param eba physical buffer address for encorder IDMA channel ++ * @param buffer_num int buffer 0 or buffer 1 ++ * ++ * @return status ++ */ ++static int csi_enc_eba_update(struct ipu_soc *ipu, dma_addr_t eba, ++ int *buffer_num) ++{ ++ int err = 0; ++ ++ pr_debug("eba %x\n", eba); ++ err = ipu_update_channel_buffer(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, ++ *buffer_num, eba); ++ if (err != 0) { ++ ipu_clear_buffer_ready(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, ++ *buffer_num); ++ ++ err = ipu_update_channel_buffer(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, ++ *buffer_num, eba); ++ if (err != 0) { ++ pr_err("ERROR: v4l2 capture: fail to update " ++ "buf%d\n", *buffer_num); ++ return err; ++ } ++ } ++ ++ ipu_select_buffer(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, *buffer_num); ++ ++ *buffer_num = (*buffer_num == 0) ? 1 : 0; ++ ++ return 0; ++} ++ ++/*! ++ * Enable encoder task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int csi_enc_enabling_tasks(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n"); ++ ++ cam->dummy_frame.vaddress = dma_alloc_coherent(0, ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), ++ &cam->dummy_frame.paddress, ++ GFP_DMA | GFP_KERNEL); ++ if (cam->dummy_frame.vaddress == 0) { ++ pr_err("ERROR: v4l2 capture: Allocate dummy frame " ++ "failed.\n"); ++ return -ENOBUFS; ++ } ++ cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; ++ cam->dummy_frame.buffer.length = ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); ++ cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; ++ ++ ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, ++ csi_enc_callback, 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error registering rot irq\n"); ++ return err; ++ } ++ ++ err = csi_enc_setup(cam); ++ if (err != 0) { ++ printk(KERN_ERR "csi_enc_setup %d\n", err); ++ return err; ++ } ++ ++ return err; ++} ++ ++/*! ++ * Disable encoder task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return int ++ */ ++static int csi_enc_disabling_tasks(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ err = ipu_disable_channel(cam->ipu, CSI_MEM, true); ++ ++ ipu_uninit_channel(cam->ipu, CSI_MEM); ++ ++ if (cam->dummy_frame.vaddress != 0) { ++ dma_free_coherent(0, cam->dummy_frame.buffer.length, ++ cam->dummy_frame.vaddress, ++ cam->dummy_frame.paddress); ++ cam->dummy_frame.vaddress = 0; ++ } ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) ++ mipi_csi2_pixelclk_disable(mipi_csi2_info); ++ } ++ } ++#endif ++ ++ return err; ++} ++ ++/*! ++ * Enable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int csi_enc_enable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ return ipu_enable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * Disable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int csi_enc_disable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ /* free csi eof irq firstly. ++ * when disable csi, wait for idmac eof. ++ * it requests eof irq again */ ++ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); ++ ++ return ipu_disable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * function to select CSI ENC as the working path ++ * ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return int ++ */ ++int csi_enc_select(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++ if (cam) { ++ cam->enc_update_eba = csi_enc_eba_update; ++ cam->enc_enable = csi_enc_enabling_tasks; ++ cam->enc_disable = csi_enc_disabling_tasks; ++ cam->enc_enable_csi = csi_enc_enable_csi; ++ cam->enc_disable_csi = csi_enc_disable_csi; ++ } else { ++ err = -EIO; ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL(csi_enc_select); ++ ++/*! ++ * function to de-select CSI ENC as the working path ++ * ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return int ++ */ ++int csi_enc_deselect(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++ if (cam) { ++ cam->enc_update_eba = NULL; ++ cam->enc_enable = NULL; ++ cam->enc_disable = NULL; ++ cam->enc_enable_csi = NULL; ++ cam->enc_disable_csi = NULL; ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL(csi_enc_deselect); ++ ++/*! ++ * Init the Encorder channels ++ * ++ * @return Error code indicating success or failure ++ */ ++__init int csi_enc_init(void) ++{ ++ return 0; ++} ++ ++/*! ++ * Deinit the Encorder channels ++ * ++ */ ++void __exit csi_enc_exit(void) ++{ ++} ++ ++module_init(csi_enc_init); ++module_exit(csi_enc_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("CSI ENC Driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c linux-openelec/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,634 @@ ++/* ++ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++/* * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_foreground_sdc.c ++ * ++ * @brief IPU Use case for PRP-VF ++ * ++ * @ingroup IPU ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++#ifdef CAMERA_DBG ++ #define CAMERA_TRACE(x) (printk)x ++#else ++ #define CAMERA_TRACE(x) ++#endif ++ ++static int csi_buffer_num, buffer_num; ++static u32 csi_mem_bufsize; ++static struct ipu_soc *disp_ipu; ++static struct fb_info *fbi; ++static struct fb_var_screeninfo fbvar; ++static u32 vf_out_format; ++static void csi_buf_work_func(struct work_struct *work) ++{ ++ int err = 0; ++ cam_data *cam = ++ container_of(work, struct _cam_data, csi_work_struct); ++ ++ struct ipu_task task; ++ memset(&task, 0, sizeof(task)); ++ ++ if (csi_buffer_num) ++ task.input.paddr = cam->vf_bufs[0]; ++ else ++ task.input.paddr = cam->vf_bufs[1]; ++ task.input.width = cam->crop_current.width; ++ task.input.height = cam->crop_current.height; ++ task.input.format = IPU_PIX_FMT_NV12; ++ ++ if (buffer_num == 0) ++ task.output.paddr = fbi->fix.smem_start + ++ (fbi->fix.line_length * fbvar.yres); ++ else ++ task.output.paddr = fbi->fix.smem_start; ++ task.output.width = cam->win.w.width; ++ task.output.height = cam->win.w.height; ++ task.output.format = vf_out_format; ++ task.output.rotate = cam->rotation; ++again: ++ err = ipu_check_task(&task); ++ if (err != IPU_CHECK_OK) { ++ if (err > IPU_CHECK_ERR_MIN) { ++ if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) { ++ task.input.crop.w -= 8; ++ goto again; ++ } ++ if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) { ++ task.input.crop.h -= 8; ++ goto again; ++ } ++ if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { ++ task.output.width -= 8; ++ task.output.crop.w = task.output.width; ++ goto again; ++ } ++ if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { ++ task.output.height -= 8; ++ task.output.crop.h = task.output.height; ++ goto again; ++ } ++ printk(KERN_ERR "check ipu taks fail\n"); ++ return; ++ } ++ printk(KERN_ERR "check ipu taks fail\n"); ++ return; ++ } ++ err = ipu_queue_task(&task); ++ if (err < 0) ++ printk(KERN_ERR "queue ipu task error\n"); ++ ipu_select_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, buffer_num); ++ buffer_num = (buffer_num == 0) ? 1 : 0; ++} ++ ++static void get_disp_ipu(cam_data *cam) ++{ ++ if (cam->output > 2) ++ disp_ipu = ipu_get_soc(1); /* using DISP4 */ ++ else ++ disp_ipu = ipu_get_soc(0); ++} ++ ++/*! ++ * csi ENC callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t csi_enc_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = (cam_data *) dev_id; ++ ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, csi_buffer_num); ++ if ((cam->crop_current.width != cam->win.w.width) || ++ (cam->crop_current.height != cam->win.w.height) || ++ (vf_out_format != IPU_PIX_FMT_NV12) || ++ (cam->rotation >= IPU_ROTATE_VERT_FLIP)) ++ schedule_work(&cam->csi_work_struct); ++ csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0; ++ return IRQ_HANDLED; ++} ++ ++static int csi_enc_setup(cam_data *cam) ++{ ++ ipu_channel_params_t params; ++ int err = 0, sensor_protocol = 0; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ CAMERA_TRACE("In csi_enc_setup\n"); ++ if (!cam) { ++ printk(KERN_ERR "cam private is NULL\n"); ++ return -ENXIO; ++ } ++ ++ memset(¶ms, 0, sizeof(ipu_channel_params_t)); ++ params.csi_mem.csi = cam->csi; ++ ++ sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi); ++ switch (sensor_protocol) { ++ case IPU_CSI_CLK_MODE_GATED_CLK: ++ case IPU_CSI_CLK_MODE_NONGATED_CLK: ++ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: ++ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: ++ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: ++ params.csi_mem.interlaced = false; ++ break; ++ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: ++ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: ++ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: ++ params.csi_mem.interlaced = true; ++ break; ++ default: ++ printk(KERN_ERR "sensor protocol unsupported\n"); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) { ++ params.csi_mem.mipi_en = true; ++ params.csi_mem.mipi_vc = ++ mipi_csi2_get_virtual_channel(mipi_csi2_info); ++ params.csi_mem.mipi_id = ++ mipi_csi2_get_datatype(mipi_csi2_info); ++ ++ mipi_csi2_pixelclk_enable(mipi_csi2_info); ++ } else { ++ params.csi_mem.mipi_en = false; ++ params.csi_mem.mipi_vc = 0; ++ params.csi_mem.mipi_id = 0; ++ } ++ } else { ++ params.csi_mem.mipi_en = false; ++ params.csi_mem.mipi_vc = 0; ++ params.csi_mem.mipi_id = 0; ++ } ++ } ++#endif ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ } ++ csi_mem_bufsize = cam->crop_current.width * ++ cam->crop_current.height * 3/2; ++ cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize); ++ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[0], ++ (dma_addr_t *) & ++ cam->vf_bufs[0], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[0] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_2; ++ } ++ cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize); ++ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[1], ++ (dma_addr_t *) & ++ cam->vf_bufs[1], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[1] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_1; ++ } ++ pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); ++ ++ err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); ++ if (err != 0) { ++ printk(KERN_ERR "ipu_init_channel %d\n", err); ++ goto out_1; ++ } ++ ++ if ((cam->crop_current.width == cam->win.w.width) && ++ (cam->crop_current.height == cam->win.w.height) && ++ (vf_out_format == IPU_PIX_FMT_NV12) && ++ (cam->rotation < IPU_ROTATE_VERT_FLIP)) { ++ err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, ++ IPU_OUTPUT_BUFFER, ++ IPU_PIX_FMT_NV12, ++ cam->crop_current.width, ++ cam->crop_current.height, ++ cam->crop_current.width, IPU_ROTATE_NONE, ++ fbi->fix.smem_start + ++ (fbi->fix.line_length * fbvar.yres), ++ fbi->fix.smem_start, 0, ++ cam->offset.u_offset, cam->offset.u_offset); ++ } else { ++ err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, ++ IPU_OUTPUT_BUFFER, ++ IPU_PIX_FMT_NV12, ++ cam->crop_current.width, ++ cam->crop_current.height, ++ cam->crop_current.width, IPU_ROTATE_NONE, ++ cam->vf_bufs[0], cam->vf_bufs[1], 0, ++ cam->offset.u_offset, cam->offset.u_offset); ++ } ++ if (err != 0) { ++ printk(KERN_ERR "CSI_MEM output buffer\n"); ++ goto out_1; ++ } ++ err = ipu_enable_channel(cam->ipu, CSI_MEM); ++ if (err < 0) { ++ printk(KERN_ERR "ipu_enable_channel CSI_MEM\n"); ++ goto out_1; ++ } ++ ++ csi_buffer_num = 0; ++ ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0); ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 1); ++ return err; ++out_1: ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++out_2: ++ return err; ++} ++ ++/*! ++ * Enable encoder task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int csi_enc_enabling_tasks(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n"); ++ ++ ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, ++ csi_enc_callback, 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error registering CSI0_OUT_EOF irq\n"); ++ return err; ++ } ++ ++ INIT_WORK(&cam->csi_work_struct, csi_buf_work_func); ++ ++ err = csi_enc_setup(cam); ++ if (err != 0) { ++ printk(KERN_ERR "csi_enc_setup %d\n", err); ++ goto out1; ++ } ++ ++ return err; ++out1: ++ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); ++ return err; ++} ++ ++/* ++ * Function definitions ++ */ ++ ++/*! ++ * foreground_start - start the vf task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int foreground_start(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0, i = 0, screen_size; ++ char *base; ++ ++ if (!cam) { ++ printk(KERN_ERR "private is NULL\n"); ++ return -EIO; ++ } ++ ++ if (cam->overlay_active == true) { ++ pr_debug("already started.\n"); ++ return 0; ++ } ++ ++ get_disp_ipu(cam); ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ char *idstr = registered_fb[i]->fix.id; ++ if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || ++ ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { ++ fbi = registered_fb[i]; ++ break; ++ } ++ } ++ ++ if (fbi == NULL) { ++ printk(KERN_ERR "DISP FG fb not found\n"); ++ return -EPERM; ++ } ++ ++ fbvar = fbi->var; ++ ++ /* Store the overlay frame buffer's original std */ ++ cam->fb_origin_std = fbvar.nonstd; ++ ++ if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) { ++ /* Use DP to do CSC so that we can get better performance */ ++ vf_out_format = IPU_PIX_FMT_NV12; ++ fbvar.nonstd = vf_out_format; ++ } else { ++ vf_out_format = IPU_PIX_FMT_RGB565; ++ fbvar.nonstd = 0; ++ } ++ ++ fbvar.bits_per_pixel = 16; ++ fbvar.xres = fbvar.xres_virtual = cam->win.w.width; ++ fbvar.yres = cam->win.w.height; ++ fbvar.yres_virtual = cam->win.w.height * 2; ++ fbvar.yoffset = 0; ++ fbvar.vmode &= ~FB_VMODE_YWRAP; ++ fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG; ++ fbvar.activate |= FB_ACTIVATE_FORCE; ++ fb_set_var(fbi, &fbvar); ++ ++ ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left, ++ cam->win.w.top); ++ ++ /* Fill black color for framebuffer */ ++ base = (char *) fbi->screen_base; ++ screen_size = fbi->var.xres * fbi->var.yres; ++ if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) { ++ memset(base, 0, screen_size); ++ base += screen_size; ++ for (i = 0; i < screen_size / 2; i++, base++) ++ *base = 0x80; ++ } else { ++ for (i = 0; i < screen_size * 2; i++, base++) ++ *base = 0x00; ++ } ++ ++ console_lock(); ++ fb_blank(fbi, FB_BLANK_UNBLANK); ++ console_unlock(); ++ ++ /* correct display ch buffer address */ ++ ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, ++ 0, fbi->fix.smem_start + ++ (fbi->fix.line_length * fbvar.yres)); ++ ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, ++ 1, fbi->fix.smem_start); ++ ++ err = csi_enc_enabling_tasks(cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error csi enc enable fail\n"); ++ return err; ++ } ++ ++ cam->overlay_active = true; ++ return err; ++ ++} ++ ++/*! ++ * foreground_stop - stop the vf task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int foreground_stop(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0, i = 0; ++ struct fb_info *fbi = NULL; ++ struct fb_var_screeninfo fbvar; ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (cam->overlay_active == false) ++ return 0; ++ ++ err = ipu_disable_channel(cam->ipu, CSI_MEM, true); ++ ++ ipu_uninit_channel(cam->ipu, CSI_MEM); ++ ++ csi_buffer_num = 0; ++ buffer_num = 0; ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ char *idstr = registered_fb[i]->fix.id; ++ if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || ++ ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { ++ fbi = registered_fb[i]; ++ break; ++ } ++ } ++ ++ if (fbi == NULL) { ++ printk(KERN_ERR "DISP FG fb not found\n"); ++ return -EPERM; ++ } ++ ++ console_lock(); ++ fb_blank(fbi, FB_BLANK_POWERDOWN); ++ console_unlock(); ++ ++ /* Set the overlay frame buffer std to what it is used to be */ ++ fbvar = fbi->var; ++ fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG; ++ fbvar.nonstd = cam->fb_origin_std; ++ fbvar.activate |= FB_ACTIVATE_FORCE; ++ fb_set_var(fbi, &fbvar); ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) ++ mipi_csi2_pixelclk_disable(mipi_csi2_info); ++ } ++ } ++#endif ++ ++ flush_work(&cam->csi_work_struct); ++ cancel_work_sync(&cam->csi_work_struct); ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++ ++ cam->overlay_active = false; ++ return err; ++} ++ ++/*! ++ * Enable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int foreground_enable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ return ipu_enable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * Disable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int foreground_disable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ /* free csi eof irq firstly. ++ * when disable csi, wait for idmac eof. ++ * it requests eof irq again */ ++ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); ++ ++ return ipu_disable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * function to select foreground as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return status ++ */ ++int foreground_sdc_select(void *private) ++{ ++ cam_data *cam; ++ int err = 0; ++ if (private) { ++ cam = (cam_data *) private; ++ cam->vf_start_sdc = foreground_start; ++ cam->vf_stop_sdc = foreground_stop; ++ cam->vf_enable_csi = foreground_enable_csi; ++ cam->vf_disable_csi = foreground_disable_csi; ++ cam->overlay_active = false; ++ } else ++ err = -EIO; ++ ++ return err; ++} ++EXPORT_SYMBOL(foreground_sdc_select); ++ ++/*! ++ * function to de-select foreground as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return int ++ */ ++int foreground_sdc_deselect(void *private) ++{ ++ cam_data *cam; ++ ++ if (private) { ++ cam = (cam_data *) private; ++ cam->vf_start_sdc = NULL; ++ cam->vf_stop_sdc = NULL; ++ cam->vf_enable_csi = NULL; ++ cam->vf_disable_csi = NULL; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(foreground_sdc_deselect); ++ ++/*! ++ * Init viewfinder task. ++ * ++ * @return Error code indicating success or failure ++ */ ++__init int foreground_sdc_init(void) ++{ ++ return 0; ++} ++ ++/*! ++ * Deinit viewfinder task. ++ * ++ * @return Error code indicating success or failure ++ */ ++void __exit foreground_sdc_exit(void) ++{ ++} ++ ++module_init(foreground_sdc_init); ++module_exit(foreground_sdc_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("IPU PRP VF SDC Driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_enc.c linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_enc.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_enc.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_enc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,595 @@ ++/* ++ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_prp_enc.c ++ * ++ * @brief IPU Use case for PRP-ENC ++ * ++ * @ingroup IPU ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++#ifdef CAMERA_DBG ++ #define CAMERA_TRACE(x) (printk)x ++#else ++ #define CAMERA_TRACE(x) ++#endif ++ ++static ipu_rotate_mode_t grotation = IPU_ROTATE_NONE; ++ ++/* ++ * Function definitions ++ */ ++ ++/*! ++ * IPU ENC callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t prp_enc_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = (cam_data *) dev_id; ++ ++ if (cam->enc_callback == NULL) ++ return IRQ_HANDLED; ++ ++ cam->enc_callback(irq, dev_id); ++ ++ return IRQ_HANDLED; ++} ++ ++/*! ++ * PrpENC enable channel setup function ++ * ++ * @param cam struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_enc_setup(cam_data *cam) ++{ ++ ipu_channel_params_t enc; ++ int err = 0; ++ dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ CAMERA_TRACE("In prp_enc_setup\n"); ++ if (!cam) { ++ printk(KERN_ERR "cam private is NULL\n"); ++ return -ENXIO; ++ } ++ memset(&enc, 0, sizeof(ipu_channel_params_t)); ++ ++ ipu_csi_get_window_size(cam->ipu, &enc.csi_prp_enc_mem.in_width, ++ &enc.csi_prp_enc_mem.in_height, cam->csi); ++ ++ enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY; ++ enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width; ++ enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height; ++ enc.csi_prp_enc_mem.csi = cam->csi; ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) { ++ enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height; ++ enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width; ++ } ++ ++ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P; ++ pr_info("YUV420\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YVU420P; ++ pr_info("YVU420\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P; ++ pr_info("YUV422P\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUYV; ++ pr_info("YUYV\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_UYVY; ++ pr_info("UYVY\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12; ++ pr_info("NV12\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24; ++ pr_info("BGR24\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24; ++ pr_info("RGB24\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565; ++ pr_info("RGB565\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32; ++ pr_info("BGR32\n"); ++ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) { ++ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32; ++ pr_info("RGB32\n"); ++ } else { ++ printk(KERN_ERR "format not supported\n"); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) { ++ enc.csi_prp_enc_mem.mipi_en = true; ++ enc.csi_prp_enc_mem.mipi_vc = ++ mipi_csi2_get_virtual_channel(mipi_csi2_info); ++ enc.csi_prp_enc_mem.mipi_id = ++ mipi_csi2_get_datatype(mipi_csi2_info); ++ ++ mipi_csi2_pixelclk_enable(mipi_csi2_info); ++ } else { ++ enc.csi_prp_enc_mem.mipi_en = false; ++ enc.csi_prp_enc_mem.mipi_vc = 0; ++ enc.csi_prp_enc_mem.mipi_id = 0; ++ } ++ } else { ++ enc.csi_prp_enc_mem.mipi_en = false; ++ enc.csi_prp_enc_mem.mipi_vc = 0; ++ enc.csi_prp_enc_mem.mipi_id = 0; ++ } ++ } ++#endif ++ ++ err = ipu_init_channel(cam->ipu, CSI_PRP_ENC_MEM, &enc); ++ if (err != 0) { ++ printk(KERN_ERR "ipu_init_channel %d\n", err); ++ return err; ++ } ++ ++ grotation = cam->rotation; ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) { ++ if (cam->rot_enc_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->rot_enc_buf_size[0], ++ cam->rot_enc_bufs_vaddr[0], ++ cam->rot_enc_bufs[0]); ++ } ++ if (cam->rot_enc_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->rot_enc_buf_size[1], ++ cam->rot_enc_bufs_vaddr[1], ++ cam->rot_enc_bufs[1]); ++ } ++ cam->rot_enc_buf_size[0] = ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); ++ cam->rot_enc_bufs_vaddr[0] = ++ (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[0], ++ &cam->rot_enc_bufs[0], ++ GFP_DMA | GFP_KERNEL); ++ if (!cam->rot_enc_bufs_vaddr[0]) { ++ printk(KERN_ERR "alloc enc_bufs0\n"); ++ return -ENOMEM; ++ } ++ cam->rot_enc_buf_size[1] = ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); ++ cam->rot_enc_bufs_vaddr[1] = ++ (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[1], ++ &cam->rot_enc_bufs[1], ++ GFP_DMA | GFP_KERNEL); ++ if (!cam->rot_enc_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->rot_enc_buf_size[0], ++ cam->rot_enc_bufs_vaddr[0], ++ cam->rot_enc_bufs[0]); ++ cam->rot_enc_bufs_vaddr[0] = NULL; ++ cam->rot_enc_bufs[0] = 0; ++ printk(KERN_ERR "alloc enc_bufs1\n"); ++ return -ENOMEM; ++ } ++ ++ err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM, ++ IPU_OUTPUT_BUFFER, ++ enc.csi_prp_enc_mem.out_pixel_fmt, ++ enc.csi_prp_enc_mem.out_width, ++ enc.csi_prp_enc_mem.out_height, ++ enc.csi_prp_enc_mem.out_width, ++ IPU_ROTATE_NONE, ++ cam->rot_enc_bufs[0], ++ cam->rot_enc_bufs[1], 0, 0, 0); ++ if (err != 0) { ++ printk(KERN_ERR "CSI_PRP_ENC_MEM err\n"); ++ return err; ++ } ++ ++ err = ipu_init_channel(cam->ipu, MEM_ROT_ENC_MEM, NULL); ++ if (err != 0) { ++ printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n"); ++ return err; ++ } ++ ++ err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM, ++ IPU_INPUT_BUFFER, ++ enc.csi_prp_enc_mem.out_pixel_fmt, ++ enc.csi_prp_enc_mem.out_width, ++ enc.csi_prp_enc_mem.out_height, ++ enc.csi_prp_enc_mem.out_width, ++ cam->rotation, ++ cam->rot_enc_bufs[0], ++ cam->rot_enc_bufs[1], 0, 0, 0); ++ if (err != 0) { ++ printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n"); ++ return err; ++ } ++ ++ err = ++ ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM, ++ IPU_OUTPUT_BUFFER, ++ enc.csi_prp_enc_mem.out_pixel_fmt, ++ enc.csi_prp_enc_mem.out_height, ++ enc.csi_prp_enc_mem.out_width, ++ cam->v2f.fmt.pix.bytesperline / ++ bytes_per_pixel(enc.csi_prp_enc_mem. ++ out_pixel_fmt), ++ IPU_ROTATE_NONE, ++ dummy, dummy, 0, ++ cam->offset.u_offset, ++ cam->offset.v_offset); ++ if (err != 0) { ++ printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n"); ++ return err; ++ } ++ ++ err = ipu_link_channels(cam->ipu, ++ CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM); ++ if (err < 0) { ++ printk(KERN_ERR ++ "link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n"); ++ return err; ++ } ++ ++ err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM); ++ if (err < 0) { ++ printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n"); ++ return err; ++ } ++ err = ipu_enable_channel(cam->ipu, MEM_ROT_ENC_MEM); ++ if (err < 0) { ++ printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n"); ++ return err; ++ } ++ ++ ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM, ++ IPU_OUTPUT_BUFFER, 0); ++ ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM, ++ IPU_OUTPUT_BUFFER, 1); ++ } else { ++ err = ++ ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM, ++ IPU_OUTPUT_BUFFER, ++ enc.csi_prp_enc_mem.out_pixel_fmt, ++ enc.csi_prp_enc_mem.out_width, ++ enc.csi_prp_enc_mem.out_height, ++ cam->v2f.fmt.pix.bytesperline / ++ bytes_per_pixel(enc.csi_prp_enc_mem. ++ out_pixel_fmt), ++ cam->rotation, ++ dummy, dummy, 0, ++ cam->offset.u_offset, ++ cam->offset.v_offset); ++ if (err != 0) { ++ printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n"); ++ return err; ++ } ++ err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM); ++ if (err < 0) { ++ printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n"); ++ return err; ++ } ++ } ++ ++ return err; ++} ++ ++/*! ++ * function to update physical buffer address for encorder IDMA channel ++ * ++ * @param eba physical buffer address for encorder IDMA channel ++ * @param buffer_num int buffer 0 or buffer 1 ++ * ++ * @return status ++ */ ++static int prp_enc_eba_update(struct ipu_soc *ipu, dma_addr_t eba, ++ int *buffer_num) ++{ ++ int err = 0; ++ ++ pr_debug("eba %x\n", eba); ++ if (grotation >= IPU_ROTATE_90_RIGHT) { ++ err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM, ++ IPU_OUTPUT_BUFFER, *buffer_num, ++ eba); ++ } else { ++ err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM, ++ IPU_OUTPUT_BUFFER, *buffer_num, ++ eba); ++ } ++ if (err != 0) { ++ if (grotation >= IPU_ROTATE_90_RIGHT) { ++ ipu_clear_buffer_ready(ipu, MEM_ROT_ENC_MEM, ++ IPU_OUTPUT_BUFFER, ++ *buffer_num); ++ err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM, ++ IPU_OUTPUT_BUFFER, ++ *buffer_num, ++ eba); ++ } else { ++ ipu_clear_buffer_ready(ipu, CSI_PRP_ENC_MEM, ++ IPU_OUTPUT_BUFFER, ++ *buffer_num); ++ err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM, ++ IPU_OUTPUT_BUFFER, ++ *buffer_num, ++ eba); ++ } ++ ++ if (err != 0) { ++ pr_err("ERROR: v4l2 capture: fail to update " ++ "buf%d\n", *buffer_num); ++ return err; ++ } ++ } ++ ++ if (grotation >= IPU_ROTATE_90_RIGHT) { ++ ipu_select_buffer(ipu, MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER, ++ *buffer_num); ++ } else { ++ ipu_select_buffer(ipu, CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, ++ *buffer_num); ++ } ++ ++ *buffer_num = (*buffer_num == 0) ? 1 : 0; ++ return 0; ++} ++ ++/*! ++ * Enable encoder task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_enc_enabling_tasks(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n"); ++ ++ cam->dummy_frame.vaddress = dma_alloc_coherent(0, ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), ++ &cam->dummy_frame.paddress, ++ GFP_DMA | GFP_KERNEL); ++ if (cam->dummy_frame.vaddress == 0) { ++ pr_err("ERROR: v4l2 capture: Allocate dummy frame " ++ "failed.\n"); ++ return -ENOBUFS; ++ } ++ cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; ++ cam->dummy_frame.buffer.length = ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); ++ cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; ++ ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) { ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, ++ prp_enc_callback, 0, "Mxc Camera", cam); ++ } else { ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, ++ prp_enc_callback, 0, "Mxc Camera", cam); ++ } ++ if (err != 0) { ++ printk(KERN_ERR "Error registering rot irq\n"); ++ return err; ++ } ++ ++ err = prp_enc_setup(cam); ++ if (err != 0) { ++ printk(KERN_ERR "prp_enc_setup %d\n", err); ++ return err; ++ } ++ ++ return err; ++} ++ ++/*! ++ * Disable encoder task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return int ++ */ ++static int prp_enc_disabling_tasks(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) { ++ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam); ++ ipu_unlink_channels(cam->ipu, CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM); ++ } ++ ++ err = ipu_disable_channel(cam->ipu, CSI_PRP_ENC_MEM, true); ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) ++ err |= ipu_disable_channel(cam->ipu, MEM_ROT_ENC_MEM, true); ++ ++ ipu_uninit_channel(cam->ipu, CSI_PRP_ENC_MEM); ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) ++ ipu_uninit_channel(cam->ipu, MEM_ROT_ENC_MEM); ++ ++ if (cam->dummy_frame.vaddress != 0) { ++ dma_free_coherent(0, cam->dummy_frame.buffer.length, ++ cam->dummy_frame.vaddress, ++ cam->dummy_frame.paddress); ++ cam->dummy_frame.vaddress = 0; ++ } ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) ++ mipi_csi2_pixelclk_disable(mipi_csi2_info); ++ } ++ } ++#endif ++ ++ return err; ++} ++ ++/*! ++ * Enable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_enc_enable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ return ipu_enable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * Disable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_enc_disable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ /* free csi eof irq firstly. ++ * when disable csi, wait for idmac eof. ++ * it requests eof irq again */ ++ if (cam->rotation < IPU_ROTATE_90_RIGHT) ++ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, cam); ++ ++ return ipu_disable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * function to select PRP-ENC as the working path ++ * ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return int ++ */ ++int prp_enc_select(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++ if (cam) { ++ cam->enc_update_eba = prp_enc_eba_update; ++ cam->enc_enable = prp_enc_enabling_tasks; ++ cam->enc_disable = prp_enc_disabling_tasks; ++ cam->enc_enable_csi = prp_enc_enable_csi; ++ cam->enc_disable_csi = prp_enc_disable_csi; ++ } else { ++ err = -EIO; ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL(prp_enc_select); ++ ++/*! ++ * function to de-select PRP-ENC as the working path ++ * ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return int ++ */ ++int prp_enc_deselect(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++ if (cam) { ++ cam->enc_update_eba = NULL; ++ cam->enc_enable = NULL; ++ cam->enc_disable = NULL; ++ cam->enc_enable_csi = NULL; ++ cam->enc_disable_csi = NULL; ++ if (cam->rot_enc_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->rot_enc_buf_size[0], ++ cam->rot_enc_bufs_vaddr[0], ++ cam->rot_enc_bufs[0]); ++ cam->rot_enc_bufs_vaddr[0] = NULL; ++ cam->rot_enc_bufs[0] = 0; ++ } ++ if (cam->rot_enc_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->rot_enc_buf_size[1], ++ cam->rot_enc_bufs_vaddr[1], ++ cam->rot_enc_bufs[1]); ++ cam->rot_enc_bufs_vaddr[1] = NULL; ++ cam->rot_enc_bufs[1] = 0; ++ } ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL(prp_enc_deselect); ++ ++/*! ++ * Init the Encorder channels ++ * ++ * @return Error code indicating success or failure ++ */ ++__init int prp_enc_init(void) ++{ ++ return 0; ++} ++ ++/*! ++ * Deinit the Encorder channels ++ * ++ */ ++void __exit prp_enc_exit(void) ++{ ++} ++ ++module_init(prp_enc_init); ++module_exit(prp_enc_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("IPU PRP ENC Driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_sw.h linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_sw.h +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_sw.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_sw.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,43 @@ ++/* ++ * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_prp_sw.h ++ * ++ * @brief This file contains the IPU PRP use case driver header. ++ * ++ * @ingroup IPU ++ */ ++ ++#ifndef _INCLUDE_IPU__PRP_SW_H_ ++#define _INCLUDE_IPU__PRP_SW_H_ ++ ++int csi_enc_select(void *private); ++int csi_enc_deselect(void *private); ++int prp_enc_select(void *private); ++int prp_enc_deselect(void *private); ++#ifdef CONFIG_MXC_IPU_PRP_VF_SDC ++int prp_vf_sdc_select(void *private); ++int prp_vf_sdc_deselect(void *private); ++int prp_vf_sdc_select_bg(void *private); ++int prp_vf_sdc_deselect_bg(void *private); ++#else ++int foreground_sdc_select(void *private); ++int foreground_sdc_deselect(void *private); ++int bg_overlay_sdc_select(void *private); ++int bg_overlay_sdc_deselect(void *private); ++#endif ++int prp_still_select(void *private); ++int prp_still_deselect(void *private); ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,521 @@ ++/* ++ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_prp_vf_sdc_bg.c ++ * ++ * @brief IPU Use case for PRP-VF back-ground ++ * ++ * @ingroup IPU ++ */ ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++static int buffer_num; ++static int buffer_ready; ++static struct ipu_soc *disp_ipu; ++ ++static void get_disp_ipu(cam_data *cam) ++{ ++ if (cam->output > 2) ++ disp_ipu = ipu_get_soc(1); /* using DISP4 */ ++ else ++ disp_ipu = ipu_get_soc(0); ++} ++ ++/* ++ * Function definitions ++ */ ++ ++/*! ++ * SDC V-Sync callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t prpvf_sdc_vsync_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = dev_id; ++ if (buffer_ready > 0) { ++ ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_OUTPUT_BUFFER, 0); ++ buffer_ready--; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/*! ++ * VF EOF callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t prpvf_vf_eof_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = dev_id; ++ pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num); ++ ++ ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_INPUT_BUFFER, buffer_num); ++ buffer_num = (buffer_num == 0) ? 1 : 0; ++ ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, buffer_num); ++ buffer_ready++; ++ return IRQ_HANDLED; ++} ++ ++/*! ++ * prpvf_start - start the vf task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int prpvf_start(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ipu_channel_params_t vf; ++ u32 format; ++ u32 offset; ++ u32 bpp, size = 3; ++ int err = 0; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (!cam) { ++ printk(KERN_ERR "private is NULL\n"); ++ return -EIO; ++ } ++ ++ if (cam->overlay_active == true) { ++ pr_debug("already start.\n"); ++ return 0; ++ } ++ ++ get_disp_ipu(cam); ++ ++ format = cam->v4l2_fb.fmt.pixelformat; ++ if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) { ++ bpp = 3, size = 3; ++ pr_info("BGR24\n"); ++ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) { ++ bpp = 2, size = 2; ++ pr_info("RGB565\n"); ++ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) { ++ bpp = 4, size = 4; ++ pr_info("BGR32\n"); ++ } else { ++ printk(KERN_ERR ++ "unsupported fix format from the framebuffer.\n"); ++ return -EINVAL; ++ } ++ ++ offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top + ++ size * cam->win.w.left; ++ ++ if (cam->v4l2_fb.base == 0) ++ printk(KERN_ERR "invalid frame buffer address.\n"); ++ else ++ offset += (u32) cam->v4l2_fb.base; ++ ++ memset(&vf, 0, sizeof(ipu_channel_params_t)); ++ ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width, ++ &vf.csi_prp_vf_mem.in_height, cam->csi); ++ vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY; ++ vf.csi_prp_vf_mem.out_width = cam->win.w.width; ++ vf.csi_prp_vf_mem.out_height = cam->win.w.height; ++ vf.csi_prp_vf_mem.csi = cam->csi; ++ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) { ++ vf.csi_prp_vf_mem.out_width = cam->win.w.height; ++ vf.csi_prp_vf_mem.out_height = cam->win.w.width; ++ } ++ vf.csi_prp_vf_mem.out_pixel_fmt = format; ++ size = cam->win.w.width * cam->win.w.height * size; ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) { ++ vf.csi_prp_vf_mem.mipi_en = true; ++ vf.csi_prp_vf_mem.mipi_vc = ++ mipi_csi2_get_virtual_channel(mipi_csi2_info); ++ vf.csi_prp_vf_mem.mipi_id = ++ mipi_csi2_get_datatype(mipi_csi2_info); ++ ++ mipi_csi2_pixelclk_enable(mipi_csi2_info); ++ } else { ++ vf.csi_prp_vf_mem.mipi_en = false; ++ vf.csi_prp_vf_mem.mipi_vc = 0; ++ vf.csi_prp_vf_mem.mipi_id = 0; ++ } ++ } else { ++ vf.csi_prp_vf_mem.mipi_en = false; ++ vf.csi_prp_vf_mem.mipi_vc = 0; ++ vf.csi_prp_vf_mem.mipi_id = 0; ++ } ++ } ++#endif ++ ++ err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf); ++ if (err != 0) ++ goto out_4; ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); ++ } ++ cam->vf_bufs_size[0] = PAGE_ALIGN(size); ++ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[0], ++ &cam->vf_bufs[0], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[0] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_3; ++ } ++ cam->vf_bufs_size[1] = PAGE_ALIGN(size); ++ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[1], ++ &cam->vf_bufs[1], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[1] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_3; ++ } ++ ++ err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, ++ format, vf.csi_prp_vf_mem.out_width, ++ vf.csi_prp_vf_mem.out_height, ++ vf.csi_prp_vf_mem.out_width, ++ IPU_ROTATE_NONE, ++ cam->vf_bufs[0], ++ cam->vf_bufs[1], ++ 0, 0, 0); ++ if (err != 0) { ++ printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n"); ++ goto out_3; ++ } ++ err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL); ++ if (err != 0) { ++ printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n"); ++ goto out_3; ++ } ++ ++ err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_INPUT_BUFFER, ++ format, vf.csi_prp_vf_mem.out_width, ++ vf.csi_prp_vf_mem.out_height, ++ vf.csi_prp_vf_mem.out_width, ++ cam->vf_rotation, ++ cam->vf_bufs[0], ++ cam->vf_bufs[1], ++ 0, 0, 0); ++ if (err != 0) { ++ printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n"); ++ goto out_2; ++ } ++ ++ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) { ++ err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_OUTPUT_BUFFER, ++ format, ++ vf.csi_prp_vf_mem.out_height, ++ vf.csi_prp_vf_mem.out_width, ++ cam->overlay_fb->var.xres * bpp, ++ IPU_ROTATE_NONE, ++ offset, 0, 0, 0, 0); ++ ++ if (err != 0) { ++ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n"); ++ goto out_2; ++ } ++ } else { ++ err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_OUTPUT_BUFFER, ++ format, ++ vf.csi_prp_vf_mem.out_width, ++ vf.csi_prp_vf_mem.out_height, ++ cam->overlay_fb->var.xres * bpp, ++ IPU_ROTATE_NONE, ++ offset, 0, 0, 0, 0); ++ if (err != 0) { ++ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n"); ++ goto out_2; ++ } ++ } ++ ++ ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF); ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, ++ prpvf_vf_eof_callback, ++ 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR ++ "Error registering IPU_IRQ_PRP_VF_OUT_EOF irq.\n"); ++ goto out_2; ++ } ++ ++ ipu_clear_irq(disp_ipu, IPU_IRQ_BG_SF_END); ++ err = ipu_request_irq(disp_ipu, IPU_IRQ_BG_SF_END, ++ prpvf_sdc_vsync_callback, ++ 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error registering IPU_IRQ_BG_SF_END irq.\n"); ++ goto out_1; ++ } ++ ++ ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM); ++ ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM); ++ ++ buffer_num = 0; ++ buffer_ready = 0; ++ ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0); ++ ++ cam->overlay_active = true; ++ return err; ++ ++out_1: ++ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL); ++out_2: ++ ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); ++out_3: ++ ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); ++out_4: ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++ if (cam->rot_vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->rot_vf_buf_size[0], ++ cam->rot_vf_bufs_vaddr[0], ++ cam->rot_vf_bufs[0]); ++ cam->rot_vf_bufs_vaddr[0] = NULL; ++ cam->rot_vf_bufs[0] = 0; ++ } ++ if (cam->rot_vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->rot_vf_buf_size[1], ++ cam->rot_vf_bufs_vaddr[1], ++ cam->rot_vf_bufs[1]); ++ cam->rot_vf_bufs_vaddr[1] = NULL; ++ cam->rot_vf_bufs[1] = 0; ++ } ++ return err; ++} ++ ++/*! ++ * prpvf_stop - stop the vf task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int prpvf_stop(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (cam->overlay_active == false) ++ return 0; ++ ++ ipu_free_irq(disp_ipu, IPU_IRQ_BG_SF_END, cam); ++ ++ ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true); ++ ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true); ++ ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); ++ ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) ++ mipi_csi2_pixelclk_disable(mipi_csi2_info); ++ } ++ } ++#endif ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++ if (cam->rot_vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->rot_vf_buf_size[0], ++ cam->rot_vf_bufs_vaddr[0], ++ cam->rot_vf_bufs[0]); ++ cam->rot_vf_bufs_vaddr[0] = NULL; ++ cam->rot_vf_bufs[0] = 0; ++ } ++ if (cam->rot_vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->rot_vf_buf_size[1], ++ cam->rot_vf_bufs_vaddr[1], ++ cam->rot_vf_bufs[1]); ++ cam->rot_vf_bufs_vaddr[1] = NULL; ++ cam->rot_vf_bufs[1] = 0; ++ } ++ ++ buffer_num = 0; ++ buffer_ready = 0; ++ cam->overlay_active = false; ++ return 0; ++} ++ ++/*! ++ * Enable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_vf_enable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ return ipu_enable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * Disable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_vf_disable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ /* free csi eof irq firstly. ++ * when disable csi, wait for idmac eof. ++ * it requests eof irq again */ ++ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam); ++ ++ return ipu_disable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * function to select PRP-VF as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return status ++ */ ++int prp_vf_sdc_select_bg(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ if (cam) { ++ cam->vf_start_sdc = prpvf_start; ++ cam->vf_stop_sdc = prpvf_stop; ++ cam->vf_enable_csi = prp_vf_enable_csi; ++ cam->vf_disable_csi = prp_vf_disable_csi; ++ cam->overlay_active = false; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(prp_vf_sdc_select_bg); ++ ++/*! ++ * function to de-select PRP-VF as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return status ++ */ ++int prp_vf_sdc_deselect_bg(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ if (cam) { ++ cam->vf_start_sdc = NULL; ++ cam->vf_stop_sdc = NULL; ++ cam->vf_enable_csi = NULL; ++ cam->vf_disable_csi = NULL; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(prp_vf_sdc_deselect_bg); ++ ++/*! ++ * Init viewfinder task. ++ * ++ * @return Error code indicating success or failure ++ */ ++__init int prp_vf_sdc_init_bg(void) ++{ ++ return 0; ++} ++ ++/*! ++ * Deinit viewfinder task. ++ * ++ * @return Error code indicating success or failure ++ */ ++void __exit prp_vf_sdc_exit_bg(void) ++{ ++} ++ ++module_init(prp_vf_sdc_init_bg); ++module_exit(prp_vf_sdc_exit_bg); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,582 @@ ++/* ++ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++/* * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_prp_vf_sdc.c ++ * ++ * @brief IPU Use case for PRP-VF ++ * ++ * @ingroup IPU ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++static int buffer_num; ++static struct ipu_soc *disp_ipu; ++ ++static void get_disp_ipu(cam_data *cam) ++{ ++ if (cam->output > 2) ++ disp_ipu = ipu_get_soc(1); /* using DISP4 */ ++ else ++ disp_ipu = ipu_get_soc(0); ++} ++ ++static irqreturn_t prpvf_rot_eof_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = dev_id; ++ pr_debug("buffer_num %d\n", buffer_num); ++ ++ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { ++ ipu_select_buffer(disp_ipu, MEM_FG_SYNC, ++ IPU_INPUT_BUFFER, buffer_num); ++ buffer_num = (buffer_num == 0) ? 1 : 0; ++ ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_OUTPUT_BUFFER, buffer_num); ++ } else { ++ ipu_select_buffer(disp_ipu, MEM_FG_SYNC, ++ IPU_INPUT_BUFFER, buffer_num); ++ buffer_num = (buffer_num == 0) ? 1 : 0; ++ ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, buffer_num); ++ } ++ return IRQ_HANDLED; ++} ++/* ++ * Function definitions ++ */ ++ ++/*! ++ * prpvf_start - start the vf task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int prpvf_start(void *private) ++{ ++ struct fb_var_screeninfo fbvar; ++ struct fb_info *fbi = NULL; ++ cam_data *cam = (cam_data *) private; ++ ipu_channel_params_t vf; ++ u32 vf_out_format = 0; ++ u32 size = 2, temp = 0; ++ int err = 0, i = 0; ++ short *tmp, color; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (!cam) { ++ printk(KERN_ERR "private is NULL\n"); ++ return -EIO; ++ } ++ ++ if (cam->overlay_active == true) { ++ pr_debug("already started.\n"); ++ return 0; ++ } ++ ++ get_disp_ipu(cam); ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ char *idstr = registered_fb[i]->fix.id; ++ if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || ++ ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { ++ fbi = registered_fb[i]; ++ break; ++ } ++ } ++ ++ if (fbi == NULL) { ++ printk(KERN_ERR "DISP FG fb not found\n"); ++ return -EPERM; ++ } ++ ++ fbvar = fbi->var; ++ ++ /* Store the overlay frame buffer's original std */ ++ cam->fb_origin_std = fbvar.nonstd; ++ ++ if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) { ++ /* Use DP to do CSC so that we can get better performance */ ++ vf_out_format = IPU_PIX_FMT_UYVY; ++ fbvar.nonstd = vf_out_format; ++ color = 0x80; ++ } else { ++ vf_out_format = IPU_PIX_FMT_RGB565; ++ fbvar.nonstd = 0; ++ color = 0x0; ++ } ++ ++ fbvar.bits_per_pixel = 16; ++ fbvar.xres = fbvar.xres_virtual = cam->win.w.width; ++ fbvar.yres = cam->win.w.height; ++ fbvar.yres_virtual = cam->win.w.height * 2; ++ fbvar.yoffset = 0; ++ fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG; ++ fbvar.activate |= FB_ACTIVATE_FORCE; ++ fb_set_var(fbi, &fbvar); ++ ++ ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left, ++ cam->win.w.top); ++ ++ /* Fill black color for framebuffer */ ++ tmp = (short *) fbi->screen_base; ++ for (i = 0; i < (fbi->fix.line_length * fbi->var.yres)/2; ++ i++, tmp++) ++ *tmp = color; ++ ++ console_lock(); ++ fb_blank(fbi, FB_BLANK_UNBLANK); ++ console_unlock(); ++ ++ /* correct display ch buffer address */ ++ ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, ++ 0, fbi->fix.smem_start + ++ (fbi->fix.line_length * fbvar.yres)); ++ ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, ++ 1, fbi->fix.smem_start); ++ ++ memset(&vf, 0, sizeof(ipu_channel_params_t)); ++ ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width, ++ &vf.csi_prp_vf_mem.in_height, cam->csi); ++ vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY; ++ vf.csi_prp_vf_mem.out_width = cam->win.w.width; ++ vf.csi_prp_vf_mem.out_height = cam->win.w.height; ++ vf.csi_prp_vf_mem.csi = cam->csi; ++ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) { ++ vf.csi_prp_vf_mem.out_width = cam->win.w.height; ++ vf.csi_prp_vf_mem.out_height = cam->win.w.width; ++ } ++ vf.csi_prp_vf_mem.out_pixel_fmt = vf_out_format; ++ size = cam->win.w.width * cam->win.w.height * size; ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) { ++ vf.csi_prp_vf_mem.mipi_en = true; ++ vf.csi_prp_vf_mem.mipi_vc = ++ mipi_csi2_get_virtual_channel(mipi_csi2_info); ++ vf.csi_prp_vf_mem.mipi_id = ++ mipi_csi2_get_datatype(mipi_csi2_info); ++ ++ mipi_csi2_pixelclk_enable(mipi_csi2_info); ++ } else { ++ vf.csi_prp_vf_mem.mipi_en = false; ++ vf.csi_prp_vf_mem.mipi_vc = 0; ++ vf.csi_prp_vf_mem.mipi_id = 0; ++ } ++ } else { ++ vf.csi_prp_vf_mem.mipi_en = false; ++ vf.csi_prp_vf_mem.mipi_vc = 0; ++ vf.csi_prp_vf_mem.mipi_id = 0; ++ } ++ } ++#endif ++ ++ err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf); ++ if (err != 0) ++ goto out_5; ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ } ++ cam->vf_bufs_size[0] = PAGE_ALIGN(size); ++ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[0], ++ (dma_addr_t *) & ++ cam->vf_bufs[0], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[0] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_4; ++ } ++ cam->vf_bufs_size[1] = PAGE_ALIGN(size); ++ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, ++ cam->vf_bufs_size[1], ++ (dma_addr_t *) & ++ cam->vf_bufs[1], ++ GFP_DMA | ++ GFP_KERNEL); ++ if (cam->vf_bufs_vaddr[1] == NULL) { ++ printk(KERN_ERR "Error to allocate vf buffer\n"); ++ err = -ENOMEM; ++ goto out_3; ++ } ++ pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); ++ ++ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { ++ err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, ++ vf_out_format, ++ vf.csi_prp_vf_mem.out_width, ++ vf.csi_prp_vf_mem.out_height, ++ vf.csi_prp_vf_mem.out_width, ++ IPU_ROTATE_NONE, ++ cam->vf_bufs[0], cam->vf_bufs[1], ++ 0, 0, 0); ++ if (err != 0) ++ goto out_3; ++ ++ err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL); ++ if (err != 0) { ++ printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n"); ++ goto out_3; ++ } ++ ++ err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_INPUT_BUFFER, ++ vf_out_format, ++ vf.csi_prp_vf_mem.out_width, ++ vf.csi_prp_vf_mem.out_height, ++ vf.csi_prp_vf_mem.out_width, ++ cam->vf_rotation, ++ cam->vf_bufs[0], ++ cam->vf_bufs[1], ++ 0, 0, 0); ++ if (err != 0) { ++ printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n"); ++ goto out_2; ++ } ++ ++ if (cam->vf_rotation < IPU_ROTATE_90_RIGHT) { ++ temp = vf.csi_prp_vf_mem.out_width; ++ vf.csi_prp_vf_mem.out_width = ++ vf.csi_prp_vf_mem.out_height; ++ vf.csi_prp_vf_mem.out_height = temp; ++ } ++ ++ err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_OUTPUT_BUFFER, ++ vf_out_format, ++ vf.csi_prp_vf_mem.out_height, ++ vf.csi_prp_vf_mem.out_width, ++ vf.csi_prp_vf_mem.out_height, ++ IPU_ROTATE_NONE, ++ fbi->fix.smem_start + ++ (fbi->fix.line_length * ++ fbi->var.yres), ++ fbi->fix.smem_start, 0, 0, 0); ++ ++ if (err != 0) { ++ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n"); ++ goto out_2; ++ } ++ ++ ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF); ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF, ++ prpvf_rot_eof_callback, ++ 0, "Mxc Camera", cam); ++ if (err < 0) { ++ printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_ROT_OUT_EOF\n"); ++ goto out_2; ++ } ++ ++ err = ipu_link_channels(cam->ipu, ++ CSI_PRP_VF_MEM, MEM_ROT_VF_MEM); ++ if (err < 0) { ++ printk(KERN_ERR ++ "Error link CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n"); ++ goto out_1; ++ } ++ ++ ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM); ++ ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM); ++ ++ ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, 0); ++ ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, 1); ++ ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, ++ IPU_OUTPUT_BUFFER, 0); ++ } else { ++ err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, ++ vf_out_format, cam->win.w.width, ++ cam->win.w.height, ++ cam->win.w.width, ++ cam->vf_rotation, ++ fbi->fix.smem_start + ++ (fbi->fix.line_length * ++ fbi->var.yres), ++ fbi->fix.smem_start, 0, 0, 0); ++ if (err != 0) { ++ printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n"); ++ goto out_4; ++ } ++ ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF); ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, ++ prpvf_rot_eof_callback, ++ 0, "Mxc Camera", cam); ++ if (err < 0) { ++ printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_OUT_EOF\n"); ++ goto out_4; ++ } ++ ++ ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM); ++ ++ ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, ++ IPU_OUTPUT_BUFFER, 0); ++ } ++ ++ cam->overlay_active = true; ++ return err; ++ ++out_1: ++ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL); ++out_2: ++ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) ++ ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); ++out_3: ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++out_4: ++ ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); ++out_5: ++ return err; ++} ++ ++/*! ++ * prpvf_stop - stop the vf task ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ */ ++static int prpvf_stop(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0, i = 0; ++ struct fb_info *fbi = NULL; ++ struct fb_var_screeninfo fbvar; ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ void *mipi_csi2_info; ++ int ipu_id; ++ int csi_id; ++#endif ++ ++ if (cam->overlay_active == false) ++ return 0; ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ char *idstr = registered_fb[i]->fix.id; ++ if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || ++ ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { ++ fbi = registered_fb[i]; ++ break; ++ } ++ } ++ ++ if (fbi == NULL) { ++ printk(KERN_ERR "DISP FG fb not found\n"); ++ return -EPERM; ++ } ++ ++ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { ++ ipu_unlink_channels(cam->ipu, CSI_PRP_VF_MEM, MEM_ROT_VF_MEM); ++ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF, cam); ++ } ++ buffer_num = 0; ++ ++ ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true); ++ ++ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { ++ ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true); ++ ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); ++ } ++ ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); ++ ++ console_lock(); ++ fb_blank(fbi, FB_BLANK_POWERDOWN); ++ console_unlock(); ++ ++ /* Set the overlay frame buffer std to what it is used to be */ ++ fbvar = fbi->var; ++ fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG; ++ fbvar.nonstd = cam->fb_origin_std; ++ fbvar.activate |= FB_ACTIVATE_FORCE; ++ fb_set_var(fbi, &fbvar); ++ ++#ifdef CONFIG_MXC_MIPI_CSI2 ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ if (mipi_csi2_info) { ++ if (mipi_csi2_get_status(mipi_csi2_info)) { ++ ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); ++ csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); ++ ++ if (cam->ipu == ipu_get_soc(ipu_id) ++ && cam->csi == csi_id) ++ mipi_csi2_pixelclk_disable(mipi_csi2_info); ++ } ++ } ++#endif ++ ++ if (cam->vf_bufs_vaddr[0]) { ++ dma_free_coherent(0, cam->vf_bufs_size[0], ++ cam->vf_bufs_vaddr[0], ++ (dma_addr_t) cam->vf_bufs[0]); ++ cam->vf_bufs_vaddr[0] = NULL; ++ cam->vf_bufs[0] = 0; ++ } ++ if (cam->vf_bufs_vaddr[1]) { ++ dma_free_coherent(0, cam->vf_bufs_size[1], ++ cam->vf_bufs_vaddr[1], ++ (dma_addr_t) cam->vf_bufs[1]); ++ cam->vf_bufs_vaddr[1] = NULL; ++ cam->vf_bufs[1] = 0; ++ } ++ ++ cam->overlay_active = false; ++ return err; ++} ++ ++/*! ++ * Enable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_vf_enable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ return ipu_enable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * Disable csi ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_vf_disable_csi(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ /* free csi eof irq firstly. ++ * when disable csi, wait for idmac eof. ++ * it requests eof irq again */ ++ if (cam->vf_rotation < IPU_ROTATE_VERT_FLIP) ++ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam); ++ ++ return ipu_disable_csi(cam->ipu, cam->csi); ++} ++ ++/*! ++ * function to select PRP-VF as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return status ++ */ ++int prp_vf_sdc_select(void *private) ++{ ++ cam_data *cam; ++ int err = 0; ++ if (private) { ++ cam = (cam_data *) private; ++ cam->vf_start_sdc = prpvf_start; ++ cam->vf_stop_sdc = prpvf_stop; ++ cam->vf_enable_csi = prp_vf_enable_csi; ++ cam->vf_disable_csi = prp_vf_disable_csi; ++ cam->overlay_active = false; ++ } else ++ err = -EIO; ++ ++ return err; ++} ++EXPORT_SYMBOL(prp_vf_sdc_select); ++ ++/*! ++ * function to de-select PRP-VF as the working path ++ * ++ * @param private cam_data * mxc v4l2 main structure ++ * ++ * @return int ++ */ ++int prp_vf_sdc_deselect(void *private) ++{ ++ cam_data *cam; ++ ++ if (private) { ++ cam = (cam_data *) private; ++ cam->vf_start_sdc = NULL; ++ cam->vf_stop_sdc = NULL; ++ cam->vf_enable_csi = NULL; ++ cam->vf_disable_csi = NULL; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(prp_vf_sdc_deselect); ++ ++/*! ++ * Init viewfinder task. ++ * ++ * @return Error code indicating success or failure ++ */ ++__init int prp_vf_sdc_init(void) ++{ ++ return 0; ++} ++ ++/*! ++ * Deinit viewfinder task. ++ * ++ * @return Error code indicating success or failure ++ */ ++void __exit prp_vf_sdc_exit(void) ++{ ++} ++ ++module_init(prp_vf_sdc_init); ++module_exit(prp_vf_sdc_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("IPU PRP VF SDC Driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ipu_still.c linux-openelec/drivers/media/platform/mxc/capture/ipu_still.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ipu_still.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ipu_still.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,268 @@ ++/* ++ * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file ipu_still.c ++ * ++ * @brief IPU Use case for still image capture ++ * ++ * @ingroup IPU ++ */ ++ ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++static int callback_eof_flag; ++#ifndef CONFIG_MXC_IPU_V1 ++static int buffer_num; ++#endif ++ ++#ifdef CONFIG_MXC_IPU_V1 ++static int callback_flag; ++/* ++ * Function definitions ++ */ ++/*! ++ * CSI EOF callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t prp_csi_eof_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = devid; ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, ++ callback_flag%2 ? 1 : 0); ++ if (callback_flag == 0) ++ ipu_enable_channel(cam->ipu, CSI_MEM); ++ ++ callback_flag++; ++ return IRQ_HANDLED; ++} ++#endif ++ ++/*! ++ * CSI callback function. ++ * ++ * @param irq int irq line ++ * @param dev_id void * device id ++ * ++ * @return status IRQ_HANDLED for handled ++ */ ++static irqreturn_t prp_still_callback(int irq, void *dev_id) ++{ ++ cam_data *cam = (cam_data *) dev_id; ++ ++ callback_eof_flag++; ++ if (callback_eof_flag < 5) { ++#ifndef CONFIG_MXC_IPU_V1 ++ buffer_num = (buffer_num == 0) ? 1 : 0; ++ ipu_select_buffer(cam->ipu, CSI_MEM, ++ IPU_OUTPUT_BUFFER, buffer_num); ++#endif ++ } else { ++ cam->still_counter++; ++ wake_up_interruptible(&cam->still_queue); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/*! ++ * start csi->mem task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_still_start(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ u32 pixel_fmt; ++ int err; ++ ipu_channel_params_t params; ++ ++ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ++ pixel_fmt = IPU_PIX_FMT_YUV420P; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) ++ pixel_fmt = IPU_PIX_FMT_NV12; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) ++ pixel_fmt = IPU_PIX_FMT_YUV422P; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) ++ pixel_fmt = IPU_PIX_FMT_UYVY; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ++ pixel_fmt = IPU_PIX_FMT_YUYV; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) ++ pixel_fmt = IPU_PIX_FMT_BGR24; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) ++ pixel_fmt = IPU_PIX_FMT_RGB24; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) ++ pixel_fmt = IPU_PIX_FMT_RGB565; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) ++ pixel_fmt = IPU_PIX_FMT_BGR32; ++ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) ++ pixel_fmt = IPU_PIX_FMT_RGB32; ++ else { ++ printk(KERN_ERR "format not supported\n"); ++ return -EINVAL; ++ } ++ ++ memset(¶ms, 0, sizeof(params)); ++ err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); ++ if (err != 0) ++ return err; ++ ++ err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, ++ pixel_fmt, cam->v2f.fmt.pix.width, ++ cam->v2f.fmt.pix.height, ++ cam->v2f.fmt.pix.width, IPU_ROTATE_NONE, ++ cam->still_buf[0], cam->still_buf[1], 0, ++ 0, 0); ++ if (err != 0) ++ return err; ++ ++#ifdef CONFIG_MXC_IPU_V1 ++ ipu_clear_irq(IPU_IRQ_SENSOR_OUT_EOF); ++ err = ipu_request_irq(IPU_IRQ_SENSOR_OUT_EOF, prp_still_callback, ++ 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error registering irq.\n"); ++ return err; ++ } ++ callback_flag = 0; ++ callback_eof_flag = 0; ++ ipu_clear_irq(IPU_IRQ_SENSOR_EOF); ++ err = ipu_request_irq(IPU_IRQ_SENSOR_EOF, prp_csi_eof_callback, ++ 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error IPU_IRQ_SENSOR_EOF\n"); ++ return err; ++ } ++#else ++ callback_eof_flag = 0; ++ buffer_num = 0; ++ ++ ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); ++ err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, ++ prp_still_callback, ++ 0, "Mxc Camera", cam); ++ if (err != 0) { ++ printk(KERN_ERR "Error registering irq.\n"); ++ return err; ++ } ++ ++ ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0); ++ ipu_enable_channel(cam->ipu, CSI_MEM); ++ ipu_enable_csi(cam->ipu, cam->csi); ++#endif ++ ++ return err; ++} ++ ++/*! ++ * stop csi->mem encoder task ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++static int prp_still_stop(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++#ifdef CONFIG_MXC_IPU_V1 ++ ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL); ++ ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam); ++#else ++ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); ++#endif ++ ++ ipu_disable_csi(cam->ipu, cam->csi); ++ ipu_disable_channel(cam->ipu, CSI_MEM, true); ++ ipu_uninit_channel(cam->ipu, CSI_MEM); ++ ++ return err; ++} ++ ++/*! ++ * function to select CSI_MEM as the working path ++ * ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++int prp_still_select(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ ++ if (cam) { ++ cam->csi_start = prp_still_start; ++ cam->csi_stop = prp_still_stop; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(prp_still_select); ++ ++/*! ++ * function to de-select CSI_MEM as the working path ++ * ++ * @param private struct cam_data * mxc capture instance ++ * ++ * @return status ++ */ ++int prp_still_deselect(void *private) ++{ ++ cam_data *cam = (cam_data *) private; ++ int err = 0; ++ ++ err = prp_still_stop(cam); ++ ++ if (cam) { ++ cam->csi_start = NULL; ++ cam->csi_stop = NULL; ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL(prp_still_deselect); ++ ++/*! ++ * Init the Encorder channels ++ * ++ * @return Error code indicating success or failure ++ */ ++__init int prp_still_init(void) ++{ ++ return 0; ++} ++ ++/*! ++ * Deinit the Encorder channels ++ * ++ */ ++void __exit prp_still_exit(void) ++{ ++} ++ ++module_init(prp_still_init); ++module_exit(prp_still_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("IPU PRP STILL IMAGE Driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/Kconfig linux-openelec/drivers/media/platform/mxc/capture/Kconfig +--- linux-3.14.36/drivers/media/platform/mxc/capture/Kconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,86 @@ ++if VIDEO_MXC_CAPTURE ++ ++menu "MXC Camera/V4L2 PRP Features support" ++config VIDEO_MXC_IPU_CAMERA ++ bool ++ depends on VIDEO_MXC_CAPTURE && MXC_IPU ++ default y ++ ++config VIDEO_MXC_CSI_CAMERA ++ tristate "CSI camera support" ++ depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2 ++ ---help--- ++ This is the video4linux2 capture driver based on CSI module. ++ ++config MXC_CAMERA_OV5640 ++ tristate "OmniVision ov5640 camera support" ++ depends on !VIDEO_MXC_EMMA_CAMERA && I2C ++ ---help--- ++ If you plan to use the ov5640 Camera with your MXC system, say Y here. ++ ++config MXC_CAMERA_OV5642 ++ tristate "OmniVision ov5642 camera support" ++ depends on !VIDEO_MXC_EMMA_CAMERA && I2C ++ ---help--- ++ If you plan to use the ov5642 Camera with your MXC system, say Y here. ++ ++config MXC_CAMERA_OV5640_MIPI ++ tristate "OmniVision ov5640 camera support using mipi" ++ depends on !VIDEO_MXC_EMMA_CAMERA && I2C ++ ---help--- ++ If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here. ++ ++config MXC_TVIN_ADV7180 ++ tristate "Analog Device adv7180 TV Decoder Input support" ++ depends on !VIDEO_MXC_EMMA_CAMERA && I2C ++ ---help--- ++ If you plan to use the adv7180 video decoder with your MXC system, say Y here. ++ ++choice ++ prompt "Select Overlay Rounting" ++ default MXC_IPU_DEVICE_QUEUE_SDC ++ depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL ++ ++config MXC_IPU_DEVICE_QUEUE_SDC ++ tristate "Queue ipu device for overlay library" ++ depends on VIDEO_MXC_IPU_CAMERA ++ ---help--- ++ Use case CSI->MEM->IPU DEVICE->SDC: ++ Images from sensor will be frist recieved in memory,then ++ queue to ipu device for processing if needed, and displaying ++ it on synchronous display with SDC use case. ++ ++config MXC_IPU_PRP_VF_SDC ++ bool "Pre-Processor VF SDC library" ++ depends on VIDEO_MXC_IPU_CAMERA ++ ---help--- ++ Use case PRP_VF_SDC: ++ Preprocessing image from smart sensor for viewfinder and ++ displaying it on synchronous display with SDC use case. ++ If SDC BG is selected, Rotation will not be supported. ++ CSI -> IC (PRP VF) -> MEM ++ MEM -> IC (ROT) -> MEM ++ MEM -> SDC (FG/BG) ++ ++endchoice ++ ++config MXC_IPU_PRP_ENC ++ tristate "Pre-processor Encoder library" ++ depends on VIDEO_MXC_IPU_CAMERA ++ default y ++ ---help--- ++ Use case PRP_ENC: ++ Preprocessing image from smart sensor for encoder. ++ CSI -> IC (PRP ENC) -> MEM ++ ++config MXC_IPU_CSI_ENC ++ tristate "IPU CSI Encoder library" ++ depends on VIDEO_MXC_IPU_CAMERA ++ default y ++ ---help--- ++ Use case IPU_CSI_ENC: ++ Get raw image with CSI from smart sensor for encoder. ++ CSI -> MEM ++endmenu ++ ++endif +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/Makefile linux-openelec/drivers/media/platform/mxc/capture/Makefile +--- linux-3.14.36/drivers/media/platform/mxc/capture/Makefile 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,21 @@ ++obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += fsl_csi.o csi_v4l2_capture.o ++ ++ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y) ++ obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc_v4l2_capture.o ++ obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o ++ obj-$(CONFIG_MXC_IPU_DEVICE_QUEUE_SDC) += ipu_fg_overlay_sdc.o ipu_bg_overlay_sdc.o ++ obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o ++ obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o ++endif ++ ++ov5640_camera-objs := ov5640.o ++obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera.o ++ ++ov5642_camera-objs := ov5642.o ++obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o ++ ++ov5640_camera_mipi-objs := ov5640_mipi.o ++obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi.o ++ ++adv7180_tvin-objs := adv7180.o ++obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c linux-openelec/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,3102 @@ ++/* ++ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @file drivers/media/video/mxc/capture/mxc_v4l2_capture.c ++ * ++ * @brief Mxc Video For Linux 2 driver ++ * ++ * @ingroup MXC_V4L2_CAPTURE ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++#include "ipu_prp_sw.h" ++ ++#define init_MUTEX(sem) sema_init(sem, 1) ++ ++static struct platform_device_id imx_v4l2_devtype[] = { ++ { ++ .name = "v4l2-capture-imx5", ++ .driver_data = IMX5_V4L2, ++ }, { ++ .name = "v4l2-capture-imx6", ++ .driver_data = IMX6_V4L2, ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(platform, imx_v4l2_devtype); ++ ++static const struct of_device_id mxc_v4l2_dt_ids[] = { ++ { ++ .compatible = "fsl,imx6q-v4l2-capture", ++ .data = &imx_v4l2_devtype[IMX6_V4L2], ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(of, mxc_v4l2_dt_ids); ++ ++static int video_nr = -1; ++ ++/*! This data is used for the output to the display. */ ++#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 6 ++#define MXC_V4L2_CAPTURE_NUM_INPUTS 2 ++static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = { ++ { ++ .index = 0, ++ .name = "DISP3 BG", ++ .type = V4L2_OUTPUT_TYPE_ANALOG, ++ .audioset = 0, ++ .modulator = 0, ++ .std = V4L2_STD_UNKNOWN, ++ }, ++ { ++ .index = 1, ++ .name = "DISP3 BG - DI1", ++ .type = V4L2_OUTPUT_TYPE_ANALOG, ++ .audioset = 0, ++ .modulator = 0, ++ .std = V4L2_STD_UNKNOWN, ++ }, ++ { ++ .index = 2, ++ .name = "DISP3 FG", ++ .type = V4L2_OUTPUT_TYPE_ANALOG, ++ .audioset = 0, ++ .modulator = 0, ++ .std = V4L2_STD_UNKNOWN, ++ }, ++ { ++ .index = 3, ++ .name = "DISP4 BG", ++ .type = V4L2_OUTPUT_TYPE_ANALOG, ++ .audioset = 0, ++ .modulator = 0, ++ .std = V4L2_STD_UNKNOWN, ++ }, ++ { ++ .index = 4, ++ .name = "DISP4 BG - DI1", ++ .type = V4L2_OUTPUT_TYPE_ANALOG, ++ .audioset = 0, ++ .modulator = 0, ++ .std = V4L2_STD_UNKNOWN, ++ }, ++ { ++ .index = 5, ++ .name = "DISP4 FG", ++ .type = V4L2_OUTPUT_TYPE_ANALOG, ++ .audioset = 0, ++ .modulator = 0, ++ .std = V4L2_STD_UNKNOWN, ++ }, ++}; ++ ++static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = { ++ { ++ .index = 0, ++ .name = "CSI IC MEM", ++ .type = V4L2_INPUT_TYPE_CAMERA, ++ .audioset = 0, ++ .tuner = 0, ++ .std = V4L2_STD_UNKNOWN, ++ .status = 0, ++ }, ++ { ++ .index = 1, ++ .name = "CSI MEM", ++ .type = V4L2_INPUT_TYPE_CAMERA, ++ .audioset = 0, ++ .tuner = 0, ++ .std = V4L2_STD_UNKNOWN, ++ .status = V4L2_IN_ST_NO_POWER, ++ }, ++}; ++ ++/*! List of TV input video formats supported. The video formats is corresponding ++ * to the v4l2_id in video_fmt_t. ++ * Currently, only PAL and NTSC is supported. Needs to be expanded in the ++ * future. ++ */ ++typedef enum { ++ TV_NTSC = 0, /*!< Locked on (M) NTSC video signal. */ ++ TV_PAL, /*!< (B, G, H, I, N)PAL video signal. */ ++ TV_NOT_LOCKED, /*!< Not locked on a signal. */ ++} video_fmt_idx; ++ ++/*! Number of video standards supported (including 'not locked' signal). */ ++#define TV_STD_MAX (TV_NOT_LOCKED + 1) ++ ++/*! Video format structure. */ ++typedef struct { ++ int v4l2_id; /*!< Video for linux ID. */ ++ char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */ ++ u16 raw_width; /*!< Raw width. */ ++ u16 raw_height; /*!< Raw height. */ ++ u16 active_width; /*!< Active width. */ ++ u16 active_height; /*!< Active height. */ ++ u16 active_top; /*!< Active top. */ ++ u16 active_left; /*!< Active left. */ ++} video_fmt_t; ++ ++/*! ++ * Description of video formats supported. ++ * ++ * PAL: raw=720x625, active=720x576. ++ * NTSC: raw=720x525, active=720x480. ++ */ ++static video_fmt_t video_fmts[] = { ++ { /*! NTSC */ ++ .v4l2_id = V4L2_STD_NTSC, ++ .name = "NTSC", ++ .raw_width = 720, /* SENS_FRM_WIDTH */ ++ .raw_height = 525, /* SENS_FRM_HEIGHT */ ++ .active_width = 720, /* ACT_FRM_WIDTH */ ++ .active_height = 480, /* ACT_FRM_HEIGHT */ ++ .active_top = 13, ++ .active_left = 0, ++ }, ++ { /*! (B, G, H, I, N) PAL */ ++ .v4l2_id = V4L2_STD_PAL, ++ .name = "PAL", ++ .raw_width = 720, ++ .raw_height = 625, ++ .active_width = 720, ++ .active_height = 576, ++ .active_top = 0, ++ .active_left = 0, ++ }, ++ { /*! Unlocked standard */ ++ .v4l2_id = V4L2_STD_ALL, ++ .name = "Autodetect", ++ .raw_width = 720, ++ .raw_height = 625, ++ .active_width = 720, ++ .active_height = 576, ++ .active_top = 0, ++ .active_left = 0, ++ }, ++}; ++ ++/*!* Standard index of TV. */ ++static video_fmt_idx video_index = TV_NOT_LOCKED; ++ ++static int mxc_v4l2_master_attach(struct v4l2_int_device *slave); ++static void mxc_v4l2_master_detach(struct v4l2_int_device *slave); ++static int start_preview(cam_data *cam); ++static int stop_preview(cam_data *cam); ++ ++/*! Information about this driver. */ ++static struct v4l2_int_master mxc_v4l2_master = { ++ .attach = mxc_v4l2_master_attach, ++ .detach = mxc_v4l2_master_detach, ++}; ++ ++/*************************************************************************** ++ * Functions for handling Frame buffers. ++ **************************************************************************/ ++ ++/*! ++ * Free frame buffers ++ * ++ * @param cam Structure cam_data * ++ * ++ * @return status 0 success. ++ */ ++static int mxc_free_frame_buf(cam_data *cam) ++{ ++ int i; ++ ++ pr_debug("MVC: In mxc_free_frame_buf\n"); ++ ++ for (i = 0; i < FRAME_NUM; i++) { ++ if (cam->frame[i].vaddress != 0) { ++ dma_free_coherent(0, cam->frame[i].buffer.length, ++ cam->frame[i].vaddress, ++ cam->frame[i].paddress); ++ cam->frame[i].vaddress = 0; ++ } ++ } ++ ++ return 0; ++} ++ ++/*! ++ * Allocate frame buffers ++ * ++ * @param cam Structure cam_data* ++ * @param count int number of buffer need to allocated ++ * ++ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. ++ */ ++static int mxc_allocate_frame_buf(cam_data *cam, int count) ++{ ++ int i; ++ ++ pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n", ++ cam->v2f.fmt.pix.sizeimage); ++ ++ for (i = 0; i < count; i++) { ++ cam->frame[i].vaddress = ++ dma_alloc_coherent(0, ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), ++ &cam->frame[i].paddress, ++ GFP_DMA | GFP_KERNEL); ++ if (cam->frame[i].vaddress == 0) { ++ pr_err("ERROR: v4l2 capture: " ++ "mxc_allocate_frame_buf failed.\n"); ++ mxc_free_frame_buf(cam); ++ return -ENOBUFS; ++ } ++ cam->frame[i].buffer.index = i; ++ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; ++ cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cam->frame[i].buffer.length = ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); ++ cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; ++ cam->frame[i].buffer.m.offset = cam->frame[i].paddress; ++ cam->frame[i].index = i; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * Free frame buffers status ++ * ++ * @param cam Structure cam_data * ++ * ++ * @return none ++ */ ++static void mxc_free_frames(cam_data *cam) ++{ ++ int i; ++ ++ pr_debug("In MVC:mxc_free_frames\n"); ++ ++ for (i = 0; i < FRAME_NUM; i++) ++ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; ++ ++ cam->enc_counter = 0; ++ INIT_LIST_HEAD(&cam->ready_q); ++ INIT_LIST_HEAD(&cam->working_q); ++ INIT_LIST_HEAD(&cam->done_q); ++} ++ ++/*! ++ * Return the buffer status ++ * ++ * @param cam Structure cam_data * ++ * @param buf Structure v4l2_buffer * ++ * ++ * @return status 0 success, EINVAL failed. ++ */ ++static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf) ++{ ++ pr_debug("In MVC:mxc_v4l2_buffer_status\n"); ++ ++ if (buf->index < 0 || buf->index >= FRAME_NUM) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers " ++ "not allocated\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf)); ++ return 0; ++} ++ ++static int mxc_v4l2_release_bufs(cam_data *cam) ++{ ++ pr_debug("In MVC:mxc_v4l2_release_bufs\n"); ++ return 0; ++} ++ ++static int mxc_v4l2_prepare_bufs(cam_data *cam, struct v4l2_buffer *buf) ++{ ++ pr_debug("In MVC:mxc_v4l2_prepare_bufs\n"); ++ ++ if (buf->index < 0 || buf->index >= FRAME_NUM || buf->length < ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage)) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l2_prepare_bufs buffers " ++ "not allocated,index=%d, length=%d\n", buf->index, ++ buf->length); ++ return -EINVAL; ++ } ++ ++ cam->frame[buf->index].buffer.index = buf->index; ++ cam->frame[buf->index].buffer.flags = V4L2_BUF_FLAG_MAPPED; ++ cam->frame[buf->index].buffer.length = buf->length; ++ cam->frame[buf->index].buffer.m.offset = cam->frame[buf->index].paddress ++ = buf->m.offset; ++ cam->frame[buf->index].buffer.type = buf->type; ++ cam->frame[buf->index].buffer.memory = V4L2_MEMORY_USERPTR; ++ cam->frame[buf->index].index = buf->index; ++ ++ return 0; ++} ++ ++/*************************************************************************** ++ * Functions for handling the video stream. ++ **************************************************************************/ ++ ++/*! ++ * Indicates whether the palette is supported. ++ * ++ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 ++ * ++ * @return 0 if failed ++ */ ++static inline int valid_mode(u32 palette) ++{ ++ return ((palette == V4L2_PIX_FMT_RGB565) || ++ (palette == V4L2_PIX_FMT_BGR24) || ++ (palette == V4L2_PIX_FMT_RGB24) || ++ (palette == V4L2_PIX_FMT_BGR32) || ++ (palette == V4L2_PIX_FMT_RGB32) || ++ (palette == V4L2_PIX_FMT_YUV422P) || ++ (palette == V4L2_PIX_FMT_UYVY) || ++ (palette == V4L2_PIX_FMT_YUYV) || ++ (palette == V4L2_PIX_FMT_YUV420) || ++ (palette == V4L2_PIX_FMT_YVU420) || ++ (palette == V4L2_PIX_FMT_NV12)); ++} ++ ++/*! ++ * Start the encoder job ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int mxc_streamon(cam_data *cam) ++{ ++ struct mxc_v4l_frame *frame; ++ unsigned long lock_flags; ++ int err = 0; ++ ++ pr_debug("In MVC:mxc_streamon\n"); ++ ++ if (NULL == cam) { ++ pr_err("ERROR! cam parameter is NULL\n"); ++ return -1; ++ } ++ ++ if (cam->capture_on) { ++ pr_err("ERROR: v4l2 capture: Capture stream has been turned " ++ " on\n"); ++ return -1; ++ } ++ ++ if (list_empty(&cam->ready_q)) { ++ pr_err("ERROR: v4l2 capture: mxc_streamon buffer has not been " ++ "queued yet\n"); ++ return -EINVAL; ++ } ++ if (cam->enc_update_eba && ++ cam->ready_q.prev == cam->ready_q.next) { ++ pr_err("ERROR: v4l2 capture: mxc_streamon buffer need " ++ "ping pong at least two buffers\n"); ++ return -EINVAL; ++ } ++ ++ cam->capture_pid = current->pid; ++ ++ if (cam->overlay_on == true) ++ stop_preview(cam); ++ ++ if (cam->enc_enable) { ++ err = cam->enc_enable(cam); ++ if (err != 0) ++ return err; ++ } ++ ++ spin_lock_irqsave(&cam->queue_int_lock, lock_flags); ++ cam->ping_pong_csi = 0; ++ cam->local_buf_num = 0; ++ if (cam->enc_update_eba) { ++ frame = ++ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); ++ list_del(cam->ready_q.next); ++ list_add_tail(&frame->queue, &cam->working_q); ++ frame->ipu_buf_num = cam->ping_pong_csi; ++ err = cam->enc_update_eba(cam->ipu, frame->buffer.m.offset, ++ &cam->ping_pong_csi); ++ ++ frame = ++ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); ++ list_del(cam->ready_q.next); ++ list_add_tail(&frame->queue, &cam->working_q); ++ frame->ipu_buf_num = cam->ping_pong_csi; ++ err |= cam->enc_update_eba(cam->ipu, frame->buffer.m.offset, ++ &cam->ping_pong_csi); ++ spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); ++ } else { ++ spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); ++ return -EINVAL; ++ } ++ ++ if (cam->overlay_on == true) ++ start_preview(cam); ++ ++ if (cam->enc_enable_csi) { ++ err = cam->enc_enable_csi(cam); ++ if (err != 0) ++ return err; ++ } ++ ++ cam->capture_on = true; ++ ++ return err; ++} ++ ++/*! ++ * Shut down the encoder job ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int mxc_streamoff(cam_data *cam) ++{ ++ int err = 0; ++ ++ pr_debug("In MVC:mxc_streamoff\n"); ++ ++ if (cam->capture_on == false) ++ return 0; ++ ++ /* For both CSI--MEM and CSI--IC--MEM ++ * 1. wait for idmac eof ++ * 2. disable csi first ++ * 3. disable idmac ++ * 4. disable smfc (CSI--MEM channel) ++ */ ++ if (mxc_capture_inputs[cam->current_input].name != NULL) { ++ if (cam->enc_disable_csi) { ++ err = cam->enc_disable_csi(cam); ++ if (err != 0) ++ return err; ++ } ++ if (cam->enc_disable) { ++ err = cam->enc_disable(cam); ++ if (err != 0) ++ return err; ++ } ++ } ++ ++ mxc_free_frames(cam); ++ mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER; ++ cam->capture_on = false; ++ return err; ++} ++ ++/*! ++ * Valid and adjust the overlay window size, position ++ * ++ * @param cam structure cam_data * ++ * @param win struct v4l2_window * ++ * ++ * @return 0 ++ */ ++static int verify_preview(cam_data *cam, struct v4l2_window *win) ++{ ++ int i = 0, width_bound = 0, height_bound = 0; ++ int *width, *height; ++ unsigned int ipu_ch = CHAN_NONE; ++ struct fb_info *bg_fbi = NULL, *fbi = NULL; ++ bool foregound_fb = false; ++ mm_segment_t old_fs; ++ ++ pr_debug("In MVC: verify_preview\n"); ++ ++ do { ++ fbi = (struct fb_info *)registered_fb[i]; ++ if (fbi == NULL) { ++ pr_err("ERROR: verify_preview frame buffer NULL.\n"); ++ return -1; ++ } ++ ++ /* Which DI supports 2 layers? */ ++ if (((strncmp(fbi->fix.id, "DISP3 BG", 8) == 0) && ++ (cam->output < 3)) || ++ ((strncmp(fbi->fix.id, "DISP4 BG", 8) == 0) && ++ (cam->output >= 3))) { ++ if (fbi->fbops->fb_ioctl) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN, ++ (unsigned long)&ipu_ch); ++ set_fs(old_fs); ++ } ++ if (ipu_ch == MEM_BG_SYNC) { ++ bg_fbi = fbi; ++ pr_debug("Found background frame buffer.\n"); ++ } ++ } ++ ++ /* Found the frame buffer to preview on. */ ++ if (strcmp(fbi->fix.id, ++ mxc_capture_outputs[cam->output].name) == 0) { ++ if (((strcmp(fbi->fix.id, "DISP3 FG") == 0) && ++ (cam->output < 3)) || ++ ((strcmp(fbi->fix.id, "DISP4 FG") == 0) && ++ (cam->output >= 3))) ++ foregound_fb = true; ++ ++ cam->overlay_fb = fbi; ++ break; ++ } ++ } while (++i < FB_MAX); ++ ++ if (foregound_fb) { ++ width_bound = bg_fbi->var.xres; ++ height_bound = bg_fbi->var.yres; ++ ++ if (win->w.width + win->w.left > bg_fbi->var.xres || ++ win->w.height + win->w.top > bg_fbi->var.yres) { ++ pr_err("ERROR: FG window position exceeds.\n"); ++ return -1; ++ } ++ } else { ++ /* 4 bytes alignment for BG */ ++ width_bound = cam->overlay_fb->var.xres; ++ height_bound = cam->overlay_fb->var.yres; ++ ++ if (cam->overlay_fb->var.bits_per_pixel == 24) ++ win->w.left -= win->w.left % 4; ++ else if (cam->overlay_fb->var.bits_per_pixel == 16) ++ win->w.left -= win->w.left % 2; ++ ++ if (win->w.width + win->w.left > cam->overlay_fb->var.xres) ++ win->w.width = cam->overlay_fb->var.xres - win->w.left; ++ if (win->w.height + win->w.top > cam->overlay_fb->var.yres) ++ win->w.height = cam->overlay_fb->var.yres - win->w.top; ++ } ++ ++ /* stride line limitation */ ++ win->w.height -= win->w.height % 8; ++ win->w.width -= win->w.width % 8; ++ ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) { ++ height = &win->w.width; ++ width = &win->w.height; ++ } else { ++ width = &win->w.width; ++ height = &win->w.height; ++ } ++ ++ if (*width == 0 || *height == 0) { ++ pr_err("ERROR: v4l2 capture: width or height" ++ " too small.\n"); ++ return -EINVAL; ++ } ++ ++ if ((cam->crop_bounds.width / *width > 8) || ++ ((cam->crop_bounds.width / *width == 8) && ++ (cam->crop_bounds.width % *width))) { ++ *width = cam->crop_bounds.width / 8; ++ if (*width % 8) ++ *width += 8 - *width % 8; ++ if (*width + win->w.left > width_bound) { ++ pr_err("ERROR: v4l2 capture: width exceeds " ++ "resize limit.\n"); ++ return -1; ++ } ++ pr_err("ERROR: v4l2 capture: width exceeds limit. " ++ "Resize to %d.\n", ++ *width); ++ } ++ ++ if ((cam->crop_bounds.height / *height > 8) || ++ ((cam->crop_bounds.height / *height == 8) && ++ (cam->crop_bounds.height % *height))) { ++ *height = cam->crop_bounds.height / 8; ++ if (*height % 8) ++ *height += 8 - *height % 8; ++ if (*height + win->w.top > height_bound) { ++ pr_err("ERROR: v4l2 capture: height exceeds " ++ "resize limit.\n"); ++ return -1; ++ } ++ pr_err("ERROR: v4l2 capture: height exceeds limit " ++ "resize to %d.\n", ++ *height); ++ } ++ ++ return 0; ++} ++ ++/*! ++ * start the viewfinder job ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int start_preview(cam_data *cam) ++{ ++ int err = 0; ++ ++ pr_debug("MVC: start_preview\n"); ++ ++ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) ++ #ifdef CONFIG_MXC_IPU_PRP_VF_SDC ++ err = prp_vf_sdc_select(cam); ++ #else ++ err = foreground_sdc_select(cam); ++ #endif ++ else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) ++ #ifdef CONFIG_MXC_IPU_PRP_VF_SDC ++ err = prp_vf_sdc_select_bg(cam); ++ #else ++ err = bg_overlay_sdc_select(cam); ++ #endif ++ if (err != 0) ++ return err; ++ ++ if (cam->vf_start_sdc) { ++ err = cam->vf_start_sdc(cam); ++ if (err != 0) ++ return err; ++ } ++ ++ if (cam->vf_enable_csi) ++ err = cam->vf_enable_csi(cam); ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, ++ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", ++ __func__, ++ cam->crop_bounds.width, cam->crop_bounds.height); ++ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", ++ __func__, ++ cam->crop_defrect.width, cam->crop_defrect.height); ++ pr_debug("End of %s: crop_current widthxheight %d x %d\n", ++ __func__, ++ cam->crop_current.width, cam->crop_current.height); ++ ++ return err; ++} ++ ++/*! ++ * shut down the viewfinder job ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int stop_preview(cam_data *cam) ++{ ++ int err = 0; ++ ++ if (cam->vf_disable_csi) { ++ err = cam->vf_disable_csi(cam); ++ if (err != 0) ++ return err; ++ } ++ ++ if (cam->vf_stop_sdc) { ++ err = cam->vf_stop_sdc(cam); ++ if (err != 0) ++ return err; ++ } ++ ++ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) ++ #ifdef CONFIG_MXC_IPU_PRP_VF_SDC ++ err = prp_vf_sdc_deselect(cam); ++ #else ++ err = foreground_sdc_deselect(cam); ++ #endif ++ else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) ++ #ifdef CONFIG_MXC_IPU_PRP_VF_SDC ++ err = prp_vf_sdc_deselect_bg(cam); ++ #else ++ err = bg_overlay_sdc_deselect(cam); ++ #endif ++ ++ return err; ++} ++ ++/*************************************************************************** ++ * VIDIOC Functions. ++ **************************************************************************/ ++ ++/*! ++ * V4L2 - mxc_v4l2_g_fmt function ++ * ++ * @param cam structure cam_data * ++ * ++ * @param f structure v4l2_format * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f) ++{ ++ int retval = 0; ++ ++ pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type); ++ ++ switch (f->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); ++ f->fmt.pix = cam->v2f.fmt.pix; ++ break; ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); ++ f->fmt.win = cam->win; ++ break; ++ default: ++ pr_debug(" type is invalid\n"); ++ retval = -EINVAL; ++ } ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, ++ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", ++ __func__, ++ cam->crop_bounds.width, cam->crop_bounds.height); ++ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", ++ __func__, ++ cam->crop_defrect.width, cam->crop_defrect.height); ++ pr_debug("End of %s: crop_current widthxheight %d x %d\n", ++ __func__, ++ cam->crop_current.width, cam->crop_current.height); ++ ++ return retval; ++} ++ ++/*! ++ * V4L2 - mxc_v4l2_s_fmt function ++ * ++ * @param cam structure cam_data * ++ * ++ * @param f structure v4l2_format * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) ++{ ++ int retval = 0; ++ int size = 0; ++ int bytesperline = 0; ++ int *width, *height; ++ ++ pr_debug("In MVC: mxc_v4l2_s_fmt\n"); ++ ++ switch (f->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); ++ if (!valid_mode(f->fmt.pix.pixelformat)) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l2_s_fmt: format " ++ "not supported\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Force the capture window resolution to be crop bounds ++ * for CSI MEM input mode. ++ */ ++ if (strcmp(mxc_capture_inputs[cam->current_input].name, ++ "CSI MEM") == 0) { ++ f->fmt.pix.width = cam->crop_current.width; ++ f->fmt.pix.height = cam->crop_current.height; ++ } ++ ++ if (cam->rotation >= IPU_ROTATE_90_RIGHT) { ++ height = &f->fmt.pix.width; ++ width = &f->fmt.pix.height; ++ } else { ++ width = &f->fmt.pix.width; ++ height = &f->fmt.pix.height; ++ } ++ ++ /* stride line limitation */ ++ *width -= *width % 8; ++ *height -= *height % 8; ++ ++ if (*width == 0 || *height == 0) { ++ pr_err("ERROR: v4l2 capture: width or height" ++ " too small.\n"); ++ return -EINVAL; ++ } ++ ++ if ((cam->crop_current.width / *width > 8) || ++ ((cam->crop_current.width / *width == 8) && ++ (cam->crop_current.width % *width))) { ++ *width = cam->crop_current.width / 8; ++ if (*width % 8) ++ *width += 8 - *width % 8; ++ pr_err("ERROR: v4l2 capture: width exceeds limit " ++ "resize to %d.\n", ++ *width); ++ } ++ ++ if ((cam->crop_current.height / *height > 8) || ++ ((cam->crop_current.height / *height == 8) && ++ (cam->crop_current.height % *height))) { ++ *height = cam->crop_current.height / 8; ++ if (*height % 8) ++ *height += 8 - *height % 8; ++ pr_err("ERROR: v4l2 capture: height exceeds limit " ++ "resize to %d.\n", ++ *height); ++ } ++ ++ switch (f->fmt.pix.pixelformat) { ++ case V4L2_PIX_FMT_RGB565: ++ size = f->fmt.pix.width * f->fmt.pix.height * 2; ++ bytesperline = f->fmt.pix.width * 2; ++ break; ++ case V4L2_PIX_FMT_BGR24: ++ size = f->fmt.pix.width * f->fmt.pix.height * 3; ++ bytesperline = f->fmt.pix.width * 3; ++ break; ++ case V4L2_PIX_FMT_RGB24: ++ size = f->fmt.pix.width * f->fmt.pix.height * 3; ++ bytesperline = f->fmt.pix.width * 3; ++ break; ++ case V4L2_PIX_FMT_BGR32: ++ size = f->fmt.pix.width * f->fmt.pix.height * 4; ++ bytesperline = f->fmt.pix.width * 4; ++ break; ++ case V4L2_PIX_FMT_RGB32: ++ size = f->fmt.pix.width * f->fmt.pix.height * 4; ++ bytesperline = f->fmt.pix.width * 4; ++ break; ++ case V4L2_PIX_FMT_YUV422P: ++ size = f->fmt.pix.width * f->fmt.pix.height * 2; ++ bytesperline = f->fmt.pix.width; ++ break; ++ case V4L2_PIX_FMT_UYVY: ++ case V4L2_PIX_FMT_YUYV: ++ size = f->fmt.pix.width * f->fmt.pix.height * 2; ++ bytesperline = f->fmt.pix.width * 2; ++ break; ++ case V4L2_PIX_FMT_YUV420: ++ case V4L2_PIX_FMT_YVU420: ++ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; ++ bytesperline = f->fmt.pix.width; ++ break; ++ case V4L2_PIX_FMT_NV12: ++ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; ++ bytesperline = f->fmt.pix.width; ++ break; ++ default: ++ break; ++ } ++ ++ if (f->fmt.pix.bytesperline < bytesperline) ++ f->fmt.pix.bytesperline = bytesperline; ++ else ++ bytesperline = f->fmt.pix.bytesperline; ++ ++ if (f->fmt.pix.sizeimage < size) ++ f->fmt.pix.sizeimage = size; ++ else ++ size = f->fmt.pix.sizeimage; ++ ++ cam->v2f.fmt.pix = f->fmt.pix; ++ ++ if (cam->v2f.fmt.pix.priv != 0) { ++ if (copy_from_user(&cam->offset, ++ (void *)cam->v2f.fmt.pix.priv, ++ sizeof(cam->offset))) { ++ retval = -EFAULT; ++ break; ++ } ++ } ++ break; ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); ++ retval = verify_preview(cam, &f->fmt.win); ++ cam->win = f->fmt.win; ++ break; ++ default: ++ retval = -EINVAL; ++ } ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, ++ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", ++ __func__, ++ cam->crop_bounds.width, cam->crop_bounds.height); ++ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", ++ __func__, ++ cam->crop_defrect.width, cam->crop_defrect.height); ++ pr_debug("End of %s: crop_current widthxheight %d x %d\n", ++ __func__, ++ cam->crop_current.width, cam->crop_current.height); ++ ++ return retval; ++} ++ ++/*! ++ * get control param ++ * ++ * @param cam structure cam_data * ++ * ++ * @param c structure v4l2_control * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int mxc_v4l2_g_ctrl(cam_data *cam, struct v4l2_control *c) ++{ ++ int status = 0; ++ ++ pr_debug("In MVC:mxc_v4l2_g_ctrl\n"); ++ ++ /* probably don't need to store the values that can be retrieved, ++ * locally, but they are for now. */ ++ switch (c->id) { ++ case V4L2_CID_HFLIP: ++ /* This is handled in the ipu. */ ++ if (cam->rotation == IPU_ROTATE_HORIZ_FLIP) ++ c->value = 1; ++ break; ++ case V4L2_CID_VFLIP: ++ /* This is handled in the ipu. */ ++ if (cam->rotation == IPU_ROTATE_VERT_FLIP) ++ c->value = 1; ++ break; ++ case V4L2_CID_MXC_ROT: ++ /* This is handled in the ipu. */ ++ c->value = cam->rotation; ++ break; ++ case V4L2_CID_BRIGHTNESS: ++ if (cam->sensor) { ++ c->value = cam->bright; ++ status = vidioc_int_g_ctrl(cam->sensor, c); ++ cam->bright = c->value; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ status = -ENODEV; ++ } ++ break; ++ case V4L2_CID_HUE: ++ if (cam->sensor) { ++ c->value = cam->hue; ++ status = vidioc_int_g_ctrl(cam->sensor, c); ++ cam->hue = c->value; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ status = -ENODEV; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (cam->sensor) { ++ c->value = cam->contrast; ++ status = vidioc_int_g_ctrl(cam->sensor, c); ++ cam->contrast = c->value; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ status = -ENODEV; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (cam->sensor) { ++ c->value = cam->saturation; ++ status = vidioc_int_g_ctrl(cam->sensor, c); ++ cam->saturation = c->value; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ status = -ENODEV; ++ } ++ break; ++ case V4L2_CID_RED_BALANCE: ++ if (cam->sensor) { ++ c->value = cam->red; ++ status = vidioc_int_g_ctrl(cam->sensor, c); ++ cam->red = c->value; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ status = -ENODEV; ++ } ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ if (cam->sensor) { ++ c->value = cam->blue; ++ status = vidioc_int_g_ctrl(cam->sensor, c); ++ cam->blue = c->value; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ status = -ENODEV; ++ } ++ break; ++ case V4L2_CID_BLACK_LEVEL: ++ if (cam->sensor) { ++ c->value = cam->ae_mode; ++ status = vidioc_int_g_ctrl(cam->sensor, c); ++ cam->ae_mode = c->value; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ status = -ENODEV; ++ } ++ break; ++ default: ++ pr_err("ERROR: v4l2 capture: unsupported ioctrl!\n"); ++ } ++ ++ return status; ++} ++ ++/*! ++ * V4L2 - set_control function ++ * V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing. ++ * 0 for normal operation ++ * 1 for vertical flip ++ * 2 for horizontal flip ++ * 3 for horizontal and vertical flip ++ * 4 for 90 degree rotation ++ * @param cam structure cam_data * ++ * ++ * @param c structure v4l2_control * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c) ++{ ++ int i, ret = 0; ++ int tmp_rotation = IPU_ROTATE_NONE; ++ struct sensor_data *sensor_data; ++ ++ pr_debug("In MVC:mxc_v4l2_s_ctrl\n"); ++ ++ switch (c->id) { ++ case V4L2_CID_HFLIP: ++ /* This is done by the IPU */ ++ if (c->value == 1) { ++ if ((cam->rotation != IPU_ROTATE_VERT_FLIP) && ++ (cam->rotation != IPU_ROTATE_180)) ++ cam->rotation = IPU_ROTATE_HORIZ_FLIP; ++ else ++ cam->rotation = IPU_ROTATE_180; ++ } else { ++ if (cam->rotation == IPU_ROTATE_HORIZ_FLIP) ++ cam->rotation = IPU_ROTATE_NONE; ++ if (cam->rotation == IPU_ROTATE_180) ++ cam->rotation = IPU_ROTATE_VERT_FLIP; ++ } ++ break; ++ case V4L2_CID_VFLIP: ++ /* This is done by the IPU */ ++ if (c->value == 1) { ++ if ((cam->rotation != IPU_ROTATE_HORIZ_FLIP) && ++ (cam->rotation != IPU_ROTATE_180)) ++ cam->rotation = IPU_ROTATE_VERT_FLIP; ++ else ++ cam->rotation = IPU_ROTATE_180; ++ } else { ++ if (cam->rotation == IPU_ROTATE_VERT_FLIP) ++ cam->rotation = IPU_ROTATE_NONE; ++ if (cam->rotation == IPU_ROTATE_180) ++ cam->rotation = IPU_ROTATE_HORIZ_FLIP; ++ } ++ break; ++ case V4L2_CID_MXC_ROT: ++ case V4L2_CID_MXC_VF_ROT: ++ /* This is done by the IPU */ ++ switch (c->value) { ++ case V4L2_MXC_ROTATE_NONE: ++ tmp_rotation = IPU_ROTATE_NONE; ++ break; ++ case V4L2_MXC_ROTATE_VERT_FLIP: ++ tmp_rotation = IPU_ROTATE_VERT_FLIP; ++ break; ++ case V4L2_MXC_ROTATE_HORIZ_FLIP: ++ tmp_rotation = IPU_ROTATE_HORIZ_FLIP; ++ break; ++ case V4L2_MXC_ROTATE_180: ++ tmp_rotation = IPU_ROTATE_180; ++ break; ++ case V4L2_MXC_ROTATE_90_RIGHT: ++ tmp_rotation = IPU_ROTATE_90_RIGHT; ++ break; ++ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP: ++ tmp_rotation = IPU_ROTATE_90_RIGHT_VFLIP; ++ break; ++ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP: ++ tmp_rotation = IPU_ROTATE_90_RIGHT_HFLIP; ++ break; ++ case V4L2_MXC_ROTATE_90_LEFT: ++ tmp_rotation = IPU_ROTATE_90_LEFT; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ #ifdef CONFIG_MXC_IPU_PRP_VF_SDC ++ if (c->id == V4L2_CID_MXC_VF_ROT) ++ cam->vf_rotation = tmp_rotation; ++ else ++ cam->rotation = tmp_rotation; ++ #else ++ cam->rotation = tmp_rotation; ++ #endif ++ ++ break; ++ case V4L2_CID_HUE: ++ if (cam->sensor) { ++ cam->hue = c->value; ++ ret = vidioc_int_s_ctrl(cam->sensor, c); ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ ret = -ENODEV; ++ } ++ break; ++ case V4L2_CID_CONTRAST: ++ if (cam->sensor) { ++ cam->contrast = c->value; ++ ret = vidioc_int_s_ctrl(cam->sensor, c); ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ ret = -ENODEV; ++ } ++ break; ++ case V4L2_CID_BRIGHTNESS: ++ if (cam->sensor) { ++ cam->bright = c->value; ++ ret = vidioc_int_s_ctrl(cam->sensor, c); ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ ret = -ENODEV; ++ } ++ break; ++ case V4L2_CID_SATURATION: ++ if (cam->sensor) { ++ cam->saturation = c->value; ++ ret = vidioc_int_s_ctrl(cam->sensor, c); ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ ret = -ENODEV; ++ } ++ break; ++ case V4L2_CID_RED_BALANCE: ++ if (cam->sensor) { ++ cam->red = c->value; ++ ret = vidioc_int_s_ctrl(cam->sensor, c); ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ ret = -ENODEV; ++ } ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ if (cam->sensor) { ++ cam->blue = c->value; ++ ret = vidioc_int_s_ctrl(cam->sensor, c); ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ ret = -ENODEV; ++ } ++ break; ++ case V4L2_CID_EXPOSURE: ++ if (cam->sensor) { ++ cam->ae_mode = c->value; ++ ret = vidioc_int_s_ctrl(cam->sensor, c); ++ } else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ ret = -ENODEV; ++ } ++ break; ++ case V4L2_CID_MXC_FLASH: ++#ifdef CONFIG_MXC_IPU_V1 ++ ipu_csi_flash_strobe(true); ++#endif ++ break; ++ case V4L2_CID_MXC_SWITCH_CAM: ++ if (cam->sensor == cam->all_sensors[c->value]) ++ break; ++ ++ /* power down other cameraes before enable new one */ ++ for (i = 0; i < cam->sensor_index; i++) { ++ if (i != c->value) { ++ vidioc_int_dev_exit(cam->all_sensors[i]); ++ vidioc_int_s_power(cam->all_sensors[i], 0); ++ if (cam->mclk_on[cam->mclk_source]) { ++ ipu_csi_enable_mclk_if(cam->ipu, ++ CSI_MCLK_I2C, ++ cam->mclk_source, ++ false, false); ++ cam->mclk_on[cam->mclk_source] = ++ false; ++ } ++ } ++ } ++ sensor_data = cam->all_sensors[c->value]->priv; ++ if (sensor_data->io_init) ++ sensor_data->io_init(); ++ cam->sensor = cam->all_sensors[c->value]; ++ cam->mclk_source = sensor_data->mclk_source; ++ ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, ++ cam->mclk_source, true, true); ++ cam->mclk_on[cam->mclk_source] = true; ++ vidioc_int_s_power(cam->sensor, 1); ++ vidioc_int_dev_init(cam->sensor); ++ break; ++ default: ++ pr_debug(" default case\n"); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * V4L2 - mxc_v4l2_s_param function ++ * Allows setting of capturemode and frame rate. ++ * ++ * @param cam structure cam_data * ++ * @param parm structure v4l2_streamparm * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) ++{ ++ struct v4l2_ifparm ifparm; ++ struct v4l2_format cam_fmt; ++ struct v4l2_streamparm currentparm; ++ ipu_csi_signal_cfg_t csi_param; ++ u32 current_fps, parm_fps; ++ int err = 0; ++ ++ pr_debug("In mxc_v4l2_s_param\n"); ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n"); ++ return -EINVAL; ++ } ++ ++ /* Stop the viewfinder */ ++ if (cam->overlay_on == true) ++ stop_preview(cam); ++ ++ currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ /* First check that this device can support the changes requested. */ ++ err = vidioc_int_g_parm(cam->sensor, ¤tparm); ++ if (err) { ++ pr_err("%s: vidioc_int_g_parm returned an error %d\n", ++ __func__, err); ++ goto exit; ++ } ++ ++ current_fps = currentparm.parm.capture.timeperframe.denominator ++ / currentparm.parm.capture.timeperframe.numerator; ++ parm_fps = parm->parm.capture.timeperframe.denominator ++ / parm->parm.capture.timeperframe.numerator; ++ ++ pr_debug(" Current capabilities are %x\n", ++ currentparm.parm.capture.capability); ++ pr_debug(" Current capturemode is %d change to %d\n", ++ currentparm.parm.capture.capturemode, ++ parm->parm.capture.capturemode); ++ pr_debug(" Current framerate is %d change to %d\n", ++ current_fps, parm_fps); ++ ++ /* This will change any camera settings needed. */ ++ err = vidioc_int_s_parm(cam->sensor, parm); ++ if (err) { ++ pr_err("%s: vidioc_int_s_parm returned an error %d\n", ++ __func__, err); ++ goto exit; ++ } ++ ++ /* If resolution changed, need to re-program the CSI */ ++ /* Get new values. */ ++ vidioc_int_g_ifparm(cam->sensor, &ifparm); ++ ++ csi_param.data_width = 0; ++ csi_param.clk_mode = 0; ++ csi_param.ext_vsync = 0; ++ csi_param.Vsync_pol = 0; ++ csi_param.Hsync_pol = 0; ++ csi_param.pixclk_pol = 0; ++ csi_param.data_pol = 0; ++ csi_param.sens_clksrc = 0; ++ csi_param.pack_tight = 0; ++ csi_param.force_eof = 0; ++ csi_param.data_en_pol = 0; ++ csi_param.data_fmt = 0; ++ csi_param.csi = cam->csi; ++ csi_param.mclk = 0; ++ ++ pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr); ++ if (ifparm.u.bt656.clock_curr == 0) ++ csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; ++ else ++ csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; ++ ++ csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; ++ ++ if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) { ++ csi_param.data_width = IPU_CSI_DATA_WIDTH_8; ++ } else if (ifparm.u.bt656.mode ++ == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) { ++ csi_param.data_width = IPU_CSI_DATA_WIDTH_10; ++ } else { ++ csi_param.data_width = IPU_CSI_DATA_WIDTH_8; ++ } ++ ++ csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; ++ csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; ++ csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct; ++ ++ /* if the capturemode changed, the size bounds will have changed. */ ++ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); ++ pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", ++ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); ++ ++ csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; ++ ++ cam->crop_bounds.top = cam->crop_bounds.left = 0; ++ cam->crop_bounds.width = cam_fmt.fmt.pix.width; ++ cam->crop_bounds.height = cam_fmt.fmt.pix.height; ++ ++ /* ++ * Set the default current cropped resolution to be the same with ++ * the cropping boundary(except for tvin module). ++ */ ++ if (cam->device_type != 1) { ++ cam->crop_current.width = cam->crop_bounds.width; ++ cam->crop_current.height = cam->crop_bounds.height; ++ } ++ ++ /* This essentially loses the data at the left and bottom of the image ++ * giving a digital zoom image, if crop_current is less than the full ++ * size of the image. */ ++ ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, ++ cam->crop_current.height, cam->csi); ++ ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, ++ cam->crop_current.top, ++ cam->csi); ++ ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, ++ cam->crop_bounds.height, ++ cam_fmt.fmt.pix.pixelformat, csi_param); ++ ++ ++exit: ++ if (cam->overlay_on == true) ++ start_preview(cam); ++ ++ return err; ++} ++ ++/*! ++ * V4L2 - mxc_v4l2_s_std function ++ * ++ * Sets the TV standard to be used. ++ * ++ * @param cam structure cam_data * ++ * @param parm structure v4l2_streamparm * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int mxc_v4l2_s_std(cam_data *cam, v4l2_std_id e) ++{ ++ pr_debug("In mxc_v4l2_s_std %Lx\n", e); ++ ++ if (e == V4L2_STD_PAL) { ++ pr_debug(" Setting standard to PAL %Lx\n", V4L2_STD_PAL); ++ cam->standard.id = V4L2_STD_PAL; ++ video_index = TV_PAL; ++ } else if (e == V4L2_STD_NTSC) { ++ pr_debug(" Setting standard to NTSC %Lx\n", ++ V4L2_STD_NTSC); ++ /* Get rid of the white dot line in NTSC signal input */ ++ cam->standard.id = V4L2_STD_NTSC; ++ video_index = TV_NTSC; ++ } else { ++ cam->standard.id = V4L2_STD_ALL; ++ video_index = TV_NOT_LOCKED; ++ pr_err("ERROR: unrecognized std! %Lx (PAL=%Lx, NTSC=%Lx\n", ++ e, V4L2_STD_PAL, V4L2_STD_NTSC); ++ } ++ ++ cam->standard.index = video_index; ++ strcpy(cam->standard.name, video_fmts[video_index].name); ++ cam->crop_bounds.width = video_fmts[video_index].raw_width; ++ cam->crop_bounds.height = video_fmts[video_index].raw_height; ++ cam->crop_current.width = video_fmts[video_index].active_width; ++ cam->crop_current.height = video_fmts[video_index].active_height; ++ cam->crop_current.top = video_fmts[video_index].active_top; ++ cam->crop_current.left = video_fmts[video_index].active_left; ++ ++ return 0; ++} ++ ++/*! ++ * V4L2 - mxc_v4l2_g_std function ++ * ++ * Gets the TV standard from the TV input device. ++ * ++ * @param cam structure cam_data * ++ * ++ * @param e structure v4l2_streamparm * ++ * ++ * @return status 0 success, EINVAL failed ++ */ ++static int mxc_v4l2_g_std(cam_data *cam, v4l2_std_id *e) ++{ ++ struct v4l2_format tv_fmt; ++ ++ pr_debug("In mxc_v4l2_g_std\n"); ++ ++ if (cam->device_type == 1) { ++ /* Use this function to get what the TV-In device detects the ++ * format to be. pixelformat is used to return the std value ++ * since the interface has no vidioc_g_std.*/ ++ tv_fmt.type = V4L2_BUF_TYPE_PRIVATE; ++ vidioc_int_g_fmt_cap(cam->sensor, &tv_fmt); ++ ++ /* If the TV-in automatically detects the standard, then if it ++ * changes, the settings need to change. */ ++ if (cam->standard_autodetect) { ++ if (cam->standard.id != tv_fmt.fmt.pix.pixelformat) { ++ pr_debug("MVC: mxc_v4l2_g_std: " ++ "Changing standard\n"); ++ mxc_v4l2_s_std(cam, tv_fmt.fmt.pix.pixelformat); ++ } ++ } ++ ++ *e = tv_fmt.fmt.pix.pixelformat; ++ } ++ ++ return 0; ++} ++ ++/*! ++ * Dequeue one V4L capture buffer ++ * ++ * @param cam structure cam_data * ++ * @param buf structure v4l2_buffer * ++ * ++ * @return status 0 success, EINVAL invalid frame number, ++ * ETIME timeout, ERESTARTSYS interrupted by user ++ */ ++static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) ++{ ++ int retval = 0; ++ struct mxc_v4l_frame *frame; ++ unsigned long lock_flags; ++ ++ pr_debug("In MVC:mxc_v4l_dqueue\n"); ++ ++ if (!wait_event_interruptible_timeout(cam->enc_queue, ++ cam->enc_counter != 0, 10 * HZ)) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout " ++ "enc_counter %x\n", ++ cam->enc_counter); ++ return -ETIME; ++ } else if (signal_pending(current)) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() " ++ "interrupt received\n"); ++ return -ERESTARTSYS; ++ } ++ ++ if (down_interruptible(&cam->busy_lock)) ++ return -EBUSY; ++ ++ spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags); ++ cam->enc_counter--; ++ ++ frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue); ++ list_del(cam->done_q.next); ++ if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) { ++ frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE; ++ } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " ++ "Buffer not filled.\n"); ++ frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; ++ retval = -EINVAL; ++ } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " ++ "Buffer not queued.\n"); ++ retval = -EINVAL; ++ } ++ ++ cam->frame[frame->index].buffer.field = cam->device_type ? ++ V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; ++ ++ buf->bytesused = cam->v2f.fmt.pix.sizeimage; ++ buf->index = frame->index; ++ buf->flags = frame->buffer.flags; ++ buf->m = cam->frame[frame->index].buffer.m; ++ buf->timestamp = cam->frame[frame->index].buffer.timestamp; ++ buf->field = cam->frame[frame->index].buffer.field; ++ spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags); ++ ++ up(&cam->busy_lock); ++ return retval; ++} ++ ++/*! ++ * V4L interface - open function ++ * ++ * @param file structure file * ++ * ++ * @return status 0 success, ENODEV invalid device instance, ++ * ENODEV timeout, ERESTARTSYS interrupted by user ++ */ ++static int mxc_v4l_open(struct file *file) ++{ ++ struct v4l2_ifparm ifparm; ++ struct v4l2_format cam_fmt; ++ ipu_csi_signal_cfg_t csi_param; ++ struct video_device *dev = video_devdata(file); ++ cam_data *cam = video_get_drvdata(dev); ++ int err = 0; ++ struct sensor_data *sensor; ++ ++ pr_debug("\nIn MVC: mxc_v4l_open\n"); ++ pr_debug(" device name is %s\n", dev->name); ++ ++ if (!cam) { ++ pr_err("ERROR: v4l2 capture: Internal error, " ++ "cam_data not found!\n"); ++ return -EBADF; ++ } ++ ++ if (cam->sensor == NULL || ++ cam->sensor->type != v4l2_int_type_slave) { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ return -EAGAIN; ++ } ++ ++ sensor = cam->sensor->priv; ++ if (!sensor) { ++ pr_err("%s: Internal error, sensor_data is not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ down(&cam->busy_lock); ++ err = 0; ++ if (signal_pending(current)) ++ goto oops; ++ ++ if (cam->open_count++ == 0) { ++ wait_event_interruptible(cam->power_queue, ++ cam->low_power == false); ++ ++ if (strcmp(mxc_capture_inputs[cam->current_input].name, ++ "CSI MEM") == 0) { ++#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) ++ err = csi_enc_select(cam); ++#endif ++ } else if (strcmp(mxc_capture_inputs[cam->current_input].name, ++ "CSI IC MEM") == 0) { ++#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) ++ err = prp_enc_select(cam); ++#endif ++ } ++ ++ cam->enc_counter = 0; ++ INIT_LIST_HEAD(&cam->ready_q); ++ INIT_LIST_HEAD(&cam->working_q); ++ INIT_LIST_HEAD(&cam->done_q); ++ ++ vidioc_int_g_ifparm(cam->sensor, &ifparm); ++ ++ csi_param.sens_clksrc = 0; ++ ++ csi_param.clk_mode = 0; ++ csi_param.data_pol = 0; ++ csi_param.ext_vsync = 0; ++ ++ csi_param.pack_tight = 0; ++ csi_param.force_eof = 0; ++ csi_param.data_en_pol = 0; ++ ++ csi_param.mclk = ifparm.u.bt656.clock_curr; ++ ++ csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; ++ ++ if (ifparm.u.bt656.mode ++ == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) ++ csi_param.data_width = IPU_CSI_DATA_WIDTH_8; ++ else if (ifparm.u.bt656.mode ++ == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) ++ csi_param.data_width = IPU_CSI_DATA_WIDTH_10; ++ else ++ csi_param.data_width = IPU_CSI_DATA_WIDTH_8; ++ ++ ++ csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; ++ csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; ++ ++ csi_param.csi = cam->csi; ++ ++ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); ++ ++ /* Reset the sizes. Needed to prevent carryover of last ++ * operation.*/ ++ cam->crop_bounds.top = cam->crop_bounds.left = 0; ++ cam->crop_bounds.width = cam_fmt.fmt.pix.width; ++ cam->crop_bounds.height = cam_fmt.fmt.pix.height; ++ ++ /* This also is the max crop size for this device. */ ++ cam->crop_defrect.top = cam->crop_defrect.left = 0; ++ cam->crop_defrect.width = cam_fmt.fmt.pix.width; ++ cam->crop_defrect.height = cam_fmt.fmt.pix.height; ++ ++ /* At this point, this is also the current image size. */ ++ cam->crop_current.top = cam->crop_current.left = 0; ++ cam->crop_current.width = cam_fmt.fmt.pix.width; ++ cam->crop_current.height = cam_fmt.fmt.pix.height; ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, ++ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", ++ __func__, ++ cam->crop_bounds.width, cam->crop_bounds.height); ++ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", ++ __func__, ++ cam->crop_defrect.width, cam->crop_defrect.height); ++ pr_debug("End of %s: crop_current widthxheight %d x %d\n", ++ __func__, ++ cam->crop_current.width, cam->crop_current.height); ++ ++ csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; ++ pr_debug("On Open: Input to ipu size is %d x %d\n", ++ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); ++ ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, ++ cam->crop_current.height, ++ cam->csi); ++ ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, ++ cam->crop_current.top, ++ cam->csi); ++ ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, ++ cam->crop_bounds.height, ++ cam_fmt.fmt.pix.pixelformat, ++ csi_param); ++ clk_prepare_enable(sensor->sensor_clk); ++ vidioc_int_s_power(cam->sensor, 1); ++ vidioc_int_init(cam->sensor); ++ vidioc_int_dev_init(cam->sensor); ++ } ++ ++ file->private_data = dev; ++ ++oops: ++ up(&cam->busy_lock); ++ return err; ++} ++ ++/*! ++ * V4L interface - close function ++ * ++ * @param file struct file * ++ * ++ * @return 0 success ++ */ ++static int mxc_v4l_close(struct file *file) ++{ ++ struct video_device *dev = video_devdata(file); ++ int err = 0; ++ cam_data *cam = video_get_drvdata(dev); ++ struct sensor_data *sensor; ++ pr_debug("In MVC:mxc_v4l_close\n"); ++ ++ if (!cam) { ++ pr_err("ERROR: v4l2 capture: Internal error, " ++ "cam_data not found!\n"); ++ return -EBADF; ++ } ++ ++ if (!cam->sensor) { ++ pr_err("%s: Internal error, camera is not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ sensor = cam->sensor->priv; ++ if (!sensor) { ++ pr_err("%s: Internal error, sensor_data is not found!\n", __func__); ++ return -EBADF; ++ } ++ ++ down(&cam->busy_lock); ++ ++ /* for the case somebody hit the ctrl C */ ++ if (cam->overlay_pid == current->pid && cam->overlay_on) { ++ err = stop_preview(cam); ++ cam->overlay_on = false; ++ } ++ if (cam->capture_pid == current->pid) { ++ err |= mxc_streamoff(cam); ++ wake_up_interruptible(&cam->enc_queue); ++ } ++ ++ if (--cam->open_count == 0) { ++ vidioc_int_s_power(cam->sensor, 0); ++ clk_disable_unprepare(sensor->sensor_clk); ++ wait_event_interruptible(cam->power_queue, ++ cam->low_power == false); ++ pr_debug("mxc_v4l_close: release resource\n"); ++ ++ if (strcmp(mxc_capture_inputs[cam->current_input].name, ++ "CSI MEM") == 0) { ++#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) ++ err |= csi_enc_deselect(cam); ++#endif ++ } else if (strcmp(mxc_capture_inputs[cam->current_input].name, ++ "CSI IC MEM") == 0) { ++#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) ++ err |= prp_enc_deselect(cam); ++#endif ++ } ++ ++ mxc_free_frame_buf(cam); ++ file->private_data = NULL; ++ ++ /* capture off */ ++ wake_up_interruptible(&cam->enc_queue); ++ mxc_free_frames(cam); ++ cam->enc_counter++; ++ } ++ ++ up(&cam->busy_lock); ++ ++ return err; ++} ++ ++#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC) || \ ++ defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) || \ ++ defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) ++/* ++ * V4L interface - read function ++ * ++ * @param file struct file * ++ * @param read buf char * ++ * @param count size_t ++ * @param ppos structure loff_t * ++ * ++ * @return bytes read ++ */ ++static ssize_t mxc_v4l_read(struct file *file, char *buf, size_t count, ++ loff_t *ppos) ++{ ++ int err = 0; ++ u8 *v_address[2]; ++ struct video_device *dev = video_devdata(file); ++ cam_data *cam = video_get_drvdata(dev); ++ ++ if (down_interruptible(&cam->busy_lock)) ++ return -EINTR; ++ ++ /* Stop the viewfinder */ ++ if (cam->overlay_on == true) ++ stop_preview(cam); ++ ++ v_address[0] = dma_alloc_coherent(0, ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), ++ &cam->still_buf[0], ++ GFP_DMA | GFP_KERNEL); ++ ++ v_address[1] = dma_alloc_coherent(0, ++ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), ++ &cam->still_buf[1], ++ GFP_DMA | GFP_KERNEL); ++ ++ if (!v_address[0] || !v_address[1]) { ++ err = -ENOBUFS; ++ goto exit0; ++ } ++ ++ err = prp_still_select(cam); ++ if (err != 0) { ++ err = -EIO; ++ goto exit0; ++ } ++ ++ cam->still_counter = 0; ++ err = cam->csi_start(cam); ++ if (err != 0) { ++ err = -EIO; ++ goto exit1; ++ } ++ ++ if (!wait_event_interruptible_timeout(cam->still_queue, ++ cam->still_counter != 0, ++ 10 * HZ)) { ++ pr_err("ERROR: v4l2 capture: mxc_v4l_read timeout counter %x\n", ++ cam->still_counter); ++ err = -ETIME; ++ goto exit1; ++ } ++ err = copy_to_user(buf, v_address[1], cam->v2f.fmt.pix.sizeimage); ++ ++exit1: ++ prp_still_deselect(cam); ++ ++exit0: ++ if (v_address[0] != 0) ++ dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[0], ++ cam->still_buf[0]); ++ if (v_address[1] != 0) ++ dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[1], ++ cam->still_buf[1]); ++ ++ cam->still_buf[0] = cam->still_buf[1] = 0; ++ ++ if (cam->overlay_on == true) ++ start_preview(cam); ++ ++ up(&cam->busy_lock); ++ if (err < 0) ++ return err; ++ ++ return cam->v2f.fmt.pix.sizeimage - err; ++} ++#endif ++ ++/*! ++ * V4L interface - ioctl function ++ * ++ * @param file struct file* ++ * ++ * @param ioctlnr unsigned int ++ * ++ * @param arg void* ++ * ++ * @return 0 success, ENODEV for invalid device instance, ++ * -1 for other errors. ++ */ ++static long mxc_v4l_do_ioctl(struct file *file, ++ unsigned int ioctlnr, void *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ cam_data *cam = video_get_drvdata(dev); ++ int retval = 0; ++ unsigned long lock_flags; ++ ++ pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr); ++ wait_event_interruptible(cam->power_queue, cam->low_power == false); ++ /* make this _really_ smp-safe */ ++ if (ioctlnr != VIDIOC_DQBUF) ++ if (down_interruptible(&cam->busy_lock)) ++ return -EBUSY; ++ ++ switch (ioctlnr) { ++ /*! ++ * V4l2 VIDIOC_QUERYCAP ioctl ++ */ ++ case VIDIOC_QUERYCAP: { ++ struct v4l2_capability *cap = arg; ++ pr_debug(" case VIDIOC_QUERYCAP\n"); ++ strcpy(cap->driver, "mxc_v4l2"); ++ cap->version = KERNEL_VERSION(0, 1, 11); ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_VIDEO_OVERLAY | ++ V4L2_CAP_STREAMING | ++ V4L2_CAP_READWRITE; ++ cap->card[0] = '\0'; ++ cap->bus_info[0] = '\0'; ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_G_FMT ioctl ++ */ ++ case VIDIOC_G_FMT: { ++ struct v4l2_format *gf = arg; ++ pr_debug(" case VIDIOC_G_FMT\n"); ++ retval = mxc_v4l2_g_fmt(cam, gf); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_S_FMT ioctl ++ */ ++ case VIDIOC_S_FMT: { ++ struct v4l2_format *sf = arg; ++ pr_debug(" case VIDIOC_S_FMT\n"); ++ retval = mxc_v4l2_s_fmt(cam, sf); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_REQBUFS ioctl ++ */ ++ case VIDIOC_REQBUFS: { ++ struct v4l2_requestbuffers *req = arg; ++ pr_debug(" case VIDIOC_REQBUFS\n"); ++ ++ if (req->count > FRAME_NUM) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " ++ "not enough buffers\n"); ++ req->count = FRAME_NUM; ++ } ++ ++ if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " ++ "wrong buffer type\n"); ++ retval = -EINVAL; ++ break; ++ } ++ ++ mxc_streamoff(cam); ++ if (req->memory & V4L2_MEMORY_MMAP) { ++ mxc_free_frame_buf(cam); ++ retval = mxc_allocate_frame_buf(cam, req->count); ++ } ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_QUERYBUF ioctl ++ */ ++ case VIDIOC_QUERYBUF: { ++ struct v4l2_buffer *buf = arg; ++ int index = buf->index; ++ pr_debug(" case VIDIOC_QUERYBUF\n"); ++ ++ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ pr_err("ERROR: v4l2 capture: " ++ "VIDIOC_QUERYBUFS: " ++ "wrong buffer type\n"); ++ retval = -EINVAL; ++ break; ++ } ++ ++ if (buf->memory & V4L2_MEMORY_MMAP) { ++ memset(buf, 0, sizeof(buf)); ++ buf->index = index; ++ } ++ ++ down(&cam->param_lock); ++ if (buf->memory & V4L2_MEMORY_USERPTR) { ++ mxc_v4l2_release_bufs(cam); ++ retval = mxc_v4l2_prepare_bufs(cam, buf); ++ } ++ ++ if (buf->memory & V4L2_MEMORY_MMAP) ++ retval = mxc_v4l2_buffer_status(cam, buf); ++ up(&cam->param_lock); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_QBUF ioctl ++ */ ++ case VIDIOC_QBUF: { ++ struct v4l2_buffer *buf = arg; ++ int index = buf->index; ++ pr_debug(" case VIDIOC_QBUF\n"); ++ ++ spin_lock_irqsave(&cam->queue_int_lock, lock_flags); ++ if ((cam->frame[index].buffer.flags & 0x7) == ++ V4L2_BUF_FLAG_MAPPED) { ++ cam->frame[index].buffer.flags |= ++ V4L2_BUF_FLAG_QUEUED; ++ list_add_tail(&cam->frame[index].queue, ++ &cam->ready_q); ++ } else if (cam->frame[index].buffer. ++ flags & V4L2_BUF_FLAG_QUEUED) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " ++ "buffer already queued\n"); ++ retval = -EINVAL; ++ } else if (cam->frame[index].buffer. ++ flags & V4L2_BUF_FLAG_DONE) { ++ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " ++ "overwrite done buffer.\n"); ++ cam->frame[index].buffer.flags &= ++ ~V4L2_BUF_FLAG_DONE; ++ cam->frame[index].buffer.flags |= ++ V4L2_BUF_FLAG_QUEUED; ++ retval = -EINVAL; ++ } ++ ++ buf->flags = cam->frame[index].buffer.flags; ++ spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_DQBUF ioctl ++ */ ++ case VIDIOC_DQBUF: { ++ struct v4l2_buffer *buf = arg; ++ pr_debug(" case VIDIOC_DQBUF\n"); ++ ++ if ((cam->enc_counter == 0) && ++ (file->f_flags & O_NONBLOCK)) { ++ retval = -EAGAIN; ++ break; ++ } ++ ++ retval = mxc_v4l_dqueue(cam, buf); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_STREAMON ioctl ++ */ ++ case VIDIOC_STREAMON: { ++ pr_debug(" case VIDIOC_STREAMON\n"); ++ retval = mxc_streamon(cam); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_STREAMOFF ioctl ++ */ ++ case VIDIOC_STREAMOFF: { ++ pr_debug(" case VIDIOC_STREAMOFF\n"); ++ retval = mxc_streamoff(cam); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_G_CTRL ioctl ++ */ ++ case VIDIOC_G_CTRL: { ++ pr_debug(" case VIDIOC_G_CTRL\n"); ++ retval = mxc_v4l2_g_ctrl(cam, arg); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_S_CTRL ioctl ++ */ ++ case VIDIOC_S_CTRL: { ++ pr_debug(" case VIDIOC_S_CTRL\n"); ++ retval = mxc_v4l2_s_ctrl(cam, arg); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_CROPCAP ioctl ++ */ ++ case VIDIOC_CROPCAP: { ++ struct v4l2_cropcap *cap = arg; ++ pr_debug(" case VIDIOC_CROPCAP\n"); ++ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { ++ retval = -EINVAL; ++ break; ++ } ++ cap->bounds = cam->crop_bounds; ++ cap->defrect = cam->crop_defrect; ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_G_CROP ioctl ++ */ ++ case VIDIOC_G_CROP: { ++ struct v4l2_crop *crop = arg; ++ pr_debug(" case VIDIOC_G_CROP\n"); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { ++ retval = -EINVAL; ++ break; ++ } ++ crop->c = cam->crop_current; ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_S_CROP ioctl ++ */ ++ case VIDIOC_S_CROP: { ++ struct v4l2_crop *crop = arg; ++ struct v4l2_rect *b = &cam->crop_bounds; ++ pr_debug(" case VIDIOC_S_CROP\n"); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { ++ retval = -EINVAL; ++ break; ++ } ++ ++ crop->c.top = (crop->c.top < b->top) ? b->top ++ : crop->c.top; ++ if (crop->c.top > b->top + b->height) ++ crop->c.top = b->top + b->height - 1; ++ if (crop->c.height > b->top + b->height - crop->c.top) ++ crop->c.height = ++ b->top + b->height - crop->c.top; ++ ++ crop->c.left = (crop->c.left < b->left) ? b->left ++ : crop->c.left; ++ if (crop->c.left > b->left + b->width) ++ crop->c.left = b->left + b->width - 1; ++ if (crop->c.width > b->left - crop->c.left + b->width) ++ crop->c.width = ++ b->left - crop->c.left + b->width; ++ ++ crop->c.width -= crop->c.width % 8; ++ crop->c.left -= crop->c.left % 4; ++ cam->crop_current = crop->c; ++ ++ pr_debug(" Cropping Input to ipu size %d x %d\n", ++ cam->crop_current.width, ++ cam->crop_current.height); ++ ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, ++ cam->crop_current.height, ++ cam->csi); ++ ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, ++ cam->crop_current.top, ++ cam->csi); ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_OVERLAY ioctl ++ */ ++ case VIDIOC_OVERLAY: { ++ int *on = arg; ++ pr_debug(" VIDIOC_OVERLAY on=%d\n", *on); ++ if (*on) { ++ cam->overlay_on = true; ++ cam->overlay_pid = current->pid; ++ retval = start_preview(cam); ++ } ++ if (!*on) { ++ retval = stop_preview(cam); ++ cam->overlay_on = false; ++ } ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_G_FBUF ioctl ++ */ ++ case VIDIOC_G_FBUF: { ++ struct v4l2_framebuffer *fb = arg; ++ pr_debug(" case VIDIOC_G_FBUF\n"); ++ *fb = cam->v4l2_fb; ++ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY; ++ break; ++ } ++ ++ /*! ++ * V4l2 VIDIOC_S_FBUF ioctl ++ */ ++ case VIDIOC_S_FBUF: { ++ struct v4l2_framebuffer *fb = arg; ++ pr_debug(" case VIDIOC_S_FBUF\n"); ++ cam->v4l2_fb = *fb; ++ break; ++ } ++ ++ case VIDIOC_G_PARM: { ++ struct v4l2_streamparm *parm = arg; ++ pr_debug(" case VIDIOC_G_PARM\n"); ++ if (cam->sensor) ++ retval = vidioc_int_g_parm(cam->sensor, parm); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ ++ case VIDIOC_S_PARM: { ++ struct v4l2_streamparm *parm = arg; ++ pr_debug(" case VIDIOC_S_PARM\n"); ++ if (cam->sensor) ++ retval = mxc_v4l2_s_param(cam, parm); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ ++ /* linux v4l2 bug, kernel c0485619 user c0405619 */ ++ case VIDIOC_ENUMSTD: { ++ struct v4l2_standard *e = arg; ++ pr_debug(" case VIDIOC_ENUMSTD\n"); ++ *e = cam->standard; ++ break; ++ } ++ ++ case VIDIOC_G_STD: { ++ v4l2_std_id *e = arg; ++ pr_debug(" case VIDIOC_G_STD\n"); ++ if (cam->sensor) ++ retval = mxc_v4l2_g_std(cam, e); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ ++ case VIDIOC_S_STD: { ++ v4l2_std_id *e = arg; ++ pr_debug(" case VIDIOC_S_STD\n"); ++ retval = mxc_v4l2_s_std(cam, *e); ++ ++ break; ++ } ++ ++ case VIDIOC_ENUMOUTPUT: { ++ struct v4l2_output *output = arg; ++ pr_debug(" case VIDIOC_ENUMOUTPUT\n"); ++ if (output->index >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) { ++ retval = -EINVAL; ++ break; ++ } ++ *output = mxc_capture_outputs[output->index]; ++ ++ break; ++ } ++ case VIDIOC_G_OUTPUT: { ++ int *p_output_num = arg; ++ pr_debug(" case VIDIOC_G_OUTPUT\n"); ++ *p_output_num = cam->output; ++ break; ++ } ++ ++ case VIDIOC_S_OUTPUT: { ++ int *p_output_num = arg; ++ pr_debug(" case VIDIOC_S_OUTPUT\n"); ++ if (*p_output_num >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) { ++ retval = -EINVAL; ++ break; ++ } ++ cam->output = *p_output_num; ++ break; ++ } ++ ++ case VIDIOC_ENUMINPUT: { ++ struct v4l2_input *input = arg; ++ pr_debug(" case VIDIOC_ENUMINPUT\n"); ++ if (input->index >= MXC_V4L2_CAPTURE_NUM_INPUTS) { ++ retval = -EINVAL; ++ break; ++ } ++ *input = mxc_capture_inputs[input->index]; ++ break; ++ } ++ ++ case VIDIOC_G_INPUT: { ++ int *index = arg; ++ pr_debug(" case VIDIOC_G_INPUT\n"); ++ *index = cam->current_input; ++ break; ++ } ++ ++ case VIDIOC_S_INPUT: { ++ int *index = arg; ++ pr_debug(" case VIDIOC_S_INPUT\n"); ++ if (*index >= MXC_V4L2_CAPTURE_NUM_INPUTS) { ++ retval = -EINVAL; ++ break; ++ } ++ ++ if (*index == cam->current_input) ++ break; ++ ++ if ((mxc_capture_inputs[cam->current_input].status & ++ V4L2_IN_ST_NO_POWER) == 0) { ++ retval = mxc_streamoff(cam); ++ if (retval) ++ break; ++ mxc_capture_inputs[cam->current_input].status |= ++ V4L2_IN_ST_NO_POWER; ++ } ++ ++ if (strcmp(mxc_capture_inputs[*index].name, "CSI MEM") == 0) { ++#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) ++ retval = csi_enc_select(cam); ++ if (retval) ++ break; ++#endif ++ } else if (strcmp(mxc_capture_inputs[*index].name, ++ "CSI IC MEM") == 0) { ++#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) ++ retval = prp_enc_select(cam); ++ if (retval) ++ break; ++#endif ++ } ++ ++ mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER; ++ cam->current_input = *index; ++ break; ++ } ++ case VIDIOC_ENUM_FMT: { ++ struct v4l2_fmtdesc *f = arg; ++ if (cam->sensor) ++ retval = vidioc_int_enum_fmt_cap(cam->sensor, f); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ case VIDIOC_ENUM_FRAMESIZES: { ++ struct v4l2_frmsizeenum *fsize = arg; ++ if (cam->sensor) ++ retval = vidioc_int_enum_framesizes(cam->sensor, fsize); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ case VIDIOC_DBG_G_CHIP_IDENT: { ++ struct v4l2_dbg_chip_ident *p = arg; ++ p->ident = V4L2_IDENT_NONE; ++ p->revision = 0; ++ if (cam->sensor) ++ retval = vidioc_int_g_chip_ident(cam->sensor, (int *)p); ++ else { ++ pr_err("ERROR: v4l2 capture: slave not found!\n"); ++ retval = -ENODEV; ++ } ++ break; ++ } ++ case VIDIOC_TRY_FMT: ++ case VIDIOC_QUERYCTRL: ++ case VIDIOC_G_TUNER: ++ case VIDIOC_S_TUNER: ++ case VIDIOC_G_FREQUENCY: ++ case VIDIOC_S_FREQUENCY: ++ default: ++ pr_debug(" case default or not supported\n"); ++ retval = -EINVAL; ++ break; ++ } ++ ++ if (ioctlnr != VIDIOC_DQBUF) ++ up(&cam->busy_lock); ++ return retval; ++} ++ ++/* ++ * V4L interface - ioctl function ++ * ++ * @return None ++ */ ++static long mxc_v4l_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ pr_debug("In MVC:mxc_v4l_ioctl\n"); ++ return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl); ++} ++ ++/*! ++ * V4L interface - mmap function ++ * ++ * @param file structure file * ++ * ++ * @param vma structure vm_area_struct * ++ * ++ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error ++ */ ++static int mxc_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct video_device *dev = video_devdata(file); ++ unsigned long size; ++ int res = 0; ++ cam_data *cam = video_get_drvdata(dev); ++ ++ pr_debug("In MVC:mxc_mmap\n"); ++ pr_debug(" pgoff=0x%lx, start=0x%lx, end=0x%lx\n", ++ vma->vm_pgoff, vma->vm_start, vma->vm_end); ++ ++ /* make this _really_ smp-safe */ ++ if (down_interruptible(&cam->busy_lock)) ++ return -EINTR; ++ ++ size = vma->vm_end - vma->vm_start; ++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); ++ ++ if (remap_pfn_range(vma, vma->vm_start, ++ vma->vm_pgoff, size, vma->vm_page_prot)) { ++ pr_err("ERROR: v4l2 capture: mxc_mmap: " ++ "remap_pfn_range failed\n"); ++ res = -ENOBUFS; ++ goto mxc_mmap_exit; ++ } ++ ++ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ ++ ++mxc_mmap_exit: ++ up(&cam->busy_lock); ++ return res; ++} ++ ++/*! ++ * V4L interface - poll function ++ * ++ * @param file structure file * ++ * ++ * @param wait structure poll_table_struct * ++ * ++ * @return status POLLIN | POLLRDNORM ++ */ ++static unsigned int mxc_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct video_device *dev = video_devdata(file); ++ cam_data *cam = video_get_drvdata(dev); ++ wait_queue_head_t *queue = NULL; ++ int res = POLLIN | POLLRDNORM; ++ ++ pr_debug("In MVC:mxc_poll\n"); ++ ++ if (down_interruptible(&cam->busy_lock)) ++ return -EINTR; ++ ++ queue = &cam->enc_queue; ++ poll_wait(file, queue, wait); ++ ++ up(&cam->busy_lock); ++ ++ return res; ++} ++ ++/*! ++ * This structure defines the functions to be called in this driver. ++ */ ++static struct v4l2_file_operations mxc_v4l_fops = { ++ .owner = THIS_MODULE, ++ .open = mxc_v4l_open, ++ .release = mxc_v4l_close, ++ .read = mxc_v4l_read, ++ .ioctl = mxc_v4l_ioctl, ++ .mmap = mxc_mmap, ++ .poll = mxc_poll, ++}; ++ ++static struct video_device mxc_v4l_template = { ++ .name = "Mxc Camera", ++ .fops = &mxc_v4l_fops, ++ .release = video_device_release, ++}; ++ ++/*! ++ * This function can be used to release any platform data on closing. ++ */ ++static void camera_platform_release(struct device *device) ++{ ++} ++ ++/*! ++ * Camera V4l2 callback function. ++ * ++ * @param mask u32 ++ * ++ * @param dev void device structure ++ * ++ * @return status ++ */ ++static void camera_callback(u32 mask, void *dev) ++{ ++ struct mxc_v4l_frame *done_frame; ++ struct mxc_v4l_frame *ready_frame; ++ struct timeval cur_time; ++ ++ cam_data *cam = (cam_data *) dev; ++ if (cam == NULL) ++ return; ++ ++ pr_debug("In MVC:camera_callback\n"); ++ ++ spin_lock(&cam->queue_int_lock); ++ spin_lock(&cam->dqueue_int_lock); ++ if (!list_empty(&cam->working_q)) { ++ do_gettimeofday(&cur_time); ++ ++ done_frame = list_entry(cam->working_q.next, ++ struct mxc_v4l_frame, ++ queue); ++ ++ if (done_frame->ipu_buf_num != cam->local_buf_num) ++ goto next; ++ ++ /* ++ * Set the current time to done frame buffer's ++ * timestamp. Users can use this information to judge ++ * the frame's usage. ++ */ ++ done_frame->buffer.timestamp = cur_time; ++ ++ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { ++ done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE; ++ done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; ++ ++ /* Added to the done queue */ ++ list_del(cam->working_q.next); ++ list_add_tail(&done_frame->queue, &cam->done_q); ++ ++ /* Wake up the queue */ ++ cam->enc_counter++; ++ wake_up_interruptible(&cam->enc_queue); ++ } else ++ pr_err("ERROR: v4l2 capture: camera_callback: " ++ "buffer not queued\n"); ++ } ++ ++next: ++ if (!list_empty(&cam->ready_q)) { ++ ready_frame = list_entry(cam->ready_q.next, ++ struct mxc_v4l_frame, ++ queue); ++ if (cam->enc_update_eba) ++ if (cam->enc_update_eba(cam->ipu, ++ ready_frame->buffer.m.offset, ++ &cam->ping_pong_csi) == 0) { ++ list_del(cam->ready_q.next); ++ list_add_tail(&ready_frame->queue, ++ &cam->working_q); ++ ready_frame->ipu_buf_num = cam->local_buf_num; ++ } ++ } else { ++ if (cam->enc_update_eba) ++ cam->enc_update_eba( ++ cam->ipu, cam->dummy_frame.buffer.m.offset, ++ &cam->ping_pong_csi); ++ } ++ ++ cam->local_buf_num = (cam->local_buf_num == 0) ? 1 : 0; ++ spin_unlock(&cam->dqueue_int_lock); ++ spin_unlock(&cam->queue_int_lock); ++ ++ return; ++} ++ ++/*! ++ * initialize cam_data structure ++ * ++ * @param cam structure cam_data * ++ * ++ * @return status 0 Success ++ */ ++static int init_camera_struct(cam_data *cam, struct platform_device *pdev) ++{ ++ const struct of_device_id *of_id = ++ of_match_device(mxc_v4l2_dt_ids, &pdev->dev); ++ struct device_node *np = pdev->dev.of_node; ++ int ipu_id, csi_id, mclk_source; ++ int ret = 0; ++ ++ pr_debug("In MVC: init_camera_struct\n"); ++ ++ ret = of_property_read_u32(np, "ipu_id", &ipu_id); ++ if (ret) { ++ dev_err(&pdev->dev, "ipu_id missing or invalid\n"); ++ return ret; ++ } ++ ++ ret = of_property_read_u32(np, "csi_id", &csi_id); ++ if (ret) { ++ dev_err(&pdev->dev, "csi_id missing or invalid\n"); ++ return ret; ++ } ++ ++ ret = of_property_read_u32(np, "mclk_source", &mclk_source); ++ if (ret) { ++ dev_err(&pdev->dev, "sensor mclk missing or invalid\n"); ++ return ret; ++ } ++ ++ /* Default everything to 0 */ ++ memset(cam, 0, sizeof(cam_data)); ++ ++ /* get devtype to distinguish if the cpu is imx5 or imx6 ++ * IMX5_V4L2 specify the cpu is imx5 ++ * IMX6_V4L2 specify the cpu is imx6q or imx6sdl ++ */ ++ if (of_id) ++ pdev->id_entry = of_id->data; ++ cam->devtype = pdev->id_entry->driver_data; ++ ++ cam->ipu = ipu_get_soc(ipu_id); ++ if (cam->ipu == NULL) { ++ pr_err("ERROR: v4l2 capture: failed to get ipu\n"); ++ return -EINVAL; ++ } else if (cam->ipu == ERR_PTR(-ENODEV)) { ++ pr_err("ERROR: v4l2 capture: get invalid ipu\n"); ++ return -ENODEV; ++ } ++ ++ init_MUTEX(&cam->param_lock); ++ init_MUTEX(&cam->busy_lock); ++ ++ cam->video_dev = video_device_alloc(); ++ if (cam->video_dev == NULL) ++ return -ENODEV; ++ ++ *(cam->video_dev) = mxc_v4l_template; ++ ++ video_set_drvdata(cam->video_dev, cam); ++ dev_set_drvdata(&pdev->dev, (void *)cam); ++ cam->video_dev->minor = -1; ++ ++ init_waitqueue_head(&cam->enc_queue); ++ init_waitqueue_head(&cam->still_queue); ++ ++ /* setup cropping */ ++ cam->crop_bounds.left = 0; ++ cam->crop_bounds.width = 640; ++ cam->crop_bounds.top = 0; ++ cam->crop_bounds.height = 480; ++ cam->crop_current = cam->crop_defrect = cam->crop_bounds; ++ ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, ++ cam->crop_current.height, cam->csi); ++ ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, ++ cam->crop_current.top, cam->csi); ++ cam->streamparm.parm.capture.capturemode = 0; ++ ++ cam->standard.index = 0; ++ cam->standard.id = V4L2_STD_UNKNOWN; ++ cam->standard.frameperiod.denominator = 30; ++ cam->standard.frameperiod.numerator = 1; ++ cam->standard.framelines = 480; ++ cam->standard_autodetect = true; ++ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; ++ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ cam->overlay_on = false; ++ cam->capture_on = false; ++ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY; ++ ++ cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2; ++ cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2; ++ cam->v2f.fmt.pix.width = 288; ++ cam->v2f.fmt.pix.height = 352; ++ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; ++ cam->win.w.width = 160; ++ cam->win.w.height = 160; ++ cam->win.w.left = 0; ++ cam->win.w.top = 0; ++ ++ cam->ipu_id = ipu_id; ++ cam->csi = csi_id; ++ cam->mclk_source = mclk_source; ++ cam->mclk_on[cam->mclk_source] = false; ++ ++ cam->enc_callback = camera_callback; ++ init_waitqueue_head(&cam->power_queue); ++ spin_lock_init(&cam->queue_int_lock); ++ spin_lock_init(&cam->dqueue_int_lock); ++ ++ cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); ++ cam->self->module = THIS_MODULE; ++ sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi); ++ cam->self->type = v4l2_int_type_master; ++ cam->self->u.master = &mxc_v4l2_master; ++ ++ return 0; ++} ++ ++static ssize_t show_streaming(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *video_dev = container_of(dev, ++ struct video_device, dev); ++ cam_data *cam = video_get_drvdata(video_dev); ++ ++ if (cam->capture_on) ++ return sprintf(buf, "stream on\n"); ++ else ++ return sprintf(buf, "stream off\n"); ++} ++static DEVICE_ATTR(fsl_v4l2_capture_property, S_IRUGO, show_streaming, NULL); ++ ++static ssize_t show_overlay(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *video_dev = container_of(dev, ++ struct video_device, dev); ++ cam_data *cam = video_get_drvdata(video_dev); ++ ++ if (cam->overlay_on) ++ return sprintf(buf, "overlay on\n"); ++ else ++ return sprintf(buf, "overlay off\n"); ++} ++static DEVICE_ATTR(fsl_v4l2_overlay_property, S_IRUGO, show_overlay, NULL); ++ ++static ssize_t show_csi(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *video_dev = container_of(dev, ++ struct video_device, dev); ++ cam_data *cam = video_get_drvdata(video_dev); ++ ++ return sprintf(buf, "ipu%d_csi%d\n", cam->ipu_id, cam->csi); ++} ++static DEVICE_ATTR(fsl_csi_property, S_IRUGO, show_csi, NULL); ++ ++/*! ++ * This function is called to probe the devices if registered. ++ * ++ * @param pdev the device structure used to give information on which device ++ * to probe ++ * ++ * @return The function returns 0 on success and -1 on failure. ++ */ ++static int mxc_v4l2_probe(struct platform_device *pdev) ++{ ++ /* Create cam and initialize it. */ ++ cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL); ++ if (cam == NULL) { ++ pr_err("ERROR: v4l2 capture: failed to register camera\n"); ++ return -1; ++ } ++ ++ init_camera_struct(cam, pdev); ++ pdev->dev.release = camera_platform_release; ++ ++ /* Set up the v4l2 device and register it*/ ++ cam->self->priv = cam; ++ v4l2_int_device_register(cam->self); ++ ++ /* register v4l video device */ ++ if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr) ++ == -1) { ++ kfree(cam); ++ cam = NULL; ++ pr_err("ERROR: v4l2 capture: video_register_device failed\n"); ++ return -1; ++ } ++ pr_debug(" Video device registered: %s #%d\n", ++ cam->video_dev->name, cam->video_dev->minor); ++ ++ if (device_create_file(&cam->video_dev->dev, ++ &dev_attr_fsl_v4l2_capture_property)) ++ dev_err(&pdev->dev, "Error on creating sysfs file" ++ " for capture\n"); ++ ++ if (device_create_file(&cam->video_dev->dev, ++ &dev_attr_fsl_v4l2_overlay_property)) ++ dev_err(&pdev->dev, "Error on creating sysfs file" ++ " for overlay\n"); ++ ++ if (device_create_file(&cam->video_dev->dev, ++ &dev_attr_fsl_csi_property)) ++ dev_err(&pdev->dev, "Error on creating sysfs file" ++ " for csi number\n"); ++ ++ return 0; ++} ++ ++/*! ++ * This function is called to remove the devices when device unregistered. ++ * ++ * @param pdev the device structure used to give information on which device ++ * to remove ++ * ++ * @return The function returns 0 on success and -1 on failure. ++ */ ++static int mxc_v4l2_remove(struct platform_device *pdev) ++{ ++ cam_data *cam = (cam_data *)platform_get_drvdata(pdev); ++ if (cam->open_count) { ++ pr_err("ERROR: v4l2 capture:camera open " ++ "-- setting ops to NULL\n"); ++ return -EBUSY; ++ } else { ++ device_remove_file(&cam->video_dev->dev, ++ &dev_attr_fsl_v4l2_capture_property); ++ device_remove_file(&cam->video_dev->dev, ++ &dev_attr_fsl_v4l2_overlay_property); ++ device_remove_file(&cam->video_dev->dev, ++ &dev_attr_fsl_csi_property); ++ ++ pr_info("V4L2 freeing image input device\n"); ++ v4l2_int_device_unregister(cam->self); ++ video_unregister_device(cam->video_dev); ++ ++ mxc_free_frame_buf(cam); ++ kfree(cam); ++ } ++ ++ pr_info("V4L2 unregistering video\n"); ++ return 0; ++} ++ ++/*! ++ * This function is called to put the sensor in a low power state. ++ * Refer to the document driver-model/driver.txt in the kernel source tree ++ * for more information. ++ * ++ * @param pdev the device structure used to give information on which I2C ++ * to suspend ++ * @param state the power state the device is entering ++ * ++ * @return The function returns 0 on success and -1 on failure. ++ */ ++static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ cam_data *cam = platform_get_drvdata(pdev); ++ ++ pr_debug("In MVC:mxc_v4l2_suspend\n"); ++ ++ if (cam == NULL) ++ return -1; ++ ++ down(&cam->busy_lock); ++ ++ cam->low_power = true; ++ ++ if (cam->overlay_on == true) ++ stop_preview(cam); ++ if ((cam->capture_on == true) && cam->enc_disable) ++ cam->enc_disable(cam); ++ ++ if (cam->sensor && cam->open_count) { ++ if (cam->mclk_on[cam->mclk_source]) { ++ ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, ++ cam->mclk_source, ++ false, false); ++ cam->mclk_on[cam->mclk_source] = false; ++ } ++ vidioc_int_s_power(cam->sensor, 0); ++ } ++ ++ up(&cam->busy_lock); ++ ++ return 0; ++} ++ ++/*! ++ * This function is called to bring the sensor back from a low power state. ++ * Refer to the document driver-model/driver.txt in the kernel source tree ++ * for more information. ++ * ++ * @param pdev the device structure ++ * ++ * @return The function returns 0 on success and -1 on failure ++ */ ++static int mxc_v4l2_resume(struct platform_device *pdev) ++{ ++ cam_data *cam = platform_get_drvdata(pdev); ++ ++ pr_debug("In MVC:mxc_v4l2_resume\n"); ++ ++ if (cam == NULL) ++ return -1; ++ ++ down(&cam->busy_lock); ++ ++ cam->low_power = false; ++ wake_up_interruptible(&cam->power_queue); ++ ++ if (cam->sensor && cam->open_count) { ++ vidioc_int_s_power(cam->sensor, 1); ++ ++ if (!cam->mclk_on[cam->mclk_source]) { ++ ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, ++ cam->mclk_source, ++ true, true); ++ cam->mclk_on[cam->mclk_source] = true; ++ } ++ } ++ ++ if (cam->overlay_on == true) ++ start_preview(cam); ++ if (cam->capture_on == true) ++ mxc_streamon(cam); ++ ++ up(&cam->busy_lock); ++ ++ return 0; ++} ++ ++/*! ++ * This structure contains pointers to the power management callback functions. ++ */ ++static struct platform_driver mxc_v4l2_driver = { ++ .driver = { ++ .name = "mxc_v4l2_capture", ++ .owner = THIS_MODULE, ++ .of_match_table = mxc_v4l2_dt_ids, ++ }, ++ .id_table = imx_v4l2_devtype, ++ .probe = mxc_v4l2_probe, ++ .remove = mxc_v4l2_remove, ++ .suspend = mxc_v4l2_suspend, ++ .resume = mxc_v4l2_resume, ++ .shutdown = NULL, ++}; ++ ++/*! ++ * Initializes the camera driver. ++ */ ++static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) ++{ ++ cam_data *cam = slave->u.slave->master->priv; ++ struct v4l2_format cam_fmt; ++ int i; ++ struct sensor_data *sdata = slave->priv; ++ ++ pr_debug("In MVC: mxc_v4l2_master_attach\n"); ++ pr_debug(" slave.name = %s\n", slave->name); ++ pr_debug(" master.name = %s\n", slave->u.slave->master->name); ++ ++ if (slave == NULL) { ++ pr_err("ERROR: v4l2 capture: slave parameter not valid.\n"); ++ return -1; ++ } ++ ++ if (sdata->csi != cam->csi) { ++ pr_debug("%s: csi doesn't match\n", __func__); ++ return -1; ++ } ++ ++ cam->sensor = slave; ++ ++ if (cam->sensor_index < MXC_SENSOR_NUM) { ++ cam->all_sensors[cam->sensor_index] = slave; ++ cam->sensor_index++; ++ } else { ++ pr_err("ERROR: v4l2 capture: slave number exceeds the maximum.\n"); ++ return -1; ++ } ++ ++ for (i = 0; i < cam->sensor_index; i++) { ++ vidioc_int_dev_exit(cam->all_sensors[i]); ++ vidioc_int_s_power(cam->all_sensors[i], 0); ++ } ++ ++ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); ++ ++ /* Used to detect TV in (type 1) vs. camera (type 0)*/ ++ cam->device_type = cam_fmt.fmt.pix.priv; ++ ++ /* Set the input size to the ipu for this device */ ++ cam->crop_bounds.top = cam->crop_bounds.left = 0; ++ cam->crop_bounds.width = cam_fmt.fmt.pix.width; ++ cam->crop_bounds.height = cam_fmt.fmt.pix.height; ++ ++ /* This also is the max crop size for this device. */ ++ cam->crop_defrect.top = cam->crop_defrect.left = 0; ++ cam->crop_defrect.width = cam_fmt.fmt.pix.width; ++ cam->crop_defrect.height = cam_fmt.fmt.pix.height; ++ ++ /* At this point, this is also the current image size. */ ++ cam->crop_current.top = cam->crop_current.left = 0; ++ cam->crop_current.width = cam_fmt.fmt.pix.width; ++ cam->crop_current.height = cam_fmt.fmt.pix.height; ++ ++ pr_debug("End of %s: v2f pix widthxheight %d x %d\n", ++ __func__, ++ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); ++ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", ++ __func__, ++ cam->crop_bounds.width, cam->crop_bounds.height); ++ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", ++ __func__, ++ cam->crop_defrect.width, cam->crop_defrect.height); ++ pr_debug("End of %s: crop_current widthxheight %d x %d\n", ++ __func__, ++ cam->crop_current.width, cam->crop_current.height); ++ ++ return 0; ++} ++ ++/*! ++ * Disconnects the camera driver. ++ */ ++static void mxc_v4l2_master_detach(struct v4l2_int_device *slave) ++{ ++ unsigned int i; ++ cam_data *cam = slave->u.slave->master->priv; ++ ++ pr_debug("In MVC:mxc_v4l2_master_detach\n"); ++ ++ if (cam->sensor_index > 1) { ++ for (i = 0; i < cam->sensor_index; i++) { ++ if (cam->all_sensors[i] != slave) ++ continue; ++ /* Move all the sensors behind this ++ * sensor one step forward ++ */ ++ for (; i <= MXC_SENSOR_NUM - 2; i++) ++ cam->all_sensors[i] = cam->all_sensors[i+1]; ++ break; ++ } ++ /* Point current sensor to the last one */ ++ cam->sensor = cam->all_sensors[cam->sensor_index - 2]; ++ } else ++ cam->sensor = NULL; ++ ++ cam->sensor_index--; ++ vidioc_int_dev_exit(slave); ++} ++ ++/*! ++ * Entry point for the V4L2 ++ * ++ * @return Error code indicating success or failure ++ */ ++static __init int camera_init(void) ++{ ++ u8 err = 0; ++ ++ pr_debug("In MVC:camera_init\n"); ++ ++ /* Register the device driver structure. */ ++ err = platform_driver_register(&mxc_v4l2_driver); ++ if (err != 0) { ++ pr_err("ERROR: v4l2 capture:camera_init: " ++ "platform_driver_register failed.\n"); ++ return err; ++ } ++ ++ return err; ++} ++ ++/*! ++ * Exit and cleanup for the V4L2 ++ */ ++static void __exit camera_exit(void) ++{ ++ pr_debug("In MVC: camera_exit\n"); ++ ++ platform_driver_unregister(&mxc_v4l2_driver); ++} ++ ++module_init(camera_init); ++module_exit(camera_exit); ++ ++module_param(video_nr, int, 0444); ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras"); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("video"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h linux-openelec/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +--- linux-3.14.36/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,260 @@ ++/* ++ * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/*! ++ * @defgroup MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver ++ */ ++/*! ++ * @file mxc_v4l2_capture.h ++ * ++ * @brief mxc V4L2 capture device API Header file ++ * ++ * It include all the defines for frame operations, also three structure defines ++ * use case ops structure, common v4l2 driver structure and frame structure. ++ * ++ * @ingroup MXC_V4L2_CAPTURE ++ */ ++#ifndef __MXC_V4L2_CAPTURE_H__ ++#define __MXC_V4L2_CAPTURE_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++#define FRAME_NUM 10 ++#define MXC_SENSOR_NUM 2 ++ ++enum imx_v4l2_devtype { ++ IMX5_V4L2, ++ IMX6_V4L2, ++}; ++ ++/*! ++ * v4l2 frame structure. ++ */ ++struct mxc_v4l_frame { ++ u32 paddress; ++ void *vaddress; ++ int count; ++ int width; ++ int height; ++ ++ struct v4l2_buffer buffer; ++ struct list_head queue; ++ int index; ++ union { ++ int ipu_buf_num; ++ int csi_buf_num; ++ }; ++}; ++ ++/* Only for old version. Will go away soon. */ ++typedef struct { ++ u8 clk_mode; ++ u8 ext_vsync; ++ u8 Vsync_pol; ++ u8 Hsync_pol; ++ u8 pixclk_pol; ++ u8 data_pol; ++ u8 data_width; ++ u8 pack_tight; ++ u8 force_eof; ++ u8 data_en_pol; ++ u16 width; ++ u16 height; ++ u32 pixel_fmt; ++ u32 mclk; ++ u16 active_width; ++ u16 active_height; ++} sensor_interface; ++ ++/* Sensor control function */ ++/* Only for old version. Will go away soon. */ ++struct camera_sensor { ++ void (*set_color) (int bright, int saturation, int red, int green, ++ int blue); ++ void (*get_color) (int *bright, int *saturation, int *red, int *green, ++ int *blue); ++ void (*set_ae_mode) (int ae_mode); ++ void (*get_ae_mode) (int *ae_mode); ++ sensor_interface *(*config) (int *frame_rate, int high_quality); ++ sensor_interface *(*reset) (void); ++ void (*get_std) (v4l2_std_id *std); ++ void (*set_std) (v4l2_std_id std); ++ unsigned int csi; ++}; ++ ++/*! ++ * common v4l2 driver structure. ++ */ ++typedef struct _cam_data { ++ struct video_device *video_dev; ++ int device_type; ++ ++ /* semaphore guard against SMP multithreading */ ++ struct semaphore busy_lock; ++ ++ int open_count; ++ ++ /* params lock for this camera */ ++ struct semaphore param_lock; ++ ++ /* Encoder */ ++ struct list_head ready_q; ++ struct list_head done_q; ++ struct list_head working_q; ++ int ping_pong_csi; ++ spinlock_t queue_int_lock; ++ spinlock_t dqueue_int_lock; ++ struct mxc_v4l_frame frame[FRAME_NUM]; ++ struct mxc_v4l_frame dummy_frame; ++ wait_queue_head_t enc_queue; ++ int enc_counter; ++ dma_addr_t rot_enc_bufs[2]; ++ void *rot_enc_bufs_vaddr[2]; ++ int rot_enc_buf_size[2]; ++ enum v4l2_buf_type type; ++ ++ /* still image capture */ ++ wait_queue_head_t still_queue; ++ int still_counter; ++ dma_addr_t still_buf[2]; ++ void *still_buf_vaddr; ++ ++ /* overlay */ ++ struct v4l2_window win; ++ struct v4l2_framebuffer v4l2_fb; ++ dma_addr_t vf_bufs[2]; ++ void *vf_bufs_vaddr[2]; ++ int vf_bufs_size[2]; ++ dma_addr_t rot_vf_bufs[2]; ++ void *rot_vf_bufs_vaddr[2]; ++ int rot_vf_buf_size[2]; ++ bool overlay_active; ++ int output; ++ struct fb_info *overlay_fb; ++ int fb_origin_std; ++ struct work_struct csi_work_struct; ++ ++ /* v4l2 format */ ++ struct v4l2_format v2f; ++ int rotation; /* for IPUv1 and IPUv3, this means encoder rotation */ ++ int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */ ++ struct v4l2_mxc_offset offset; ++ ++ /* V4l2 control bit */ ++ int bright; ++ int hue; ++ int contrast; ++ int saturation; ++ int red; ++ int green; ++ int blue; ++ int ae_mode; ++ ++ /* standard */ ++ struct v4l2_streamparm streamparm; ++ struct v4l2_standard standard; ++ bool standard_autodetect; ++ ++ /* crop */ ++ struct v4l2_rect crop_bounds; ++ struct v4l2_rect crop_defrect; ++ struct v4l2_rect crop_current; ++ ++ int (*enc_update_eba) (struct ipu_soc *ipu, dma_addr_t eba, ++ int *bufferNum); ++ int (*enc_enable) (void *private); ++ int (*enc_disable) (void *private); ++ int (*enc_enable_csi) (void *private); ++ int (*enc_disable_csi) (void *private); ++ void (*enc_callback) (u32 mask, void *dev); ++ int (*vf_start_adc) (void *private); ++ int (*vf_stop_adc) (void *private); ++ int (*vf_start_sdc) (void *private); ++ int (*vf_stop_sdc) (void *private); ++ int (*vf_enable_csi) (void *private); ++ int (*vf_disable_csi) (void *private); ++ int (*csi_start) (void *private); ++ int (*csi_stop) (void *private); ++ ++ /* misc status flag */ ++ bool overlay_on; ++ bool capture_on; ++ int overlay_pid; ++ int capture_pid; ++ bool low_power; ++ wait_queue_head_t power_queue; ++ unsigned int ipu_id; ++ unsigned int csi; ++ u8 mclk_source; ++ bool mclk_on[2]; /* two mclk sources at most now */ ++ int current_input; ++ ++ int local_buf_num; ++ ++ /* camera sensor interface */ ++ struct camera_sensor *cam_sensor; /* old version */ ++ struct v4l2_int_device *all_sensors[MXC_SENSOR_NUM]; ++ struct v4l2_int_device *sensor; ++ struct v4l2_int_device *self; ++ int sensor_index; ++ void *ipu; ++ enum imx_v4l2_devtype devtype; ++ ++ /* v4l2 buf elements related to PxP DMA */ ++ struct completion pxp_tx_cmpl; ++ struct pxp_channel *pxp_chan; ++ struct pxp_config_data pxp_conf; ++ struct dma_async_tx_descriptor *txd; ++ dma_cookie_t cookie; ++ struct scatterlist sg[2]; ++} cam_data; ++ ++struct sensor_data { ++ const struct ov5642_platform_data *platform_data; ++ struct v4l2_int_device *v4l2_int_device; ++ struct i2c_client *i2c_client; ++ struct v4l2_pix_format pix; ++ struct v4l2_captureparm streamcap; ++ bool on; ++ ++ /* control settings */ ++ int brightness; ++ int hue; ++ int contrast; ++ int saturation; ++ int red; ++ int green; ++ int blue; ++ int ae_mode; ++ ++ u32 mclk; ++ u8 mclk_source; ++ struct clk *sensor_clk; ++ int csi; ++ ++ void (*io_init)(void); ++}; ++ ++void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi); ++#endif /* __MXC_V4L2_CAPTURE_H__ */ +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ov5640.c linux-openelec/drivers/media/platform/mxc/capture/ov5640.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ov5640.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ov5640.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,1951 @@ ++/* ++ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++ ++#define OV5640_VOLTAGE_ANALOG 2800000 ++#define OV5640_VOLTAGE_DIGITAL_CORE 1500000 ++#define OV5640_VOLTAGE_DIGITAL_IO 1800000 ++ ++#define MIN_FPS 15 ++#define MAX_FPS 30 ++#define DEFAULT_FPS 30 ++ ++#define OV5640_XCLK_MIN 6000000 ++#define OV5640_XCLK_MAX 24000000 ++ ++#define OV5640_CHIP_ID_HIGH_BYTE 0x300A ++#define OV5640_CHIP_ID_LOW_BYTE 0x300B ++ ++enum ov5640_mode { ++ ov5640_mode_MIN = 0, ++ ov5640_mode_VGA_640_480 = 0, ++ ov5640_mode_QVGA_320_240 = 1, ++ ov5640_mode_NTSC_720_480 = 2, ++ ov5640_mode_PAL_720_576 = 3, ++ ov5640_mode_720P_1280_720 = 4, ++ ov5640_mode_1080P_1920_1080 = 5, ++ ov5640_mode_QSXGA_2592_1944 = 6, ++ ov5640_mode_QCIF_176_144 = 7, ++ ov5640_mode_XGA_1024_768 = 8, ++ ov5640_mode_MAX = 8 ++}; ++ ++enum ov5640_frame_rate { ++ ov5640_15_fps, ++ ov5640_30_fps ++}; ++ ++static int ov5640_framerates[] = { ++ [ov5640_15_fps] = 15, ++ [ov5640_30_fps] = 30, ++}; ++ ++struct reg_value { ++ u16 u16RegAddr; ++ u8 u8Val; ++ u8 u8Mask; ++ u32 u32Delay_ms; ++}; ++ ++struct ov5640_mode_info { ++ enum ov5640_mode mode; ++ u32 width; ++ u32 height; ++ struct reg_value *init_data_ptr; ++ u32 init_data_size; ++}; ++ ++/*! ++ * Maintains the information on the current state of the sesor. ++ */ ++static struct sensor_data ov5640_data; ++static int pwn_gpio, rst_gpio; ++static int prev_sysclk; ++static int AE_Target = 52, night_mode; ++static int prev_HTS; ++static int AE_high, AE_low; ++ ++static struct reg_value ov5640_global_init_setting[] = { ++ {0x3008, 0x42, 0, 0}, ++ {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0}, ++ {0x3034, 0x1a, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, ++ {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, ++ {0x3633, 0x12, 0, 0}, {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, ++ {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, ++ {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, ++ {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, ++ {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, ++ {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, ++ {0x3a13, 0x43, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, ++ {0x3635, 0x13, 0, 0}, {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, ++ {0x3622, 0x01, 0, 0}, {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, ++ {0x3c05, 0x98, 0, 0}, {0x3c06, 0x00, 0, 0}, {0x3c07, 0x07, 0, 0}, ++ {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, ++ {0x3c0b, 0x40, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, ++ {0x3812, 0x00, 0, 0}, {0x3708, 0x64, 0, 0}, {0x4001, 0x02, 0, 0}, ++ {0x4005, 0x1a, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, ++ {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, ++ {0x501f, 0x00, 0, 0}, {0x440e, 0x00, 0, 0}, {0x5000, 0xa7, 0, 0}, ++ {0x3008, 0x02, 0, 0}, ++}; ++ ++static struct reg_value ov5640_init_setting_30fps_VGA[] = { ++ {0x3008, 0x42, 0, 0}, ++ {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0}, ++ {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0}, ++ {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0}, ++ {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, ++ {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, ++ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, ++ {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, ++ {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, ++ {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, ++ {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0}, ++ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0}, ++ {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0}, ++ {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0}, ++ {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, ++ {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, ++ {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0}, ++ {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, {0x5187, 0x09, 0, 0}, ++ {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0}, {0x518a, 0x54, 0, 0}, ++ {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x50, 0, 0}, ++ {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x6c, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x09, 0, 0}, ++ {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0}, {0x5381, 0x1e, 0, 0}, ++ {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0}, {0x5384, 0x0a, 0, 0}, ++ {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0}, {0x5387, 0x7c, 0, 0}, ++ {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0}, {0x538a, 0x01, 0, 0}, ++ {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0}, {0x5301, 0x30, 0, 0}, ++ {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0}, {0x5304, 0x08, 0, 0}, ++ {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0}, {0x5307, 0x16, 0, 0}, ++ {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0}, {0x530b, 0x04, 0, 0}, ++ {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0}, {0x5481, 0x08, 0, 0}, ++ {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0}, {0x5484, 0x51, 0, 0}, ++ {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0}, {0x5487, 0x7d, 0, 0}, ++ {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0}, {0x548a, 0x9a, 0, 0}, ++ {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0}, {0x548d, 0xcd, 0, 0}, ++ {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0}, {0x5490, 0x1d, 0, 0}, ++ {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x10, 0, 0}, ++ {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0}, {0x558b, 0xf8, 0, 0}, ++ {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0}, {0x5802, 0x0f, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0}, {0x5805, 0x26, 0, 0}, ++ {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0}, {0x5808, 0x05, 0, 0}, ++ {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0}, {0x580b, 0x0d, 0, 0}, ++ {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0}, {0x580e, 0x00, 0, 0}, ++ {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0}, {0x5811, 0x09, 0, 0}, ++ {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x00, 0, 0}, ++ {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0}, {0x5817, 0x08, 0, 0}, ++ {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0}, {0x581a, 0x05, 0, 0}, ++ {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0}, {0x581d, 0x0e, 0, 0}, ++ {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0}, {0x5820, 0x11, 0, 0}, ++ {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0}, {0x5823, 0x28, 0, 0}, ++ {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0}, {0x5829, 0x26, 0, 0}, ++ {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0}, {0x582c, 0x24, 0, 0}, ++ {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0}, {0x582f, 0x22, 0, 0}, ++ {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0}, {0x5832, 0x24, 0, 0}, ++ {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0}, {0x5835, 0x22, 0, 0}, ++ {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0}, {0x5838, 0x44, 0, 0}, ++ {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0}, {0x583b, 0x28, 0, 0}, ++ {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0}, {0x5025, 0x00, 0, 0}, ++ {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0}, ++ {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0}, ++ {0x3008, 0x02, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_VGA_640_480[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, ++ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_VGA_640_480[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, ++ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, ++ {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, ++ {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, ++ {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, ++ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, ++ {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, ++ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_PAL_720_576[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, ++ {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_PAL_720_576[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, ++ {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_720P_1280_720[] = { ++ {0x3035, 0x21, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, ++ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, ++ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0}, ++ {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0}, ++ {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0}, ++ {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, ++ {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, ++ {0x3503, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_720P_1280_720[] = { ++ {0x3035, 0x41, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, ++ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, ++ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0}, ++ {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0}, ++ {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0}, ++ {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, ++ {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, ++ {0x3503, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, ++ {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, ++ {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, ++ {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { ++ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, ++ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, ++ {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0}, ++ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, ++ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, ++ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, ++ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, ++ {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++ ++static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { ++ {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, ++ {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xee, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x05, 0, 0}, ++ {0x3807, 0xc3, 0, 0}, {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0}, ++ {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, {0x380c, 0x0b, 0, 0}, ++ {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, ++ {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0}, ++ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0}, ++ {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0}, ++ {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0}, ++ {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0}, ++ {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { ++ {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, ++ {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, ++ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, ++ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, ++ {0x3807, 0x9f, 0, 0}, {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, ++ {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, ++ {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, ++ {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0}, ++ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0}, ++ {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0}, ++ {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0}, ++ {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0}, ++ {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0}, ++}; ++ ++static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = { ++ { ++ {ov5640_mode_VGA_640_480, 640, 480, ++ ov5640_setting_15fps_VGA_640_480, ++ ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, ++ {ov5640_mode_QVGA_320_240, 320, 240, ++ ov5640_setting_15fps_QVGA_320_240, ++ ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, ++ {ov5640_mode_NTSC_720_480, 720, 480, ++ ov5640_setting_15fps_NTSC_720_480, ++ ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, ++ {ov5640_mode_PAL_720_576, 720, 576, ++ ov5640_setting_15fps_PAL_720_576, ++ ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, ++ {ov5640_mode_720P_1280_720, 1280, 720, ++ ov5640_setting_15fps_720P_1280_720, ++ ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, ++ {ov5640_mode_1080P_1920_1080, 1920, 1080, ++ ov5640_setting_15fps_1080P_1920_1080, ++ ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, ++ {ov5640_mode_QSXGA_2592_1944, 2592, 1944, ++ ov5640_setting_15fps_QSXGA_2592_1944, ++ ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, ++ {ov5640_mode_QCIF_176_144, 176, 144, ++ ov5640_setting_15fps_QCIF_176_144, ++ ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, ++ {ov5640_mode_XGA_1024_768, 1024, 768, ++ ov5640_setting_15fps_XGA_1024_768, ++ ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, ++ }, ++ { ++ {ov5640_mode_VGA_640_480, 640, 480, ++ ov5640_setting_30fps_VGA_640_480, ++ ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, ++ {ov5640_mode_QVGA_320_240, 320, 240, ++ ov5640_setting_30fps_QVGA_320_240, ++ ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, ++ {ov5640_mode_NTSC_720_480, 720, 480, ++ ov5640_setting_30fps_NTSC_720_480, ++ ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, ++ {ov5640_mode_PAL_720_576, 720, 576, ++ ov5640_setting_30fps_PAL_720_576, ++ ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, ++ {ov5640_mode_720P_1280_720, 1280, 720, ++ ov5640_setting_30fps_720P_1280_720, ++ ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, ++ {ov5640_mode_1080P_1920_1080, 0, 0, NULL, 0}, ++ {ov5640_mode_QSXGA_2592_1944, 0, 0, NULL, 0}, ++ {ov5640_mode_QCIF_176_144, 176, 144, ++ ov5640_setting_30fps_QCIF_176_144, ++ ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, ++ {ov5640_mode_XGA_1024_768, 1024, 768, ++ ov5640_setting_30fps_XGA_1024_768, ++ ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, ++ }, ++}; ++ ++static struct regulator *io_regulator; ++static struct regulator *core_regulator; ++static struct regulator *analog_regulator; ++ ++static int ov5640_probe(struct i2c_client *adapter, ++ const struct i2c_device_id *device_id); ++static int ov5640_remove(struct i2c_client *client); ++ ++static s32 ov5640_read_reg(u16 reg, u8 *val); ++static s32 ov5640_write_reg(u16 reg, u8 val); ++ ++static const struct i2c_device_id ov5640_id[] = { ++ {"ov5640", 0}, ++ {"ov564x", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, ov5640_id); ++ ++static struct i2c_driver ov5640_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ov5640", ++ }, ++ .probe = ov5640_probe, ++ .remove = ov5640_remove, ++ .id_table = ov5640_id, ++}; ++ ++static inline void ov5640_power_down(int enable) ++{ ++ gpio_set_value(pwn_gpio, enable); ++ ++ msleep(2); ++} ++ ++static inline void ov5640_reset(void) ++{ ++ /* camera reset */ ++ gpio_set_value(rst_gpio, 1); ++ ++ /* camera power down */ ++ gpio_set_value(pwn_gpio, 1); ++ msleep(5); ++ gpio_set_value(pwn_gpio, 0); ++ msleep(5); ++ gpio_set_value(rst_gpio, 0); ++ msleep(1); ++ gpio_set_value(rst_gpio, 1); ++ msleep(5); ++ gpio_set_value(pwn_gpio, 1); ++} ++ ++static int ov5640_regulator_enable(struct device *dev) ++{ ++ int ret = 0; ++ ++ io_regulator = devm_regulator_get(dev, "DOVDD"); ++ if (!IS_ERR(io_regulator)) { ++ regulator_set_voltage(io_regulator, ++ OV5640_VOLTAGE_DIGITAL_IO, ++ OV5640_VOLTAGE_DIGITAL_IO); ++ ret = regulator_enable(io_regulator); ++ if (ret) { ++ dev_err(dev, "set io voltage failed\n"); ++ return ret; ++ } else { ++ dev_dbg(dev, "set io voltage ok\n"); ++ } ++ } else { ++ io_regulator = NULL; ++ dev_warn(dev, "cannot get io voltage\n"); ++ } ++ ++ core_regulator = devm_regulator_get(dev, "DVDD"); ++ if (!IS_ERR(core_regulator)) { ++ regulator_set_voltage(core_regulator, ++ OV5640_VOLTAGE_DIGITAL_CORE, ++ OV5640_VOLTAGE_DIGITAL_CORE); ++ ret = regulator_enable(core_regulator); ++ if (ret) { ++ dev_err(dev, "set core voltage failed\n"); ++ return ret; ++ } else { ++ dev_dbg(dev, "set core voltage ok\n"); ++ } ++ } else { ++ core_regulator = NULL; ++ dev_warn(dev, "cannot get core voltage\n"); ++ } ++ ++ analog_regulator = devm_regulator_get(dev, "AVDD"); ++ if (!IS_ERR(analog_regulator)) { ++ regulator_set_voltage(analog_regulator, ++ OV5640_VOLTAGE_ANALOG, ++ OV5640_VOLTAGE_ANALOG); ++ ret = regulator_enable(analog_regulator); ++ if (ret) { ++ dev_err(dev, "set analog voltage failed\n"); ++ return ret; ++ } else { ++ dev_dbg(dev, "set analog voltage ok\n"); ++ } ++ } else { ++ analog_regulator = NULL; ++ dev_warn(dev, "cannot get analog voltage\n"); ++ } ++ ++ return ret; ++} ++ ++static s32 ov5640_write_reg(u16 reg, u8 val) ++{ ++ u8 au8Buf[3] = {0}; ++ ++ au8Buf[0] = reg >> 8; ++ au8Buf[1] = reg & 0xff; ++ au8Buf[2] = val; ++ ++ if (i2c_master_send(ov5640_data.i2c_client, au8Buf, 3) < 0) { ++ pr_err("%s:write reg error:reg=%x,val=%x\n", ++ __func__, reg, val); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static s32 ov5640_read_reg(u16 reg, u8 *val) ++{ ++ u8 au8RegBuf[2] = {0}; ++ u8 u8RdVal = 0; ++ ++ au8RegBuf[0] = reg >> 8; ++ au8RegBuf[1] = reg & 0xff; ++ ++ if (2 != i2c_master_send(ov5640_data.i2c_client, au8RegBuf, 2)) { ++ pr_err("%s:write reg error:reg=%x\n", ++ __func__, reg); ++ return -1; ++ } ++ ++ if (1 != i2c_master_recv(ov5640_data.i2c_client, &u8RdVal, 1)) { ++ pr_err("%s:read reg error:reg=%x,val=%x\n", ++ __func__, reg, u8RdVal); ++ return -1; ++ } ++ ++ *val = u8RdVal; ++ ++ return u8RdVal; ++} ++ ++static void ov5640_soft_reset(void) ++{ ++ /* sysclk from pad */ ++ ov5640_write_reg(0x3103, 0x11); ++ ++ /* software reset */ ++ ov5640_write_reg(0x3008, 0x82); ++ ++ /* delay at least 5ms */ ++ msleep(10); ++} ++ ++/* set sensor driver capability ++ * 0x302c[7:6] - strength ++ 00 - 1x ++ 01 - 2x ++ 10 - 3x ++ 11 - 4x ++ */ ++static int ov5640_driver_capability(int strength) ++{ ++ u8 temp = 0; ++ ++ if (strength > 4 || strength < 1) { ++ pr_err("The valid driver capability of ov5640 is 1x~4x\n"); ++ return -EINVAL; ++ } ++ ++ ov5640_read_reg(0x302c, &temp); ++ ++ temp &= ~0xc0; /* clear [7:6] */ ++ temp |= ((strength - 1) << 6); /* set [7:6] */ ++ ++ ov5640_write_reg(0x302c, temp); ++ ++ return 0; ++} ++ ++/* calculate sysclk */ ++static int ov5640_get_sysclk(void) ++{ ++ int xvclk = ov5640_data.mclk / 10000; ++ int sysclk; ++ int temp1, temp2; ++ int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv; ++ int sclk_rdiv_map[] = {1, 2, 4, 8}; ++ u8 regval = 0; ++ ++ temp1 = ov5640_read_reg(0x3034, ®val); ++ temp2 = temp1 & 0x0f; ++ if (temp2 == 8 || temp2 == 10) { ++ Bit_div2x = temp2 / 2; ++ } else { ++ pr_err("ov5640: unsupported bit mode %d\n", temp2); ++ return -1; ++ } ++ ++ temp1 = ov5640_read_reg(0x3035, ®val); ++ SysDiv = temp1 >> 4; ++ if (SysDiv == 0) ++ SysDiv = 16; ++ ++ temp1 = ov5640_read_reg(0x3036, ®val); ++ Multiplier = temp1; ++ temp1 = ov5640_read_reg(0x3037, ®val); ++ PreDiv = temp1 & 0x0f; ++ Pll_rdiv = ((temp1 >> 4) & 0x01) + 1; ++ ++ temp1 = ov5640_read_reg(0x3108, ®val); ++ temp2 = temp1 & 0x03; ++ ++ sclk_rdiv = sclk_rdiv_map[temp2]; ++ VCO = xvclk * Multiplier / PreDiv; ++ sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv; ++ ++ return sysclk; ++} ++ ++/* read HTS from register settings */ ++static int ov5640_get_HTS(void) ++{ ++ int HTS; ++ u8 temp = 0; ++ ++ HTS = ov5640_read_reg(0x380c, &temp); ++ HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp); ++ return HTS; ++} ++ ++/* read VTS from register settings */ ++static int ov5640_get_VTS(void) ++{ ++ int VTS; ++ u8 temp = 0; ++ ++ VTS = ov5640_read_reg(0x380e, &temp); ++ VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp); ++ ++ return VTS; ++} ++ ++/* write VTS to registers */ ++static int ov5640_set_VTS(int VTS) ++{ ++ int temp; ++ ++ temp = VTS & 0xff; ++ ov5640_write_reg(0x380f, temp); ++ ++ temp = VTS>>8; ++ ov5640_write_reg(0x380e, temp); ++ return 0; ++} ++ ++/* read shutter, in number of line period */ ++static int ov5640_get_shutter(void) ++{ ++ int shutter; ++ u8 regval; ++ ++ shutter = (ov5640_read_reg(0x03500, ®val) & 0x0f); ++ ++ shutter = (shutter<<8) + ov5640_read_reg(0x3501, ®val); ++ shutter = (shutter<<4) + (ov5640_read_reg(0x3502, ®val)>>4); ++ ++ return shutter; ++} ++ ++/* write shutter, in number of line period */ ++static int ov5640_set_shutter(int shutter) ++{ ++ int temp; ++ ++ shutter = shutter & 0xffff; ++ temp = shutter & 0x0f; ++ temp = temp<<4; ++ ov5640_write_reg(0x3502, temp); ++ ++ temp = shutter & 0xfff; ++ temp = temp>>4; ++ ov5640_write_reg(0x3501, temp); ++ ++ temp = shutter>>12; ++ ov5640_write_reg(0x3500, temp); ++ ++ return 0; ++} ++ ++/* read gain, 16 = 1x */ ++static int ov5640_get_gain16(void) ++{ ++ int gain16; ++ u8 regval; ++ ++ gain16 = ov5640_read_reg(0x350a, ®val) & 0x03; ++ gain16 = (gain16<<8) + ov5640_read_reg(0x350b, ®val); ++ ++ return gain16; ++} ++ ++/* write gain, 16 = 1x */ ++static int ov5640_set_gain16(int gain16) ++{ ++ int temp; ++ ++ gain16 = gain16 & 0x3ff; ++ temp = gain16 & 0xff; ++ ++ ov5640_write_reg(0x350b, temp); ++ temp = gain16>>8; ++ ++ ov5640_write_reg(0x350a, temp); ++ return 0; ++} ++ ++/* get banding filter value */ ++static int ov5640_get_light_freq(void) ++{ ++ int temp, temp1, light_frequency; ++ u8 regval; ++ ++ temp = ov5640_read_reg(0x3c01, ®val); ++ if (temp & 0x80) { ++ /* manual */ ++ temp1 = ov5640_read_reg(0x3c00, ®val); ++ if (temp1 & 0x04) { ++ /* 50Hz */ ++ light_frequency = 50; ++ } else { ++ /* 60Hz */ ++ light_frequency = 60; ++ } ++ } else { ++ /* auto */ ++ temp1 = ov5640_read_reg(0x3c0c, ®val); ++ if (temp1 & 0x01) { ++ /* 50Hz */ ++ light_frequency = 50; ++ } else { ++ /* 60Hz */ ++ light_frequency = 60; ++ } ++ } ++ ++ return light_frequency; ++} ++ ++static void ov5640_set_bandingfilter(void) ++{ ++ int prev_VTS; ++ int band_step60, max_band60, band_step50, max_band50; ++ ++ /* read preview PCLK */ ++ prev_sysclk = ov5640_get_sysclk(); ++ ++ /* read preview HTS */ ++ prev_HTS = ov5640_get_HTS(); ++ ++ /* read preview VTS */ ++ prev_VTS = ov5640_get_VTS(); ++ ++ /* calculate banding filter */ ++ /* 60Hz */ ++ band_step60 = prev_sysclk * 100/prev_HTS * 100/120; ++ ov5640_write_reg(0x3a0a, (band_step60 >> 8)); ++ ov5640_write_reg(0x3a0b, (band_step60 & 0xff)); ++ ++ max_band60 = (int)((prev_VTS-4)/band_step60); ++ ov5640_write_reg(0x3a0d, max_band60); ++ ++ /* 50Hz */ ++ band_step50 = prev_sysclk * 100/prev_HTS; ++ ov5640_write_reg(0x3a08, (band_step50 >> 8)); ++ ov5640_write_reg(0x3a09, (band_step50 & 0xff)); ++ ++ max_band50 = (int)((prev_VTS-4)/band_step50); ++ ov5640_write_reg(0x3a0e, max_band50); ++} ++ ++/* stable in high */ ++static int ov5640_set_AE_target(int target) ++{ ++ int fast_high, fast_low; ++ ++ AE_low = target * 23 / 25; /* 0.92 */ ++ AE_high = target * 27 / 25; /* 1.08 */ ++ fast_high = AE_high << 1; ++ ++ if (fast_high > 255) ++ fast_high = 255; ++ fast_low = AE_low >> 1; ++ ++ ov5640_write_reg(0x3a0f, AE_high); ++ ov5640_write_reg(0x3a10, AE_low); ++ ov5640_write_reg(0x3a1b, AE_high); ++ ov5640_write_reg(0x3a1e, AE_low); ++ ov5640_write_reg(0x3a11, fast_high); ++ ov5640_write_reg(0x3a1f, fast_low); ++ ++ return 0; ++} ++ ++/* enable = 0 to turn off night mode ++ enable = 1 to turn on night mode */ ++static int ov5640_set_night_mode(int enable) ++{ ++ u8 mode; ++ ++ ov5640_read_reg(0x3a00, &mode); ++ ++ if (enable) { ++ /* night mode on */ ++ mode |= 0x04; ++ ov5640_write_reg(0x3a00, mode); ++ } else { ++ /* night mode off */ ++ mode &= 0xfb; ++ ov5640_write_reg(0x3a00, mode); ++ } ++ ++ return 0; ++} ++ ++/* enable = 0 to turn off AEC/AGC ++ enable = 1 to turn on AEC/AGC */ ++void ov5640_turn_on_AE_AG(int enable) ++{ ++ u8 ae_ag_ctrl; ++ ++ ov5640_read_reg(0x3503, &ae_ag_ctrl); ++ if (enable) { ++ /* turn on auto AE/AG */ ++ ae_ag_ctrl = ae_ag_ctrl & ~(0x03); ++ } else { ++ /* turn off AE/AG */ ++ ae_ag_ctrl = ae_ag_ctrl | 0x03; ++ } ++ ov5640_write_reg(0x3503, ae_ag_ctrl); ++} ++ ++/* download ov5640 settings to sensor through i2c */ ++static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize) ++{ ++ register u32 Delay_ms = 0; ++ register u16 RegAddr = 0; ++ register u8 Mask = 0; ++ register u8 Val = 0; ++ u8 RegVal = 0; ++ int i, retval = 0; ++ ++ for (i = 0; i < ArySize; ++i, ++pModeSetting) { ++ Delay_ms = pModeSetting->u32Delay_ms; ++ RegAddr = pModeSetting->u16RegAddr; ++ Val = pModeSetting->u8Val; ++ Mask = pModeSetting->u8Mask; ++ ++ if (Mask) { ++ retval = ov5640_read_reg(RegAddr, &RegVal); ++ if (retval < 0) ++ goto err; ++ ++ RegVal &= ~(u8)Mask; ++ Val &= Mask; ++ Val |= RegVal; ++ } ++ ++ retval = ov5640_write_reg(RegAddr, Val); ++ if (retval < 0) ++ goto err; ++ ++ if (Delay_ms) ++ msleep(Delay_ms); ++ } ++err: ++ return retval; ++} ++ ++static int ov5640_init_mode(void) ++{ ++ struct reg_value *pModeSetting = NULL; ++ int ArySize = 0, retval = 0; ++ ++ ov5640_soft_reset(); ++ ++ pModeSetting = ov5640_global_init_setting; ++ ArySize = ARRAY_SIZE(ov5640_global_init_setting); ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ if (retval < 0) ++ goto err; ++ ++ pModeSetting = ov5640_init_setting_30fps_VGA; ++ ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA); ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ if (retval < 0) ++ goto err; ++ ++ /* change driver capability to 2x according to validation board. ++ * if the image is not stable, please increase the driver strength. ++ */ ++ ov5640_driver_capability(2); ++ ov5640_set_bandingfilter(); ++ ov5640_set_AE_target(AE_Target); ++ ov5640_set_night_mode(night_mode); ++ ++ /* skip 9 vysnc: start capture at 10th vsync */ ++ msleep(300); ++ ++ /* turn off night mode */ ++ night_mode = 0; ++ ov5640_data.pix.width = 640; ++ ov5640_data.pix.height = 480; ++err: ++ return retval; ++} ++ ++/* change to or back to subsampling mode set the mode directly ++ * image size below 1280 * 960 is subsampling mode */ ++static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate, ++ enum ov5640_mode mode) ++{ ++ struct reg_value *pModeSetting = NULL; ++ s32 ArySize = 0; ++ int retval = 0; ++ ++ if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) { ++ pr_err("Wrong ov5640 mode detected!\n"); ++ return -1; ++ } ++ ++ pModeSetting = ov5640_mode_info_data[frame_rate][mode].init_data_ptr; ++ ArySize = ++ ov5640_mode_info_data[frame_rate][mode].init_data_size; ++ ++ ov5640_data.pix.width = ov5640_mode_info_data[frame_rate][mode].width; ++ ov5640_data.pix.height = ov5640_mode_info_data[frame_rate][mode].height; ++ ++ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || ++ pModeSetting == NULL || ArySize == 0) ++ return -EINVAL; ++ ++ /* set ov5640 to subsampling mode */ ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ ++ /* turn on AE AG for subsampling mode, in case the firmware didn't */ ++ ov5640_turn_on_AE_AG(1); ++ ++ /* calculate banding filter */ ++ ov5640_set_bandingfilter(); ++ ++ /* set AE target */ ++ ov5640_set_AE_target(AE_Target); ++ ++ /* update night mode setting */ ++ ov5640_set_night_mode(night_mode); ++ ++ /* skip 9 vysnc: start capture at 10th vsync */ ++ if (mode == ov5640_mode_XGA_1024_768 && frame_rate == ov5640_30_fps) { ++ pr_warning("ov5640: actual frame rate of XGA is 22.5fps\n"); ++ /* 1/22.5 * 9*/ ++ msleep(400); ++ return retval; ++ } ++ ++ if (frame_rate == ov5640_15_fps) { ++ /* 1/15 * 9*/ ++ msleep(600); ++ } else if (frame_rate == ov5640_30_fps) { ++ /* 1/30 * 9*/ ++ msleep(300); ++ } ++ ++ return retval; ++} ++ ++/* change to scaling mode go through exposure calucation ++ * image size above 1280 * 960 is scaling mode */ ++static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate, ++ enum ov5640_mode mode) ++{ ++ int prev_shutter, prev_gain16, average; ++ int cap_shutter, cap_gain16; ++ int cap_sysclk, cap_HTS, cap_VTS; ++ int light_freq, cap_bandfilt, cap_maxband; ++ long cap_gain16_shutter; ++ u8 temp; ++ struct reg_value *pModeSetting = NULL; ++ s32 ArySize = 0; ++ int retval = 0; ++ ++ /* check if the input mode and frame rate is valid */ ++ pModeSetting = ++ ov5640_mode_info_data[frame_rate][mode].init_data_ptr; ++ ArySize = ++ ov5640_mode_info_data[frame_rate][mode].init_data_size; ++ ++ ov5640_data.pix.width = ++ ov5640_mode_info_data[frame_rate][mode].width; ++ ov5640_data.pix.height = ++ ov5640_mode_info_data[frame_rate][mode].height; ++ ++ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || ++ pModeSetting == NULL || ArySize == 0) ++ return -EINVAL; ++ ++ /* read preview shutter */ ++ prev_shutter = ov5640_get_shutter(); ++ ++ /* read preview gain */ ++ prev_gain16 = ov5640_get_gain16(); ++ ++ /* get average */ ++ average = ov5640_read_reg(0x56a1, &temp); ++ ++ /* turn off night mode for capture */ ++ ov5640_set_night_mode(0); ++ ++ /* turn off overlay */ ++ ov5640_write_reg(0x3022, 0x06); ++ ++ /* Write capture setting */ ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ if (retval < 0) ++ goto err; ++ ++ /* turn off AE AG when capture image. */ ++ ov5640_turn_on_AE_AG(0); ++ ++ /* read capture VTS */ ++ cap_VTS = ov5640_get_VTS(); ++ cap_HTS = ov5640_get_HTS(); ++ cap_sysclk = ov5640_get_sysclk(); ++ ++ /* calculate capture banding filter */ ++ light_freq = ov5640_get_light_freq(); ++ if (light_freq == 60) { ++ /* 60Hz */ ++ cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120; ++ } else { ++ /* 50Hz */ ++ cap_bandfilt = cap_sysclk * 100 / cap_HTS; ++ } ++ cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt); ++ /* calculate capture shutter/gain16 */ ++ if (average > AE_low && average < AE_high) { ++ /* in stable range */ ++ cap_gain16_shutter = ++ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk * ++ prev_HTS/cap_HTS * AE_Target / average; ++ } else { ++ cap_gain16_shutter = ++ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk * ++ prev_HTS/cap_HTS; ++ } ++ ++ /* gain to shutter */ ++ if (cap_gain16_shutter < (cap_bandfilt * 16)) { ++ /* shutter < 1/100 */ ++ cap_shutter = cap_gain16_shutter/16; ++ if (cap_shutter < 1) ++ cap_shutter = 1; ++ cap_gain16 = cap_gain16_shutter/cap_shutter; ++ if (cap_gain16 < 16) ++ cap_gain16 = 16; ++ } else { ++ if (cap_gain16_shutter > (cap_bandfilt*cap_maxband*16)) { ++ /* exposure reach max */ ++ cap_shutter = cap_bandfilt*cap_maxband; ++ cap_gain16 = cap_gain16_shutter / cap_shutter; ++ } else { ++ /* 1/100 < cap_shutter =< max, cap_shutter = n/100 */ ++ cap_shutter = ++ ((int)(cap_gain16_shutter/16/cap_bandfilt)) ++ * cap_bandfilt; ++ cap_gain16 = cap_gain16_shutter / cap_shutter; ++ } ++ } ++ ++ /* write capture gain */ ++ ov5640_set_gain16(cap_gain16); ++ ++ /* write capture shutter */ ++ if (cap_shutter > (cap_VTS - 4)) { ++ cap_VTS = cap_shutter + 4; ++ ov5640_set_VTS(cap_VTS); ++ } ++ ++ ov5640_set_shutter(cap_shutter); ++ ++ /* skip 2 vysnc: start capture at 3rd vsync ++ * frame rate of QSXGA and 1080P is 7.5fps: 1/7.5 * 2 ++ */ ++ pr_warning("ov5640: the actual frame rate of %s is 7.5fps\n", ++ mode == ov5640_mode_1080P_1920_1080 ? "1080P" : "QSXGA"); ++ msleep(267); ++err: ++ return retval; ++} ++ ++static int ov5640_change_mode(enum ov5640_frame_rate frame_rate, ++ enum ov5640_mode mode) ++{ ++ int retval = 0; ++ ++ if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) { ++ pr_err("Wrong ov5640 mode detected!\n"); ++ return -1; ++ } ++ ++ if (mode == ov5640_mode_1080P_1920_1080 || ++ mode == ov5640_mode_QSXGA_2592_1944) { ++ /* change to scaling mode go through exposure calucation ++ * image size above 1280 * 960 is scaling mode */ ++ retval = ov5640_change_mode_exposure_calc(frame_rate, mode); ++ } else { ++ /* change back to subsampling modem download firmware directly ++ * image size below 1280 * 960 is subsampling mode */ ++ retval = ov5640_change_mode_direct(frame_rate, mode); ++ } ++ ++ return retval; ++} ++ ++/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ ++ ++static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) ++{ ++ if (s == NULL) { ++ pr_err(" ERROR!! no slave device set!\n"); ++ return -1; ++ } ++ ++ memset(p, 0, sizeof(*p)); ++ p->u.bt656.clock_curr = ov5640_data.mclk; ++ pr_debug(" clock_curr=mclk=%d\n", ov5640_data.mclk); ++ p->if_type = V4L2_IF_TYPE_BT656; ++ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; ++ p->u.bt656.clock_min = OV5640_XCLK_MIN; ++ p->u.bt656.clock_max = OV5640_XCLK_MAX; ++ p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @on: indicates power mode (on or off) ++ * ++ * Turns the power on or off, depending on the value of on and returns the ++ * appropriate error code. ++ */ ++static int ioctl_s_power(struct v4l2_int_device *s, int on) ++{ ++ struct sensor_data *sensor = s->priv; ++ ++ if (on && !sensor->on) { ++ if (io_regulator) ++ if (regulator_enable(io_regulator) != 0) ++ return -EIO; ++ if (core_regulator) ++ if (regulator_enable(core_regulator) != 0) ++ return -EIO; ++ if (analog_regulator) ++ if (regulator_enable(analog_regulator) != 0) ++ return -EIO; ++ /* Make sure power on */ ++ ov5640_power_down(0); ++ } else if (!on && sensor->on) { ++ if (analog_regulator) ++ regulator_disable(analog_regulator); ++ if (core_regulator) ++ regulator_disable(core_regulator); ++ if (io_regulator) ++ regulator_disable(io_regulator); ++ ++ ov5640_power_down(1); ++} ++ ++ sensor->on = on; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure ++ * ++ * Returns the sensor's video CAPTURE parameters. ++ */ ++static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ struct sensor_data *sensor = s->priv; ++ struct v4l2_captureparm *cparm = &a->parm.capture; ++ int ret = 0; ++ ++ switch (a->type) { ++ /* This is the only case currently handled. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ memset(a, 0, sizeof(*a)); ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cparm->capability = sensor->streamcap.capability; ++ cparm->timeperframe = sensor->streamcap.timeperframe; ++ cparm->capturemode = sensor->streamcap.capturemode; ++ ret = 0; ++ break; ++ ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ ret = -EINVAL; ++ break; ++ ++ default: ++ pr_debug(" type is unknown - %d\n", a->type); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure ++ * ++ * Configures the sensor to use the input parameters, if possible. If ++ * not possible, reverts to the old parameters and returns the ++ * appropriate error code. ++ */ ++static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ struct sensor_data *sensor = s->priv; ++ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; ++ u32 tgt_fps; /* target frames per secound */ ++ enum ov5640_frame_rate frame_rate; ++ int ret = 0; ++ ++ /* Make sure power on */ ++ ov5640_power_down(0); ++ ++ switch (a->type) { ++ /* This is the only case currently handled. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ /* Check that the new frame rate is allowed. */ ++ if ((timeperframe->numerator == 0) || ++ (timeperframe->denominator == 0)) { ++ timeperframe->denominator = DEFAULT_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ tgt_fps = timeperframe->denominator / ++ timeperframe->numerator; ++ ++ if (tgt_fps > MAX_FPS) { ++ timeperframe->denominator = MAX_FPS; ++ timeperframe->numerator = 1; ++ } else if (tgt_fps < MIN_FPS) { ++ timeperframe->denominator = MIN_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ /* Actual frame rate we use */ ++ tgt_fps = timeperframe->denominator / ++ timeperframe->numerator; ++ ++ if (tgt_fps == 15) ++ frame_rate = ov5640_15_fps; ++ else if (tgt_fps == 30) ++ frame_rate = ov5640_30_fps; ++ else { ++ pr_err(" The camera frame rate is not supported!\n"); ++ return -EINVAL; ++ } ++ ++ ret = ov5640_change_mode(frame_rate, ++ a->parm.capture.capturemode); ++ if (ret < 0) ++ return ret; ++ ++ sensor->streamcap.timeperframe = *timeperframe; ++ sensor->streamcap.capturemode = a->parm.capture.capturemode; ++ ++ break; ++ ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ pr_debug(" type is not " \ ++ "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", ++ a->type); ++ ret = -EINVAL; ++ break; ++ ++ default: ++ pr_debug(" type is unknown - %d\n", a->type); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap ++ * @s: pointer to standard V4L2 device structure ++ * @f: pointer to standard V4L2 v4l2_format structure ++ * ++ * Returns the sensor's current pixel format in the v4l2_format ++ * parameter. ++ */ ++static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) ++{ ++ struct sensor_data *sensor = s->priv; ++ ++ f->fmt.pix = sensor->pix; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure ++ * ++ * If the requested control is supported, returns the control's current ++ * value from the video_control[] array. Otherwise, returns -EINVAL ++ * if the control is not supported. ++ */ ++static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int ret = 0; ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ vc->value = ov5640_data.brightness; ++ break; ++ case V4L2_CID_HUE: ++ vc->value = ov5640_data.hue; ++ break; ++ case V4L2_CID_CONTRAST: ++ vc->value = ov5640_data.contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ vc->value = ov5640_data.saturation; ++ break; ++ case V4L2_CID_RED_BALANCE: ++ vc->value = ov5640_data.red; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ vc->value = ov5640_data.blue; ++ break; ++ case V4L2_CID_EXPOSURE: ++ vc->value = ov5640_data.ae_mode; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure ++ * ++ * If the requested control is supported, sets the control's current ++ * value in HW (and updates the video_control[] array). Otherwise, ++ * returns -EINVAL if the control is not supported. ++ */ ++static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int retval = 0; ++ ++ pr_debug("In ov5640:ioctl_s_ctrl %d\n", ++ vc->id); ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ break; ++ case V4L2_CID_CONTRAST: ++ break; ++ case V4L2_CID_SATURATION: ++ break; ++ case V4L2_CID_HUE: ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ break; ++ case V4L2_CID_RED_BALANCE: ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ break; ++ case V4L2_CID_GAMMA: ++ break; ++ case V4L2_CID_EXPOSURE: ++ break; ++ case V4L2_CID_AUTOGAIN: ++ break; ++ case V4L2_CID_GAIN: ++ break; ++ case V4L2_CID_HFLIP: ++ break; ++ case V4L2_CID_VFLIP: ++ break; ++ default: ++ retval = -EPERM; ++ break; ++ } ++ ++ return retval; ++} ++ ++/*! ++ * ioctl_enum_framesizes - V4L2 sensor interface handler for ++ * VIDIOC_ENUM_FRAMESIZES ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure ++ * ++ * Return 0 if successful, otherwise -EINVAL. ++ */ ++static int ioctl_enum_framesizes(struct v4l2_int_device *s, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ if (fsize->index > ov5640_mode_MAX) ++ return -EINVAL; ++ ++ fsize->pixel_format = ov5640_data.pix.pixelformat; ++ fsize->discrete.width = ++ max(ov5640_mode_info_data[0][fsize->index].width, ++ ov5640_mode_info_data[1][fsize->index].width); ++ fsize->discrete.height = ++ max(ov5640_mode_info_data[0][fsize->index].height, ++ ov5640_mode_info_data[1][fsize->index].height); ++ return 0; ++} ++ ++/*! ++ * ioctl_enum_frameintervals - V4L2 sensor interface handler for ++ * VIDIOC_ENUM_FRAMEINTERVALS ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure ++ * ++ * Return 0 if successful, otherwise -EINVAL. ++ */ ++static int ioctl_enum_frameintervals(struct v4l2_int_device *s, ++ struct v4l2_frmivalenum *fival) ++{ ++ int i, j, count; ++ ++ if (fival->index < 0 || fival->index > ov5640_mode_MAX) ++ return -EINVAL; ++ ++ if (fival->width == 0 || fival->height == 0 || ++ fival->pixel_format == 0) { ++ pr_warning("Please assign pixelformat, width and height.\n"); ++ return -EINVAL; ++ } ++ ++ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ fival->discrete.numerator = 1; ++ ++ count = 0; ++ for (i = 0; i < ARRAY_SIZE(ov5640_mode_info_data); i++) { ++ for (j = 0; j < (ov5640_mode_MAX + 1); j++) { ++ if (fival->pixel_format == ov5640_data.pix.pixelformat ++ && fival->width == ov5640_mode_info_data[i][j].width ++ && fival->height == ov5640_mode_info_data[i][j].height ++ && ov5640_mode_info_data[i][j].init_data_ptr != NULL) { ++ count++; ++ } ++ if (fival->index == (count - 1)) { ++ fival->discrete.denominator = ++ ov5640_framerates[i]; ++ return 0; ++ } ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++/*! ++ * ioctl_g_chip_ident - V4L2 sensor interface handler for ++ * VIDIOC_DBG_G_CHIP_IDENT ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @id: pointer to int ++ * ++ * Return 0. ++ */ ++static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) ++{ ++ ((struct v4l2_dbg_chip_ident *)id)->match.type = ++ V4L2_CHIP_MATCH_I2C_DRIVER; ++ strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "ov5640_camera"); ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT ++ * @s: pointer to standard V4L2 device structure ++ */ ++static int ioctl_init(struct v4l2_int_device *s) ++{ ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT ++ * @s: pointer to standard V4L2 device structure ++ * @fmt: pointer to standard V4L2 fmt description structure ++ * ++ * Return 0. ++ */ ++static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, ++ struct v4l2_fmtdesc *fmt) ++{ ++ if (fmt->index > ov5640_mode_MAX) ++ return -EINVAL; ++ ++ fmt->pixelformat = ov5640_data.pix.pixelformat; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num ++ * @s: pointer to standard V4L2 device structure ++ * ++ * Initialise the device when slave attaches to the master. ++ */ ++static int ioctl_dev_init(struct v4l2_int_device *s) ++{ ++ struct sensor_data *sensor = s->priv; ++ u32 tgt_xclk; /* target xclk */ ++ u32 tgt_fps; /* target frames per secound */ ++ enum ov5640_frame_rate frame_rate; ++ int ret; ++ ++ ov5640_data.on = true; ++ ++ /* mclk */ ++ tgt_xclk = ov5640_data.mclk; ++ tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX); ++ tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN); ++ ov5640_data.mclk = tgt_xclk; ++ ++ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); ++ clk_set_rate(ov5640_data.sensor_clk, ov5640_data.mclk); ++ ++ /* Default camera frame rate is set in probe */ ++ tgt_fps = sensor->streamcap.timeperframe.denominator / ++ sensor->streamcap.timeperframe.numerator; ++ ++ if (tgt_fps == 15) ++ frame_rate = ov5640_15_fps; ++ else if (tgt_fps == 30) ++ frame_rate = ov5640_30_fps; ++ else ++ return -EINVAL; /* Only support 15fps or 30fps now. */ ++ ++ ret = ov5640_init_mode(); ++ return ret; ++} ++ ++/*! ++ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num ++ * @s: pointer to standard V4L2 device structure ++ * ++ * Delinitialise the device when slave detaches to the master. ++ */ ++static int ioctl_dev_exit(struct v4l2_int_device *s) ++{ ++ return 0; ++} ++ ++/*! ++ * This structure defines all the ioctls for this module and links them to the ++ * enumeration. ++ */ ++static struct v4l2_int_ioctl_desc ov5640_ioctl_desc[] = { ++ { vidioc_int_dev_init_num, ++ (v4l2_int_ioctl_func *)ioctl_dev_init }, ++ { vidioc_int_dev_exit_num, ++ ioctl_dev_exit}, ++ { vidioc_int_s_power_num, ++ (v4l2_int_ioctl_func *)ioctl_s_power }, ++ { vidioc_int_g_ifparm_num, ++ (v4l2_int_ioctl_func *)ioctl_g_ifparm }, ++ { vidioc_int_init_num, ++ (v4l2_int_ioctl_func *)ioctl_init }, ++ { vidioc_int_enum_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, ++ { vidioc_int_g_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, ++ { vidioc_int_g_parm_num, ++ (v4l2_int_ioctl_func *)ioctl_g_parm }, ++ { vidioc_int_s_parm_num, ++ (v4l2_int_ioctl_func *)ioctl_s_parm }, ++ { vidioc_int_g_ctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_g_ctrl }, ++ { vidioc_int_s_ctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_s_ctrl }, ++ { vidioc_int_enum_framesizes_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_framesizes }, ++ { vidioc_int_enum_frameintervals_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_frameintervals }, ++ { vidioc_int_g_chip_ident_num, ++ (v4l2_int_ioctl_func *)ioctl_g_chip_ident }, ++}; ++ ++static struct v4l2_int_slave ov5640_slave = { ++ .ioctls = ov5640_ioctl_desc, ++ .num_ioctls = ARRAY_SIZE(ov5640_ioctl_desc), ++}; ++ ++static struct v4l2_int_device ov5640_int_device = { ++ .module = THIS_MODULE, ++ .name = "ov5640", ++ .type = v4l2_int_type_slave, ++ .u = { ++ .slave = &ov5640_slave, ++ }, ++}; ++ ++/*! ++ * ov5640 I2C probe function ++ * ++ * @param adapter struct i2c_adapter * ++ * @return Error code indicating success or failure ++ */ ++static int ov5640_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct pinctrl *pinctrl; ++ struct device *dev = &client->dev; ++ int retval; ++ u8 chip_id_high, chip_id_low; ++ ++ /* ov5640 pinctrl */ ++ pinctrl = devm_pinctrl_get_select_default(dev); ++ if (IS_ERR(pinctrl)) { ++ dev_err(dev, "setup pinctrl failed\n"); ++ return PTR_ERR(pinctrl); ++ } ++ ++ /* request power down pin */ ++ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); ++ if (!gpio_is_valid(pwn_gpio)) { ++ dev_err(dev, "no sensor pwdn pin available\n"); ++ return -ENODEV; ++ } ++ retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, ++ "ov5640_pwdn"); ++ if (retval < 0) ++ return retval; ++ ++ /* request reset pin */ ++ rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); ++ if (!gpio_is_valid(rst_gpio)) { ++ dev_err(dev, "no sensor reset pin available\n"); ++ return -EINVAL; ++ } ++ retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, ++ "ov5640_reset"); ++ if (retval < 0) ++ return retval; ++ ++ /* Set initial values for the sensor struct. */ ++ memset(&ov5640_data, 0, sizeof(ov5640_data)); ++ ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); ++ if (IS_ERR(ov5640_data.sensor_clk)) { ++ dev_err(dev, "get mclk failed\n"); ++ return PTR_ERR(ov5640_data.sensor_clk); ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "mclk", ++ &ov5640_data.mclk); ++ if (retval) { ++ dev_err(dev, "mclk frequency is invalid\n"); ++ return retval; ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "mclk_source", ++ (u32 *) &(ov5640_data.mclk_source)); ++ if (retval) { ++ dev_err(dev, "mclk_source invalid\n"); ++ return retval; ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "csi_id", ++ &(ov5640_data.csi)); ++ if (retval) { ++ dev_err(dev, "csi_id invalid\n"); ++ return retval; ++ } ++ ++ clk_prepare_enable(ov5640_data.sensor_clk); ++ ++ ov5640_data.io_init = ov5640_reset; ++ ov5640_data.i2c_client = client; ++ ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV; ++ ov5640_data.pix.width = 640; ++ ov5640_data.pix.height = 480; ++ ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | ++ V4L2_CAP_TIMEPERFRAME; ++ ov5640_data.streamcap.capturemode = 0; ++ ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS; ++ ov5640_data.streamcap.timeperframe.numerator = 1; ++ ++ ov5640_regulator_enable(&client->dev); ++ ++ ov5640_reset(); ++ ++ ov5640_power_down(0); ++ ++ retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high); ++ if (retval < 0 || chip_id_high != 0x56) { ++ clk_disable_unprepare(ov5640_data.sensor_clk); ++ pr_warning("camera ov5640 is not found\n"); ++ return -ENODEV; ++ } ++ retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low); ++ if (retval < 0 || chip_id_low != 0x40) { ++ clk_disable_unprepare(ov5640_data.sensor_clk); ++ pr_warning("camera ov5640 is not found\n"); ++ return -ENODEV; ++ } ++ ++ ov5640_power_down(1); ++ ++ clk_disable_unprepare(ov5640_data.sensor_clk); ++ ++ ov5640_int_device.priv = &ov5640_data; ++ retval = v4l2_int_device_register(&ov5640_int_device); ++ ++ pr_info("camera ov5640 is found\n"); ++ return retval; ++} ++ ++/*! ++ * ov5640 I2C detach function ++ * ++ * @param client struct i2c_client * ++ * @return Error code indicating success or failure ++ */ ++static int ov5640_remove(struct i2c_client *client) ++{ ++ v4l2_int_device_unregister(&ov5640_int_device); ++ ++ if (analog_regulator) ++ regulator_disable(analog_regulator); ++ ++ if (core_regulator) ++ regulator_disable(core_regulator); ++ ++ if (io_regulator) ++ regulator_disable(io_regulator); ++ ++ return 0; ++} ++ ++module_i2c_driver(ov5640_i2c_driver); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("OV5640 Camera Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++MODULE_ALIAS("CSI"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ov5640_mipi.c linux-openelec/drivers/media/platform/mxc/capture/ov5640_mipi.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ov5640_mipi.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ov5640_mipi.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,2104 @@ ++/* ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++ ++#define OV5640_VOLTAGE_ANALOG 2800000 ++#define OV5640_VOLTAGE_DIGITAL_CORE 1500000 ++#define OV5640_VOLTAGE_DIGITAL_IO 1800000 ++ ++#define MIN_FPS 15 ++#define MAX_FPS 30 ++#define DEFAULT_FPS 30 ++ ++#define OV5640_XCLK_MIN 6000000 ++#define OV5640_XCLK_MAX 24000000 ++ ++#define OV5640_CHIP_ID_HIGH_BYTE 0x300A ++#define OV5640_CHIP_ID_LOW_BYTE 0x300B ++ ++enum ov5640_mode { ++ ov5640_mode_MIN = 0, ++ ov5640_mode_VGA_640_480 = 0, ++ ov5640_mode_QVGA_320_240 = 1, ++ ov5640_mode_NTSC_720_480 = 2, ++ ov5640_mode_PAL_720_576 = 3, ++ ov5640_mode_720P_1280_720 = 4, ++ ov5640_mode_1080P_1920_1080 = 5, ++ ov5640_mode_QSXGA_2592_1944 = 6, ++ ov5640_mode_QCIF_176_144 = 7, ++ ov5640_mode_XGA_1024_768 = 8, ++ ov5640_mode_MAX = 8, ++ ov5640_mode_INIT = 0xff, /*only for sensor init*/ ++}; ++ ++enum ov5640_frame_rate { ++ ov5640_15_fps, ++ ov5640_30_fps ++}; ++ ++/* image size under 1280 * 960 are SUBSAMPLING ++ * image size upper 1280 * 960 are SCALING ++ */ ++enum ov5640_downsize_mode { ++ SUBSAMPLING, ++ SCALING, ++}; ++ ++struct reg_value { ++ u16 u16RegAddr; ++ u8 u8Val; ++ u8 u8Mask; ++ u32 u32Delay_ms; ++}; ++ ++struct ov5640_mode_info { ++ enum ov5640_mode mode; ++ enum ov5640_downsize_mode dn_mode; ++ u32 width; ++ u32 height; ++ struct reg_value *init_data_ptr; ++ u32 init_data_size; ++}; ++ ++/*! ++ * Maintains the information on the current state of the sesor. ++ */ ++static struct sensor_data ov5640_data; ++static int pwn_gpio, rst_gpio; ++ ++static struct reg_value ov5640_init_setting_30fps_VGA[] = { ++ ++ {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, ++ {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, ++ {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, ++ {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0}, ++ {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, ++ {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, ++ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, ++ {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, ++ {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, ++ {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, ++ {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0}, ++ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0}, ++ {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0}, ++ {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0}, ++ {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, ++ {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, ++ {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, ++ {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0}, ++ {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, ++ {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, ++ {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, ++ {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0}, ++ {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0}, ++ {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0}, ++ {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, ++ {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, ++ {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, ++ {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0}, ++ {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0}, ++ {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0}, ++ {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0}, ++ {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0}, ++ {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0}, ++ {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0}, ++ {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0}, ++ {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0}, ++ {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0}, ++ {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0}, ++ {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0}, ++ {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0}, ++ {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0}, ++ {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0}, ++ {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0}, ++ {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0}, ++ {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0}, ++ {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0}, ++ {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0}, ++ {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0}, ++ {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0}, ++ {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0}, ++ {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0}, ++ {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0}, ++ {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0}, ++ {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0}, ++ {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0}, ++ {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0}, ++ {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0}, ++ {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0}, ++ {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0}, ++ {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0}, ++ {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0}, ++ {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0}, ++ {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0}, ++ {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, ++ {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, ++ {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_VGA_640_480[] = { ++ ++ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_VGA_640_480[] = { ++ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { ++ ++ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, ++ {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, ++ {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { ++ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0}, ++ {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { ++ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { ++ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { ++ {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { ++ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { ++ {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { ++ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_PAL_720_576[] = { ++ {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_PAL_720_576[] = { ++ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_720P_1280_720[] = { ++ {0x3008, 0x42, 0, 0}, ++ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, ++ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, ++ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, ++ {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, ++ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, ++ {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, ++ {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0}, ++ {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_720P_1280_720[] = { ++ {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, ++ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, ++ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, ++ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, ++ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, ++ {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, ++ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, ++ {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, ++ {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { ++ {0x3008, 0x42, 0, 0}, ++ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, ++ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, ++ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, ++ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, ++ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, ++ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0}, ++ {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, ++ {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, ++ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, ++ {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, ++ {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, ++ {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, ++ {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, ++ {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, ++ {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, ++ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, ++ {0x3503, 0, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { ++ {0x3008, 0x42, 0, 0}, ++ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, ++ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, ++ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, ++ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, ++ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, ++ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0}, ++ {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, ++ {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, ++ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, ++ {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, ++ {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, ++ {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, ++ {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, ++ {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, ++ {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, ++ {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, ++ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, ++}; ++ ++static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { ++ {0x4202, 0x0f, 0, 0}, /* stream off the sensor */ ++ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, /*disable flip*/ ++ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, ++ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, ++ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, ++ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, ++ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, ++ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, ++ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, ++ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, ++ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, ++ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, ++ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, ++ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, ++ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, ++ {0x4202, 0x00, 0, 0}, /* stream on the sensor */ ++}; ++ ++static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = { ++ { ++ {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480, ++ ov5640_setting_15fps_VGA_640_480, ++ ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, ++ {ov5640_mode_QVGA_320_240, SUBSAMPLING, 320, 240, ++ ov5640_setting_15fps_QVGA_320_240, ++ ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, ++ {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480, ++ ov5640_setting_15fps_NTSC_720_480, ++ ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, ++ {ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576, ++ ov5640_setting_15fps_PAL_720_576, ++ ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, ++ {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720, ++ ov5640_setting_15fps_720P_1280_720, ++ ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, ++ {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080, ++ ov5640_setting_15fps_1080P_1920_1080, ++ ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, ++ {ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944, ++ ov5640_setting_15fps_QSXGA_2592_1944, ++ ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, ++ {ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144, ++ ov5640_setting_15fps_QCIF_176_144, ++ ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, ++ {ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, ++ ov5640_setting_15fps_XGA_1024_768, ++ ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, ++ }, ++ { ++ {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480, ++ ov5640_setting_30fps_VGA_640_480, ++ ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, ++ {ov5640_mode_QVGA_320_240, SUBSAMPLING, 320, 240, ++ ov5640_setting_30fps_QVGA_320_240, ++ ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, ++ {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480, ++ ov5640_setting_30fps_NTSC_720_480, ++ ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, ++ {ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576, ++ ov5640_setting_30fps_PAL_720_576, ++ ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, ++ {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720, ++ ov5640_setting_30fps_720P_1280_720, ++ ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, ++ {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080, ++ ov5640_setting_30fps_1080P_1920_1080, ++ ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, ++ {ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0}, ++ {ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144, ++ ov5640_setting_30fps_QCIF_176_144, ++ ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, ++ {ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, ++ ov5640_setting_30fps_XGA_1024_768, ++ ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, ++ }, ++}; ++ ++static struct regulator *io_regulator; ++static struct regulator *core_regulator; ++static struct regulator *analog_regulator; ++static struct regulator *gpo_regulator; ++ ++static int ov5640_probe(struct i2c_client *adapter, ++ const struct i2c_device_id *device_id); ++static int ov5640_remove(struct i2c_client *client); ++ ++static s32 ov5640_read_reg(u16 reg, u8 *val); ++static s32 ov5640_write_reg(u16 reg, u8 val); ++ ++static const struct i2c_device_id ov5640_id[] = { ++ {"ov5640_mipi", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, ov5640_id); ++ ++static struct i2c_driver ov5640_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ov5640_mipi", ++ }, ++ .probe = ov5640_probe, ++ .remove = ov5640_remove, ++ .id_table = ov5640_id, ++}; ++ ++static void ov5640_standby(s32 enable) ++{ ++ if (enable) ++ gpio_set_value(pwn_gpio, 1); ++ else ++ gpio_set_value(pwn_gpio, 0); ++ ++ msleep(2); ++} ++ ++static void ov5640_reset(void) ++{ ++ /* camera reset */ ++ gpio_set_value(rst_gpio, 1); ++ ++ /* camera power dowmn */ ++ gpio_set_value(pwn_gpio, 1); ++ msleep(5); ++ ++ gpio_set_value(pwn_gpio, 0); ++ msleep(5); ++ ++ gpio_set_value(rst_gpio, 0); ++ msleep(1); ++ ++ gpio_set_value(rst_gpio, 1); ++ msleep(5); ++ ++ gpio_set_value(pwn_gpio, 1); ++} ++ ++static int ov5640_power_on(struct device *dev) ++{ ++ int ret = 0; ++ ++ io_regulator = devm_regulator_get(dev, "DOVDD"); ++ if (!IS_ERR(io_regulator)) { ++ regulator_set_voltage(io_regulator, ++ OV5640_VOLTAGE_DIGITAL_IO, ++ OV5640_VOLTAGE_DIGITAL_IO); ++ ret = regulator_enable(io_regulator); ++ if (ret) { ++ pr_err("%s:io set voltage error\n", __func__); ++ return ret; ++ } else { ++ dev_dbg(dev, ++ "%s:io set voltage ok\n", __func__); ++ } ++ } else { ++ pr_err("%s: cannot get io voltage error\n", __func__); ++ io_regulator = NULL; ++ } ++ ++ core_regulator = devm_regulator_get(dev, "DVDD"); ++ if (!IS_ERR(core_regulator)) { ++ regulator_set_voltage(core_regulator, ++ OV5640_VOLTAGE_DIGITAL_CORE, ++ OV5640_VOLTAGE_DIGITAL_CORE); ++ ret = regulator_enable(core_regulator); ++ if (ret) { ++ pr_err("%s:core set voltage error\n", __func__); ++ return ret; ++ } else { ++ dev_dbg(dev, ++ "%s:core set voltage ok\n", __func__); ++ } ++ } else { ++ core_regulator = NULL; ++ pr_err("%s: cannot get core voltage error\n", __func__); ++ } ++ ++ analog_regulator = devm_regulator_get(dev, "AVDD"); ++ if (!IS_ERR(analog_regulator)) { ++ regulator_set_voltage(analog_regulator, ++ OV5640_VOLTAGE_ANALOG, ++ OV5640_VOLTAGE_ANALOG); ++ ret = regulator_enable(analog_regulator); ++ if (ret) { ++ pr_err("%s:analog set voltage error\n", ++ __func__); ++ return ret; ++ } else { ++ dev_dbg(dev, ++ "%s:analog set voltage ok\n", __func__); ++ } ++ } else { ++ analog_regulator = NULL; ++ pr_err("%s: cannot get analog voltage error\n", __func__); ++ } ++ ++ return ret; ++} ++ ++static s32 ov5640_write_reg(u16 reg, u8 val) ++{ ++ u8 au8Buf[3] = {0}; ++ ++ au8Buf[0] = reg >> 8; ++ au8Buf[1] = reg & 0xff; ++ au8Buf[2] = val; ++ ++ if (i2c_master_send(ov5640_data.i2c_client, au8Buf, 3) < 0) { ++ pr_err("%s:write reg error:reg=%x,val=%x\n", ++ __func__, reg, val); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static s32 ov5640_read_reg(u16 reg, u8 *val) ++{ ++ u8 au8RegBuf[2] = {0}; ++ u8 u8RdVal = 0; ++ ++ au8RegBuf[0] = reg >> 8; ++ au8RegBuf[1] = reg & 0xff; ++ ++ if (2 != i2c_master_send(ov5640_data.i2c_client, au8RegBuf, 2)) { ++ pr_err("%s:write reg error:reg=%x\n", ++ __func__, reg); ++ return -1; ++ } ++ ++ if (1 != i2c_master_recv(ov5640_data.i2c_client, &u8RdVal, 1)) { ++ pr_err("%s:read reg error:reg=%x,val=%x\n", ++ __func__, reg, u8RdVal); ++ return -1; ++ } ++ ++ *val = u8RdVal; ++ ++ return u8RdVal; ++} ++ ++static int prev_sysclk, prev_HTS; ++static int AE_low, AE_high, AE_Target = 52; ++ ++void OV5640_stream_on(void) ++{ ++ ov5640_write_reg(0x4202, 0x00); ++} ++ ++void OV5640_stream_off(void) ++{ ++ ov5640_write_reg(0x4202, 0x0f); ++} ++ ++ ++int OV5640_get_sysclk(void) ++{ ++ /* calculate sysclk */ ++ int xvclk = ov5640_data.mclk / 10000; ++ int temp1, temp2; ++ int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv; ++ int Bit_div2x = 1, sclk_rdiv, sysclk; ++ u8 temp; ++ ++ int sclk_rdiv_map[] = {1, 2, 4, 8}; ++ ++ temp1 = ov5640_read_reg(0x3034, &temp); ++ temp2 = temp1 & 0x0f; ++ if (temp2 == 8 || temp2 == 10) ++ Bit_div2x = temp2 / 2; ++ ++ temp1 = ov5640_read_reg(0x3035, &temp); ++ SysDiv = temp1>>4; ++ if (SysDiv == 0) ++ SysDiv = 16; ++ ++ temp1 = ov5640_read_reg(0x3036, &temp); ++ Multiplier = temp1; ++ ++ temp1 = ov5640_read_reg(0x3037, &temp); ++ PreDiv = temp1 & 0x0f; ++ Pll_rdiv = ((temp1 >> 4) & 0x01) + 1; ++ ++ temp1 = ov5640_read_reg(0x3108, &temp); ++ temp2 = temp1 & 0x03; ++ sclk_rdiv = sclk_rdiv_map[temp2]; ++ ++ VCO = xvclk * Multiplier / PreDiv; ++ ++ sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv; ++ ++ return sysclk; ++} ++ ++void OV5640_set_night_mode(void) ++{ ++ /* read HTS from register settings */ ++ u8 mode; ++ ++ ov5640_read_reg(0x3a00, &mode); ++ mode &= 0xfb; ++ ov5640_write_reg(0x3a00, mode); ++} ++ ++int OV5640_get_HTS(void) ++{ ++ /* read HTS from register settings */ ++ int HTS; ++ u8 temp; ++ ++ HTS = ov5640_read_reg(0x380c, &temp); ++ HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp); ++ ++ return HTS; ++} ++ ++int OV5640_get_VTS(void) ++{ ++ /* read VTS from register settings */ ++ int VTS; ++ u8 temp; ++ ++ /* total vertical size[15:8] high byte */ ++ VTS = ov5640_read_reg(0x380e, &temp); ++ ++ VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp); ++ ++ return VTS; ++} ++ ++int OV5640_set_VTS(int VTS) ++{ ++ /* write VTS to registers */ ++ int temp; ++ ++ temp = VTS & 0xff; ++ ov5640_write_reg(0x380f, temp); ++ ++ temp = VTS>>8; ++ ov5640_write_reg(0x380e, temp); ++ ++ return 0; ++} ++ ++int OV5640_get_shutter(void) ++{ ++ /* read shutter, in number of line period */ ++ int shutter; ++ u8 temp; ++ ++ shutter = (ov5640_read_reg(0x03500, &temp) & 0x0f); ++ shutter = (shutter<<8) + ov5640_read_reg(0x3501, &temp); ++ shutter = (shutter<<4) + (ov5640_read_reg(0x3502, &temp)>>4); ++ ++ return shutter; ++} ++ ++int OV5640_set_shutter(int shutter) ++{ ++ /* write shutter, in number of line period */ ++ int temp; ++ ++ shutter = shutter & 0xffff; ++ ++ temp = shutter & 0x0f; ++ temp = temp<<4; ++ ov5640_write_reg(0x3502, temp); ++ ++ temp = shutter & 0xfff; ++ temp = temp>>4; ++ ov5640_write_reg(0x3501, temp); ++ ++ temp = shutter>>12; ++ ov5640_write_reg(0x3500, temp); ++ ++ return 0; ++} ++ ++int OV5640_get_gain16(void) ++{ ++ /* read gain, 16 = 1x */ ++ int gain16; ++ u8 temp; ++ ++ gain16 = ov5640_read_reg(0x350a, &temp) & 0x03; ++ gain16 = (gain16<<8) + ov5640_read_reg(0x350b, &temp); ++ ++ return gain16; ++} ++ ++int OV5640_set_gain16(int gain16) ++{ ++ /* write gain, 16 = 1x */ ++ u8 temp; ++ gain16 = gain16 & 0x3ff; ++ ++ temp = gain16 & 0xff; ++ ov5640_write_reg(0x350b, temp); ++ ++ temp = gain16>>8; ++ ov5640_write_reg(0x350a, temp); ++ ++ return 0; ++} ++ ++int OV5640_get_light_freq(void) ++{ ++ /* get banding filter value */ ++ int temp, temp1, light_freq = 0; ++ u8 tmp; ++ ++ temp = ov5640_read_reg(0x3c01, &tmp); ++ ++ if (temp & 0x80) { ++ /* manual */ ++ temp1 = ov5640_read_reg(0x3c00, &tmp); ++ if (temp1 & 0x04) { ++ /* 50Hz */ ++ light_freq = 50; ++ } else { ++ /* 60Hz */ ++ light_freq = 60; ++ } ++ } else { ++ /* auto */ ++ temp1 = ov5640_read_reg(0x3c0c, &tmp); ++ if (temp1 & 0x01) { ++ /* 50Hz */ ++ light_freq = 50; ++ } else { ++ /* 60Hz */ ++ } ++ } ++ return light_freq; ++} ++ ++void OV5640_set_bandingfilter(void) ++{ ++ int prev_VTS; ++ int band_step60, max_band60, band_step50, max_band50; ++ ++ /* read preview PCLK */ ++ prev_sysclk = OV5640_get_sysclk(); ++ /* read preview HTS */ ++ prev_HTS = OV5640_get_HTS(); ++ ++ /* read preview VTS */ ++ prev_VTS = OV5640_get_VTS(); ++ ++ /* calculate banding filter */ ++ /* 60Hz */ ++ band_step60 = prev_sysclk * 100/prev_HTS * 100/120; ++ ov5640_write_reg(0x3a0a, (band_step60 >> 8)); ++ ov5640_write_reg(0x3a0b, (band_step60 & 0xff)); ++ ++ max_band60 = (int)((prev_VTS-4)/band_step60); ++ ov5640_write_reg(0x3a0d, max_band60); ++ ++ /* 50Hz */ ++ band_step50 = prev_sysclk * 100/prev_HTS; ++ ov5640_write_reg(0x3a08, (band_step50 >> 8)); ++ ov5640_write_reg(0x3a09, (band_step50 & 0xff)); ++ ++ max_band50 = (int)((prev_VTS-4)/band_step50); ++ ov5640_write_reg(0x3a0e, max_band50); ++} ++ ++int OV5640_set_AE_target(int target) ++{ ++ /* stable in high */ ++ int fast_high, fast_low; ++ AE_low = target * 23 / 25; /* 0.92 */ ++ AE_high = target * 27 / 25; /* 1.08 */ ++ ++ fast_high = AE_high<<1; ++ if (fast_high > 255) ++ fast_high = 255; ++ ++ fast_low = AE_low >> 1; ++ ++ ov5640_write_reg(0x3a0f, AE_high); ++ ov5640_write_reg(0x3a10, AE_low); ++ ov5640_write_reg(0x3a1b, AE_high); ++ ov5640_write_reg(0x3a1e, AE_low); ++ ov5640_write_reg(0x3a11, fast_high); ++ ov5640_write_reg(0x3a1f, fast_low); ++ ++ return 0; ++} ++ ++void OV5640_turn_on_AE_AG(int enable) ++{ ++ u8 ae_ag_ctrl; ++ ++ ov5640_read_reg(0x3503, &ae_ag_ctrl); ++ if (enable) { ++ /* turn on auto AE/AG */ ++ ae_ag_ctrl = ae_ag_ctrl & ~(0x03); ++ } else { ++ /* turn off AE/AG */ ++ ae_ag_ctrl = ae_ag_ctrl | 0x03; ++ } ++ ov5640_write_reg(0x3503, ae_ag_ctrl); ++} ++ ++bool binning_on(void) ++{ ++ u8 temp; ++ ov5640_read_reg(0x3821, &temp); ++ temp &= 0xfe; ++ if (temp) ++ return true; ++ else ++ return false; ++} ++ ++static void ov5640_set_virtual_channel(int channel) ++{ ++ u8 channel_id; ++ ++ ov5640_read_reg(0x4814, &channel_id); ++ channel_id &= ~(3 << 6); ++ ov5640_write_reg(0x4814, channel_id | (channel << 6)); ++} ++ ++/* download ov5640 settings to sensor through i2c */ ++static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize) ++{ ++ register u32 Delay_ms = 0; ++ register u16 RegAddr = 0; ++ register u8 Mask = 0; ++ register u8 Val = 0; ++ u8 RegVal = 0; ++ int i, retval = 0; ++ ++ for (i = 0; i < ArySize; ++i, ++pModeSetting) { ++ Delay_ms = pModeSetting->u32Delay_ms; ++ RegAddr = pModeSetting->u16RegAddr; ++ Val = pModeSetting->u8Val; ++ Mask = pModeSetting->u8Mask; ++ ++ if (Mask) { ++ retval = ov5640_read_reg(RegAddr, &RegVal); ++ if (retval < 0) ++ goto err; ++ ++ RegVal &= ~(u8)Mask; ++ Val &= Mask; ++ Val |= RegVal; ++ } ++ ++ retval = ov5640_write_reg(RegAddr, Val); ++ if (retval < 0) ++ goto err; ++ ++ if (Delay_ms) ++ msleep(Delay_ms); ++ } ++err: ++ return retval; ++} ++ ++/* sensor changes between scaling and subsampling ++ * go through exposure calcualtion ++ */ ++static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate, ++ enum ov5640_mode mode) ++{ ++ struct reg_value *pModeSetting = NULL; ++ s32 ArySize = 0; ++ u8 average; ++ int prev_shutter, prev_gain16; ++ int cap_shutter, cap_gain16; ++ int cap_sysclk, cap_HTS, cap_VTS; ++ int light_freq, cap_bandfilt, cap_maxband; ++ long cap_gain16_shutter; ++ int retval = 0; ++ ++ /* check if the input mode and frame rate is valid */ ++ pModeSetting = ++ ov5640_mode_info_data[frame_rate][mode].init_data_ptr; ++ ArySize = ++ ov5640_mode_info_data[frame_rate][mode].init_data_size; ++ ++ ov5640_data.pix.width = ++ ov5640_mode_info_data[frame_rate][mode].width; ++ ov5640_data.pix.height = ++ ov5640_mode_info_data[frame_rate][mode].height; ++ ++ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || ++ pModeSetting == NULL || ArySize == 0) ++ return -EINVAL; ++ ++ /* auto focus */ ++ /* OV5640_auto_focus();//if no af function, just skip it */ ++ ++ /* turn off AE/AG */ ++ OV5640_turn_on_AE_AG(0); ++ ++ /* read preview shutter */ ++ prev_shutter = OV5640_get_shutter(); ++ if ((binning_on()) && (mode != ov5640_mode_720P_1280_720) ++ && (mode != ov5640_mode_1080P_1920_1080)) ++ prev_shutter *= 2; ++ ++ /* read preview gain */ ++ prev_gain16 = OV5640_get_gain16(); ++ ++ /* get average */ ++ ov5640_read_reg(0x56a1, &average); ++ ++ /* turn off night mode for capture */ ++ OV5640_set_night_mode(); ++ ++ /* turn off overlay */ ++ /* ov5640_write_reg(0x3022, 0x06);//if no af function, just skip it */ ++ ++ OV5640_stream_off(); ++ ++ /* Write capture setting */ ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ if (retval < 0) ++ goto err; ++ ++ /* read capture VTS */ ++ cap_VTS = OV5640_get_VTS(); ++ cap_HTS = OV5640_get_HTS(); ++ cap_sysclk = OV5640_get_sysclk(); ++ ++ /* calculate capture banding filter */ ++ light_freq = OV5640_get_light_freq(); ++ if (light_freq == 60) { ++ /* 60Hz */ ++ cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120; ++ } else { ++ /* 50Hz */ ++ cap_bandfilt = cap_sysclk * 100 / cap_HTS; ++ } ++ cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt); ++ ++ /* calculate capture shutter/gain16 */ ++ if (average > AE_low && average < AE_high) { ++ /* in stable range */ ++ cap_gain16_shutter = ++ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk ++ * prev_HTS/cap_HTS * AE_Target / average; ++ } else { ++ cap_gain16_shutter = ++ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk ++ * prev_HTS/cap_HTS; ++ } ++ ++ /* gain to shutter */ ++ if (cap_gain16_shutter < (cap_bandfilt * 16)) { ++ /* shutter < 1/100 */ ++ cap_shutter = cap_gain16_shutter/16; ++ if (cap_shutter < 1) ++ cap_shutter = 1; ++ ++ cap_gain16 = cap_gain16_shutter/cap_shutter; ++ if (cap_gain16 < 16) ++ cap_gain16 = 16; ++ } else { ++ if (cap_gain16_shutter > ++ (cap_bandfilt * cap_maxband * 16)) { ++ /* exposure reach max */ ++ cap_shutter = cap_bandfilt * cap_maxband; ++ cap_gain16 = cap_gain16_shutter / cap_shutter; ++ } else { ++ /* 1/100 < (cap_shutter = n/100) =< max */ ++ cap_shutter = ++ ((int) (cap_gain16_shutter/16 / cap_bandfilt)) ++ *cap_bandfilt; ++ cap_gain16 = cap_gain16_shutter / cap_shutter; ++ } ++ } ++ ++ /* write capture gain */ ++ OV5640_set_gain16(cap_gain16); ++ ++ /* write capture shutter */ ++ if (cap_shutter > (cap_VTS - 4)) { ++ cap_VTS = cap_shutter + 4; ++ OV5640_set_VTS(cap_VTS); ++ } ++ OV5640_set_shutter(cap_shutter); ++ ++ OV5640_stream_on(); ++ ++err: ++ return retval; ++} ++ ++/* if sensor changes inside scaling or subsampling ++ * change mode directly ++ * */ ++static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate, ++ enum ov5640_mode mode) ++{ ++ struct reg_value *pModeSetting = NULL; ++ s32 ArySize = 0; ++ int retval = 0; ++ ++ /* check if the input mode and frame rate is valid */ ++ pModeSetting = ++ ov5640_mode_info_data[frame_rate][mode].init_data_ptr; ++ ArySize = ++ ov5640_mode_info_data[frame_rate][mode].init_data_size; ++ ++ ov5640_data.pix.width = ++ ov5640_mode_info_data[frame_rate][mode].width; ++ ov5640_data.pix.height = ++ ov5640_mode_info_data[frame_rate][mode].height; ++ ++ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || ++ pModeSetting == NULL || ArySize == 0) ++ return -EINVAL; ++ ++ /* turn off AE/AG */ ++ OV5640_turn_on_AE_AG(0); ++ ++ OV5640_stream_off(); ++ ++ /* Write capture setting */ ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ if (retval < 0) ++ goto err; ++ ++ OV5640_stream_on(); ++ ++ OV5640_turn_on_AE_AG(1); ++ ++err: ++ return retval; ++} ++ ++static int ov5640_init_mode(enum ov5640_frame_rate frame_rate, ++ enum ov5640_mode mode, enum ov5640_mode orig_mode) ++{ ++ struct reg_value *pModeSetting = NULL; ++ s32 ArySize = 0; ++ int retval = 0; ++ void *mipi_csi2_info; ++ u32 mipi_reg, msec_wait4stable = 0; ++ enum ov5640_downsize_mode dn_mode, orig_dn_mode; ++ ++ if ((mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) ++ && (mode != ov5640_mode_INIT)) { ++ pr_err("Wrong ov5640 mode detected!\n"); ++ return -1; ++ } ++ ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ /* initial mipi dphy */ ++ if (!mipi_csi2_info) { ++ printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", ++ __func__, __FILE__); ++ return -1; ++ } ++ ++ if (!mipi_csi2_get_status(mipi_csi2_info)) ++ mipi_csi2_enable(mipi_csi2_info); ++ ++ if (!mipi_csi2_get_status(mipi_csi2_info)) { ++ pr_err("Can not enable mipi csi2 driver!\n"); ++ return -1; ++ } ++ ++ mipi_csi2_set_lanes(mipi_csi2_info); ++ ++ /*Only reset MIPI CSI2 HW at sensor initialize*/ ++ if (mode == ov5640_mode_INIT) ++ mipi_csi2_reset(mipi_csi2_info); ++ ++ if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_UYVY) ++ mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_YUV422); ++ else if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_RGB565) ++ mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_RGB565); ++ else ++ pr_err("currently this sensor format can not be supported!\n"); ++ ++ dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode; ++ orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode; ++ if (mode == ov5640_mode_INIT) { ++ pModeSetting = ov5640_init_setting_30fps_VGA; ++ ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA); ++ ++ ov5640_data.pix.width = 640; ++ ov5640_data.pix.height = 480; ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ if (retval < 0) ++ goto err; ++ ++ pModeSetting = ov5640_setting_30fps_VGA_640_480; ++ ArySize = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480); ++ retval = ov5640_download_firmware(pModeSetting, ArySize); ++ } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || ++ (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { ++ /* change between subsampling and scaling ++ * go through exposure calucation */ ++ retval = ov5640_change_mode_exposure_calc(frame_rate, mode); ++ } else { ++ /* change inside subsampling or scaling ++ * download firmware directly */ ++ retval = ov5640_change_mode_direct(frame_rate, mode); ++ } ++ ++ if (retval < 0) ++ goto err; ++ ++ OV5640_set_AE_target(AE_Target); ++ OV5640_get_light_freq(); ++ OV5640_set_bandingfilter(); ++ ov5640_set_virtual_channel(ov5640_data.csi); ++ ++ /* add delay to wait for sensor stable */ ++ if (mode == ov5640_mode_QSXGA_2592_1944) { ++ /* dump the first two frames: 1/7.5*2 ++ * the frame rate of QSXGA is 7.5fps */ ++ msec_wait4stable = 267; ++ } else if (frame_rate == ov5640_15_fps) { ++ /* dump the first nine frames: 1/15*9 */ ++ msec_wait4stable = 600; ++ } else if (frame_rate == ov5640_30_fps) { ++ /* dump the first nine frames: 1/30*9 */ ++ msec_wait4stable = 300; ++ } ++ msleep(msec_wait4stable); ++ ++ if (mipi_csi2_info) { ++ unsigned int i; ++ ++ i = 0; ++ ++ /* wait for mipi sensor ready */ ++ mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); ++ while ((mipi_reg == 0x200) && (i < 10)) { ++ mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); ++ i++; ++ msleep(10); ++ } ++ ++ if (i >= 10) { ++ pr_err("mipi csi2 can not receive sensor clk!\n"); ++ return -1; ++ } ++ ++ i = 0; ++ ++ /* wait for mipi stable */ ++ mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); ++ while ((mipi_reg != 0x0) && (i < 10)) { ++ mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); ++ i++; ++ msleep(10); ++ } ++ ++ if (i >= 10) { ++ pr_err("mipi csi2 can not reveive data correctly!\n"); ++ return -1; ++ } ++ } ++err: ++ return retval; ++} ++ ++/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ ++ ++static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) ++{ ++ if (s == NULL) { ++ pr_err(" ERROR!! no slave device set!\n"); ++ return -1; ++ } ++ ++ memset(p, 0, sizeof(*p)); ++ p->u.bt656.clock_curr = ov5640_data.mclk; ++ pr_debug(" clock_curr=mclk=%d\n", ov5640_data.mclk); ++ p->if_type = V4L2_IF_TYPE_BT656; ++ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; ++ p->u.bt656.clock_min = OV5640_XCLK_MIN; ++ p->u.bt656.clock_max = OV5640_XCLK_MAX; ++ p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @on: indicates power mode (on or off) ++ * ++ * Turns the power on or off, depending on the value of on and returns the ++ * appropriate error code. ++ */ ++static int ioctl_s_power(struct v4l2_int_device *s, int on) ++{ ++ struct sensor_data *sensor = s->priv; ++ ++ if (on && !sensor->on) { ++ if (io_regulator) ++ if (regulator_enable(io_regulator) != 0) ++ return -EIO; ++ if (core_regulator) ++ if (regulator_enable(core_regulator) != 0) ++ return -EIO; ++ if (gpo_regulator) ++ if (regulator_enable(gpo_regulator) != 0) ++ return -EIO; ++ if (analog_regulator) ++ if (regulator_enable(analog_regulator) != 0) ++ return -EIO; ++ /* Make sure power on */ ++ ov5640_standby(0); ++ } else if (!on && sensor->on) { ++ if (analog_regulator) ++ regulator_disable(analog_regulator); ++ if (core_regulator) ++ regulator_disable(core_regulator); ++ if (io_regulator) ++ regulator_disable(io_regulator); ++ if (gpo_regulator) ++ regulator_disable(gpo_regulator); ++ ++ ov5640_standby(1); ++ } ++ ++ sensor->on = on; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure ++ * ++ * Returns the sensor's video CAPTURE parameters. ++ */ ++static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ struct sensor_data *sensor = s->priv; ++ struct v4l2_captureparm *cparm = &a->parm.capture; ++ int ret = 0; ++ ++ switch (a->type) { ++ /* This is the only case currently handled. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ memset(a, 0, sizeof(*a)); ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cparm->capability = sensor->streamcap.capability; ++ cparm->timeperframe = sensor->streamcap.timeperframe; ++ cparm->capturemode = sensor->streamcap.capturemode; ++ ret = 0; ++ break; ++ ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ ret = -EINVAL; ++ break; ++ ++ default: ++ pr_debug(" type is unknown - %d\n", a->type); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure ++ * ++ * Configures the sensor to use the input parameters, if possible. If ++ * not possible, reverts to the old parameters and returns the ++ * appropriate error code. ++ */ ++static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ struct sensor_data *sensor = s->priv; ++ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; ++ u32 tgt_fps; /* target frames per secound */ ++ enum ov5640_frame_rate frame_rate; ++ enum ov5640_mode orig_mode; ++ int ret = 0; ++ ++ /* Make sure power on */ ++ ov5640_standby(0); ++ ++ switch (a->type) { ++ /* This is the only case currently handled. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ /* Check that the new frame rate is allowed. */ ++ if ((timeperframe->numerator == 0) || ++ (timeperframe->denominator == 0)) { ++ timeperframe->denominator = DEFAULT_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ tgt_fps = timeperframe->denominator / ++ timeperframe->numerator; ++ ++ if (tgt_fps > MAX_FPS) { ++ timeperframe->denominator = MAX_FPS; ++ timeperframe->numerator = 1; ++ } else if (tgt_fps < MIN_FPS) { ++ timeperframe->denominator = MIN_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ /* Actual frame rate we use */ ++ tgt_fps = timeperframe->denominator / ++ timeperframe->numerator; ++ ++ if (tgt_fps == 15) ++ frame_rate = ov5640_15_fps; ++ else if (tgt_fps == 30) ++ frame_rate = ov5640_30_fps; ++ else { ++ pr_err(" The camera frame rate is not supported!\n"); ++ return -EINVAL; ++ } ++ ++ orig_mode = sensor->streamcap.capturemode; ++ ret = ov5640_init_mode(frame_rate, ++ (u32)a->parm.capture.capturemode, orig_mode); ++ if (ret < 0) ++ return ret; ++ ++ sensor->streamcap.timeperframe = *timeperframe; ++ sensor->streamcap.capturemode = ++ (u32)a->parm.capture.capturemode; ++ ++ break; ++ ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ pr_debug(" type is not " \ ++ "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", ++ a->type); ++ ret = -EINVAL; ++ break; ++ ++ default: ++ pr_debug(" type is unknown - %d\n", a->type); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap ++ * @s: pointer to standard V4L2 device structure ++ * @f: pointer to standard V4L2 v4l2_format structure ++ * ++ * Returns the sensor's current pixel format in the v4l2_format ++ * parameter. ++ */ ++static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) ++{ ++ struct sensor_data *sensor = s->priv; ++ ++ f->fmt.pix = sensor->pix; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure ++ * ++ * If the requested control is supported, returns the control's current ++ * value from the video_control[] array. Otherwise, returns -EINVAL ++ * if the control is not supported. ++ */ ++static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int ret = 0; ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ vc->value = ov5640_data.brightness; ++ break; ++ case V4L2_CID_HUE: ++ vc->value = ov5640_data.hue; ++ break; ++ case V4L2_CID_CONTRAST: ++ vc->value = ov5640_data.contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ vc->value = ov5640_data.saturation; ++ break; ++ case V4L2_CID_RED_BALANCE: ++ vc->value = ov5640_data.red; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ vc->value = ov5640_data.blue; ++ break; ++ case V4L2_CID_EXPOSURE: ++ vc->value = ov5640_data.ae_mode; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure ++ * ++ * If the requested control is supported, sets the control's current ++ * value in HW (and updates the video_control[] array). Otherwise, ++ * returns -EINVAL if the control is not supported. ++ */ ++static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int retval = 0; ++ ++ pr_debug("In ov5640:ioctl_s_ctrl %d\n", ++ vc->id); ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ break; ++ case V4L2_CID_CONTRAST: ++ break; ++ case V4L2_CID_SATURATION: ++ break; ++ case V4L2_CID_HUE: ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ break; ++ case V4L2_CID_RED_BALANCE: ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ break; ++ case V4L2_CID_GAMMA: ++ break; ++ case V4L2_CID_EXPOSURE: ++ break; ++ case V4L2_CID_AUTOGAIN: ++ break; ++ case V4L2_CID_GAIN: ++ break; ++ case V4L2_CID_HFLIP: ++ break; ++ case V4L2_CID_VFLIP: ++ break; ++ default: ++ retval = -EPERM; ++ break; ++ } ++ ++ return retval; ++} ++ ++/*! ++ * ioctl_enum_framesizes - V4L2 sensor interface handler for ++ * VIDIOC_ENUM_FRAMESIZES ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure ++ * ++ * Return 0 if successful, otherwise -EINVAL. ++ */ ++static int ioctl_enum_framesizes(struct v4l2_int_device *s, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ if (fsize->index > ov5640_mode_MAX) ++ return -EINVAL; ++ ++ fsize->pixel_format = ov5640_data.pix.pixelformat; ++ fsize->discrete.width = ++ max(ov5640_mode_info_data[0][fsize->index].width, ++ ov5640_mode_info_data[1][fsize->index].width); ++ fsize->discrete.height = ++ max(ov5640_mode_info_data[0][fsize->index].height, ++ ov5640_mode_info_data[1][fsize->index].height); ++ return 0; ++} ++ ++/*! ++ * ioctl_g_chip_ident - V4L2 sensor interface handler for ++ * VIDIOC_DBG_G_CHIP_IDENT ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @id: pointer to int ++ * ++ * Return 0. ++ */ ++static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) ++{ ++ ((struct v4l2_dbg_chip_ident *)id)->match.type = ++ V4L2_CHIP_MATCH_I2C_DRIVER; ++ strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, ++ "ov5640_mipi_camera"); ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT ++ * @s: pointer to standard V4L2 device structure ++ */ ++static int ioctl_init(struct v4l2_int_device *s) ++{ ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT ++ * @s: pointer to standard V4L2 device structure ++ * @fmt: pointer to standard V4L2 fmt description structure ++ * ++ * Return 0. ++ */ ++static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, ++ struct v4l2_fmtdesc *fmt) ++{ ++ if (fmt->index > ov5640_mode_MAX) ++ return -EINVAL; ++ ++ fmt->pixelformat = ov5640_data.pix.pixelformat; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num ++ * @s: pointer to standard V4L2 device structure ++ * ++ * Initialise the device when slave attaches to the master. ++ */ ++static int ioctl_dev_init(struct v4l2_int_device *s) ++{ ++ struct sensor_data *sensor = s->priv; ++ u32 tgt_xclk; /* target xclk */ ++ u32 tgt_fps; /* target frames per secound */ ++ int ret; ++ enum ov5640_frame_rate frame_rate; ++ void *mipi_csi2_info; ++ ++ ov5640_data.on = true; ++ ++ /* mclk */ ++ tgt_xclk = ov5640_data.mclk; ++ tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX); ++ tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN); ++ ov5640_data.mclk = tgt_xclk; ++ ++ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); ++ ++ /* Default camera frame rate is set in probe */ ++ tgt_fps = sensor->streamcap.timeperframe.denominator / ++ sensor->streamcap.timeperframe.numerator; ++ ++ if (tgt_fps == 15) ++ frame_rate = ov5640_15_fps; ++ else if (tgt_fps == 30) ++ frame_rate = ov5640_30_fps; ++ else ++ return -EINVAL; /* Only support 15fps or 30fps now. */ ++ ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ /* enable mipi csi2 */ ++ if (mipi_csi2_info) ++ mipi_csi2_enable(mipi_csi2_info); ++ else { ++ printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", ++ __func__, __FILE__); ++ return -EPERM; ++ } ++ ++ ret = ov5640_init_mode(frame_rate, ov5640_mode_INIT, ov5640_mode_INIT); ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num ++ * @s: pointer to standard V4L2 device structure ++ * ++ * Delinitialise the device when slave detaches to the master. ++ */ ++static int ioctl_dev_exit(struct v4l2_int_device *s) ++{ ++ void *mipi_csi2_info; ++ ++ mipi_csi2_info = mipi_csi2_get_info(); ++ ++ /* disable mipi csi2 */ ++ if (mipi_csi2_info) ++ if (mipi_csi2_get_status(mipi_csi2_info)) ++ mipi_csi2_disable(mipi_csi2_info); ++ ++ return 0; ++} ++ ++/*! ++ * This structure defines all the ioctls for this module and links them to the ++ * enumeration. ++ */ ++static struct v4l2_int_ioctl_desc ov5640_ioctl_desc[] = { ++ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *) ioctl_dev_init}, ++ {vidioc_int_dev_exit_num, ioctl_dev_exit}, ++ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *) ioctl_s_power}, ++ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *) ioctl_g_ifparm}, ++/* {vidioc_int_g_needs_reset_num, ++ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */ ++/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */ ++ {vidioc_int_init_num, (v4l2_int_ioctl_func *) ioctl_init}, ++ {vidioc_int_enum_fmt_cap_num, ++ (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, ++/* {vidioc_int_try_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */ ++ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, ++/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, */ ++ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, ++ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, ++/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */ ++ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, ++ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, ++ {vidioc_int_enum_framesizes_num, ++ (v4l2_int_ioctl_func *) ioctl_enum_framesizes}, ++ {vidioc_int_g_chip_ident_num, ++ (v4l2_int_ioctl_func *) ioctl_g_chip_ident}, ++}; ++ ++static struct v4l2_int_slave ov5640_slave = { ++ .ioctls = ov5640_ioctl_desc, ++ .num_ioctls = ARRAY_SIZE(ov5640_ioctl_desc), ++}; ++ ++static struct v4l2_int_device ov5640_int_device = { ++ .module = THIS_MODULE, ++ .name = "ov5640", ++ .type = v4l2_int_type_slave, ++ .u = { ++ .slave = &ov5640_slave, ++ }, ++}; ++ ++/*! ++ * ov5640 I2C probe function ++ * ++ * @param adapter struct i2c_adapter * ++ * @return Error code indicating success or failure ++ */ ++static int ov5640_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ int retval; ++ u8 chip_id_high, chip_id_low; ++ ++ /* request power down pin */ ++ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); ++ if (!gpio_is_valid(pwn_gpio)) { ++ dev_warn(dev, "no sensor pwdn pin available"); ++ return -EINVAL; ++ } ++ retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, ++ "ov5640_mipi_pwdn"); ++ if (retval < 0) ++ return retval; ++ ++ /* request reset pin */ ++ rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); ++ if (!gpio_is_valid(rst_gpio)) { ++ dev_warn(dev, "no sensor reset pin available"); ++ return -EINVAL; ++ } ++ retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, ++ "ov5640_mipi_reset"); ++ if (retval < 0) ++ return retval; ++ ++ /* Set initial values for the sensor struct. */ ++ memset(&ov5640_data, 0, sizeof(ov5640_data)); ++ ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); ++ if (IS_ERR(ov5640_data.sensor_clk)) { ++ /* assuming clock enabled by default */ ++ ov5640_data.sensor_clk = NULL; ++ dev_err(dev, "clock-frequency missing or invalid\n"); ++ return PTR_ERR(ov5640_data.sensor_clk); ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "mclk", ++ &(ov5640_data.mclk)); ++ if (retval) { ++ dev_err(dev, "mclk missing or invalid\n"); ++ return retval; ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "mclk_source", ++ (u32 *) &(ov5640_data.mclk_source)); ++ if (retval) { ++ dev_err(dev, "mclk_source missing or invalid\n"); ++ return retval; ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "csi_id", ++ &(ov5640_data.csi)); ++ if (retval) { ++ dev_err(dev, "csi id missing or invalid\n"); ++ return retval; ++ } ++ ++ clk_prepare_enable(ov5640_data.sensor_clk); ++ ++ ov5640_data.io_init = ov5640_reset; ++ ov5640_data.i2c_client = client; ++ ov5640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY; ++ ov5640_data.pix.width = 640; ++ ov5640_data.pix.height = 480; ++ ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | ++ V4L2_CAP_TIMEPERFRAME; ++ ov5640_data.streamcap.capturemode = 0; ++ ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS; ++ ov5640_data.streamcap.timeperframe.numerator = 1; ++ ++ ov5640_power_on(dev); ++ ++ ov5640_reset(); ++ ++ ov5640_standby(0); ++ ++ retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high); ++ if (retval < 0 || chip_id_high != 0x56) { ++ pr_warning("camera ov5640_mipi is not found\n"); ++ clk_disable_unprepare(ov5640_data.sensor_clk); ++ return -ENODEV; ++ } ++ retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low); ++ if (retval < 0 || chip_id_low != 0x40) { ++ pr_warning("camera ov5640_mipi is not found\n"); ++ clk_disable_unprepare(ov5640_data.sensor_clk); ++ return -ENODEV; ++ } ++ ++ ov5640_standby(1); ++ ++ ov5640_int_device.priv = &ov5640_data; ++ retval = v4l2_int_device_register(&ov5640_int_device); ++ ++ clk_disable_unprepare(ov5640_data.sensor_clk); ++ ++ pr_info("camera ov5640_mipi is found\n"); ++ return retval; ++} ++ ++/*! ++ * ov5640 I2C detach function ++ * ++ * @param client struct i2c_client * ++ * @return Error code indicating success or failure ++ */ ++static int ov5640_remove(struct i2c_client *client) ++{ ++ v4l2_int_device_unregister(&ov5640_int_device); ++ ++ if (gpo_regulator) ++ regulator_disable(gpo_regulator); ++ ++ if (analog_regulator) ++ regulator_disable(analog_regulator); ++ ++ if (core_regulator) ++ regulator_disable(core_regulator); ++ ++ if (io_regulator) ++ regulator_disable(io_regulator); ++ ++ return 0; ++} ++ ++/*! ++ * ov5640 init function ++ * Called by insmod ov5640_camera.ko. ++ * ++ * @return Error code indicating success or failure ++ */ ++static __init int ov5640_init(void) ++{ ++ u8 err; ++ ++ err = i2c_add_driver(&ov5640_i2c_driver); ++ if (err != 0) ++ pr_err("%s:driver registration failed, error=%d\n", ++ __func__, err); ++ ++ return err; ++} ++ ++/*! ++ * OV5640 cleanup function ++ * Called on rmmod ov5640_camera.ko ++ * ++ * @return Error code indicating success or failure ++ */ ++static void __exit ov5640_clean(void) ++{ ++ i2c_del_driver(&ov5640_i2c_driver); ++} ++ ++module_init(ov5640_init); ++module_exit(ov5640_clean); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("OV5640 MIPI Camera Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++MODULE_ALIAS("CSI"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/capture/ov5642.c linux-openelec/drivers/media/platform/mxc/capture/ov5642.c +--- linux-3.14.36/drivers/media/platform/mxc/capture/ov5642.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/capture/ov5642.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,4252 @@ ++/* ++ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mxc_v4l2_capture.h" ++ ++#define OV5642_VOLTAGE_ANALOG 2800000 ++#define OV5642_VOLTAGE_DIGITAL_CORE 1500000 ++#define OV5642_VOLTAGE_DIGITAL_IO 1800000 ++ ++#define MIN_FPS 15 ++#define MAX_FPS 30 ++#define DEFAULT_FPS 30 ++ ++#define OV5642_XCLK_MIN 6000000 ++#define OV5642_XCLK_MAX 24000000 ++ ++#define OV5642_CHIP_ID_HIGH_BYTE 0x300A ++#define OV5642_CHIP_ID_LOW_BYTE 0x300B ++ ++enum ov5642_mode { ++ ov5642_mode_MIN = 0, ++ ov5642_mode_VGA_640_480 = 0, ++ ov5642_mode_QVGA_320_240 = 1, ++ ov5642_mode_NTSC_720_480 = 2, ++ ov5642_mode_PAL_720_576 = 3, ++ ov5642_mode_720P_1280_720 = 4, ++ ov5642_mode_1080P_1920_1080 = 5, ++ ov5642_mode_QSXGA_2592_1944 = 6, ++ ov5642_mode_QCIF_176_144 = 7, ++ ov5642_mode_XGA_1024_768 = 8, ++ ov5642_mode_MAX = 8 ++}; ++ ++enum ov5642_frame_rate { ++ ov5642_15_fps, ++ ov5642_30_fps ++}; ++ ++static int ov5642_framerates[] = { ++ [ov5642_15_fps] = 15, ++ [ov5642_30_fps] = 30, ++}; ++ ++struct reg_value { ++ u16 u16RegAddr; ++ u8 u8Val; ++ u8 u8Mask; ++ u32 u32Delay_ms; ++}; ++ ++struct ov5642_mode_info { ++ enum ov5642_mode mode; ++ u32 width; ++ u32 height; ++ struct reg_value *init_data_ptr; ++ u32 init_data_size; ++}; ++ ++/*! ++ * Maintains the information on the current state of the sesor. ++ */ ++static struct sensor_data ov5642_data; ++static int pwn_gpio, rst_gpio; ++ ++static struct reg_value ov5642_rot_none_VGA[] = { ++ {0x3818, 0xc1, 0x00, 0x00}, {0x3621, 0x87, 0x00, 0x00}, ++}; ++ ++static struct reg_value ov5642_rot_vert_flip_VGA[] = { ++ {0x3818, 0x20, 0xbf, 0x00}, {0x3621, 0x20, 0xff, 0x00}, ++}; ++ ++static struct reg_value ov5642_rot_horiz_flip_VGA[] = { ++ {0x3818, 0x81, 0x00, 0x01}, {0x3621, 0xa7, 0x00, 0x00}, ++}; ++ ++static struct reg_value ov5642_rot_180_VGA[] = { ++ {0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00}, ++}; ++ ++ ++static struct reg_value ov5642_rot_none_FULL[] = { ++ {0x3818, 0xc0, 0x00, 0x00}, {0x3621, 0x09, 0x00, 0x00}, ++}; ++ ++static struct reg_value ov5642_rot_vert_flip_FULL[] = { ++ {0x3818, 0x20, 0xbf, 0x01}, {0x3621, 0x20, 0xff, 0x00}, ++}; ++ ++static struct reg_value ov5642_rot_horiz_flip_FULL[] = { ++ {0x3818, 0x80, 0x00, 0x01}, {0x3621, 0x29, 0x00, 0x00}, ++}; ++ ++static struct reg_value ov5642_rot_180_FULL[] = { ++ {0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00}, ++}; ++ ++ ++static struct reg_value ov5642_initial_setting[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, ++ {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c00, 0x04, 0, 0}, {0x3c01, 0x80, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, ++ {0x5182, 0x00, 0, 0}, {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, ++ {0x5001, 0xff, 0, 0}, {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, ++ {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, ++ {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, ++ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, ++ {0x380b, 0xe0, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, ++ {0x501f, 0x00, 0, 0}, {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, ++ {0x350b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, ++ {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x0b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 300}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_QCIF_176_144[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, ++ {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, ++ {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, ++ {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, ++ {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, ++ {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, ++ {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, ++ {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, ++ {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, ++ {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, ++ {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, ++ {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, ++ {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, ++ {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, ++ {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, ++ {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, ++ {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, ++ {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, ++ {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, ++ {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, ++ {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, ++ {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, ++ {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, ++ {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, ++ {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, ++ {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, ++ {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, ++ {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, ++ {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, ++ {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, ++ {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, ++ {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, ++ {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, ++ {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, ++ {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, ++ {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, ++ {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, ++ {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, ++ {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, ++ {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, ++ {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, ++ {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, ++ {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, ++ {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, ++ {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, ++ {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, ++ {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, ++ {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, ++ {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, ++ {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, ++ {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, ++ {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, ++ {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, ++ {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, ++ {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, ++ {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, ++ {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, ++ {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, ++ {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, ++ {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, ++ {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, ++ {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, ++ {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, ++ {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, ++ {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, ++ {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, ++ {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, ++ {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, ++ {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, ++ {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, ++ {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, ++ {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, ++ {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, ++ {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, ++ {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, ++ {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, ++ {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, ++ {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, ++ {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, ++ {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, ++ {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, ++ {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, ++ {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, ++ {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, ++ {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, ++ {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, ++ {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, ++ {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, ++ {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, ++ {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, ++ {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, ++ {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, ++ {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, ++ {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, ++ {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, ++ {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, ++ {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, ++ {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, ++ {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, ++ {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, ++ {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, ++ {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, ++ {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, ++ {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, ++ {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, ++ {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, ++ {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, ++ {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, ++ {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, ++ {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, ++ {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, ++ {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, ++ {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, ++ {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, ++ {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, ++ {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, ++ {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, ++ {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, ++ {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, ++ {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, ++ {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, ++ {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, ++ {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, ++ {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, ++ {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, ++ {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, ++ {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, ++ {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, ++ {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, ++ {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, ++ {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, ++ {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, ++ {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_30fps_QCIF_176_144[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, ++ {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x10, 0, 0}, ++ {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, ++ {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, ++ {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, ++ {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, ++ {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, ++ {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, ++ {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, ++ {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, ++ {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, ++ {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, ++ {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, ++ {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, ++ {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, ++ {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, ++ {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, ++ {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, ++ {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, ++ {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, ++ {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, ++ {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, ++ {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, ++ {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, ++ {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, ++ {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, ++ {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, ++ {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, ++ {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, ++ {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, ++ {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, ++ {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, ++ {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, ++ {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, ++ {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, ++ {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, ++ {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, ++ {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, ++ {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, ++ {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, ++ {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, ++ {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, ++ {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, ++ {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, ++ {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, ++ {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, ++ {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, ++ {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, ++ {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, ++ {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, ++ {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, ++ {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, ++ {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, ++ {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, ++ {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, ++ {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, ++ {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, ++ {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, ++ {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, ++ {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, ++ {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, ++ {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, ++ {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, ++ {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, ++ {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, ++ {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, ++ {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, ++ {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, ++ {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, ++ {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, ++ {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, ++ {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, ++ {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, ++ {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, ++ {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, ++ {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, ++ {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, ++ {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, ++ {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, ++ {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, ++ {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, ++ {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, ++ {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, ++ {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, ++ {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, ++ {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, ++ {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, ++ {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, ++ {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, ++ {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, ++ {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, ++ {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, ++ {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, ++ {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, ++ {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, ++ {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, ++ {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, ++ {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, ++ {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, ++ {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, ++ {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, ++ {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, ++ {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, ++ {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, ++ {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, ++ {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, ++ {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, ++ {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, ++ {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, ++ {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, ++ {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, ++ {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, ++ {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, ++ {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, ++ {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, ++ {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, ++ {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, ++ {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, ++ {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, ++ {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, ++ {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, ++ {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, ++ {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, ++ {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, ++ {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, ++ {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, ++ {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, ++ {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, ++ {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, ++ {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, ++ {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, ++ {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, ++ {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_QSXGA_2592_1944[] = { ++ {0x3503, 0x07, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, ++ {0x3002, 0x00, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, ++ {0x3005, 0xff, 0, 0}, {0x3006, 0xff, 0, 0}, {0x3007, 0x3f, 0, 0}, ++ {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3818, 0xc0, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, ++ {0x3602, 0xe4, 0, 0}, {0x3612, 0xac, 0, 0}, {0x3613, 0x44, 0, 0}, ++ {0x3622, 0x60, 0, 0}, {0x3623, 0x22, 0, 0}, {0x3604, 0x48, 0, 0}, ++ {0x3705, 0xda, 0, 0}, {0x370a, 0x80, 0, 0}, {0x3801, 0x95, 0, 0}, ++ {0x3803, 0x0e, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x20, 0, 0}, ++ {0x3806, 0x07, 0, 0}, {0x3807, 0x98, 0, 0}, {0x3808, 0x0a, 0, 0}, ++ {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, ++ {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, ++ {0x380f, 0xd0, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3815, 0x44, 0, 0}, ++ {0x3824, 0x11, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, ++ {0x3a00, 0x78, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, ++ {0x5682, 0x0a, 0, 0}, {0x5683, 0x20, 0, 0}, {0x5686, 0x07, 0, 0}, ++ {0x5687, 0x98, 0, 0}, {0x5001, 0xff, 0, 0}, {0x589b, 0x00, 0, 0}, ++ {0x589a, 0xc0, 0, 0}, {0x4407, 0x04, 0, 0}, {0x3008, 0x02, 0, 0}, ++ {0x460b, 0x37, 0, 0}, {0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0}, ++ {0x4713, 0x03, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x3815, 0x01, 0, 0}, ++ {0x501f, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0}, ++ {0x5002, 0xe0, 0, 0}, {0x530a, 0x01, 0, 0}, {0x530d, 0x10, 0, 0}, ++ {0x530c, 0x04, 0, 0}, {0x5312, 0x20, 0, 0}, {0x5282, 0x01, 0, 0}, ++ {0x3010, 0x10, 0, 0}, {0x3012, 0x00, 0, 0}, ++}; ++ ++ ++static struct reg_value ov5642_setting_VGA_2_QVGA[] = { ++ {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0xf0, 0, 0}, {0x3815, 0x04, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_QSXGA_2_VGA[] = { ++ {0x3503, 0x00, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, ++ {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, ++ {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x3818, 0xc1, 0, 0}, {0x3621, 0x87, 0, 0}, ++ {0x350c, 0x03, 0, 0}, {0x350d, 0xe8, 0, 0}, {0x3602, 0xfc, 0, 0}, ++ {0x3612, 0xff, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3622, 0x60, 0, 0}, ++ {0x3623, 0x01, 0, 0}, {0x3604, 0x48, 0, 0}, {0x3705, 0xdb, 0, 0}, ++ {0x370a, 0x81, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, ++ {0x3807, 0xc0, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, ++ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x0c, 0, 0}, ++ {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, ++ {0x3810, 0x40, 0, 0}, {0x3815, 0x04, 0, 0}, {0x3824, 0x11, 0, 0}, ++ {0x3825, 0xb4, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, ++ {0x5001, 0xff, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x4407, 0x0c, 0, 0}, {0x3008, 0x02, 0, 0}, {0x460b, 0x37, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4713, 0x02, 0, 0}, ++ {0x471c, 0xd0, 0, 0}, {0x3815, 0x04, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x3002, 0x5c, 0, 0}, {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, ++ {0x530a, 0x01, 0, 0}, {0x530d, 0x0c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x5312, 0x40, 0, 0}, {0x5282, 0x00, 0, 0}, ++ {0x3012, 0x02, 0, 0}, {0x3010, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_30fps_VGA_640_480[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, ++ {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, ++ {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, ++ {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_VGA_640_480[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, ++ {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, ++ {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x07, 0, 0}, ++ {0x380f, 0xd0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, ++}; ++ ++ ++static struct reg_value ov5642_setting_30fps_XGA_1024_768[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, ++ {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, ++ {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, ++ {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, ++ {0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0}, {0x302c, 0x60, 0x60, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_XGA_1024_768[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, ++ {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, ++ {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x07, 0, 0}, ++ {0x380f, 0xd0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, ++ {0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0}, {0x302c, 0x60, 0x60, 0}, ++}; ++ ++static struct reg_value ov5642_setting_30fps_QVGA_320_240[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, ++ {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, ++ {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, ++ {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3808, 0x01, 0, 0}, ++ {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_30fps_NTSC_720_480[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, ++ {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, ++ {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, ++ {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, ++ {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x302c, 0x60, 0x60, 0}, ++}; ++ ++static struct reg_value ov5642_setting_30fps_PAL_720_576[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, ++ {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, ++ {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, ++ {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, ++ {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xd8, 0, 0}, ++ {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, ++ {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, ++ {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, ++ {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, ++ {0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0}, ++ {0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x5682, 0x04, 0, 0}, ++ {0x5683, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0}, ++ {0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, ++ {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, ++ {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, ++ {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, ++ {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, ++ {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, ++ {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, ++ {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, ++ {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, ++ {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, ++ {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, ++ {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, ++ {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, ++ {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x302c, 0x60, 0x60, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_720P_1280_720[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0}, ++ {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0}, ++ {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0}, ++ {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, ++ {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, ++ {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0}, ++ {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, ++ {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, ++ {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0}, ++ {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, ++ {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, ++ {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0}, ++ {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0}, ++ {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0}, ++ {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0}, ++ {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, ++ {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, ++ {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0}, ++ {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0}, ++ {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, ++ {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0}, ++ {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, ++ {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0}, ++ {0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0}, ++ {0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0}, ++ {0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0}, ++ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0}, ++ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0}, ++ {0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0}, ++ {0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0}, ++ {0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0}, ++ {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0}, ++ {0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0}, ++ {0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0}, ++ {0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, ++ {0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, ++ {0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, ++ {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, {0x3010, 0x30, 0, 0}, ++ {0x3a08, 0x06, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x05, 0, 0}, ++ {0x3a0b, 0x50, 0, 0}, {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x07, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_30fps_720P_1280_720[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0}, ++ {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0}, ++ {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0}, ++ {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, ++ {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, ++ {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0}, ++ {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, ++ {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, ++ {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0}, ++ {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, ++ {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, ++ {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0}, ++ {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0}, ++ {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0}, ++ {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0}, ++ {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, ++ {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, ++ {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0}, ++ {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0}, ++ {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, ++ {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0}, ++ {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, ++ {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0}, ++ {0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0}, ++ {0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0}, ++ {0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0}, ++ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, ++ {0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0}, ++ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0}, ++ {0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0}, ++ {0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0}, ++ {0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0}, ++ {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0}, ++ {0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0}, ++ {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0}, ++ {0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0}, ++ {0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, ++ {0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, ++ {0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, ++ {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_1080P_1920_1080[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0}, ++ {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0}, ++ {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0}, ++ {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, ++ {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, ++ {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0}, ++ {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, ++ {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, ++ {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0}, ++ {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, ++ {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, ++ {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0}, ++ {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0}, ++ {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0}, ++ {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0}, ++ {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0}, ++ {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, ++ {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, ++ {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0}, ++ {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, ++ {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0}, ++ {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0}, ++ {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, ++ {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, ++ {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, ++ {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, ++ {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0}, ++ {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, ++ {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, ++ {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, ++ {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, ++ {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, ++ {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, ++ {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, ++ {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, ++ {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, ++ {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, ++ {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, ++ {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, ++ {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, ++ {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, ++ {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, ++ {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, ++ {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, ++ {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, ++ {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, ++ {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, ++ {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, ++ {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, ++ {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, ++ {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, ++ {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, ++ {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, ++ {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, ++ {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, ++ {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, ++ {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, ++ {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, ++ {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, ++ {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, ++ {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, ++ {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, ++ {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, ++ {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, ++ {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, ++ {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, ++ {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, ++ {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, ++ {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, ++ {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, ++ {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, ++ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, ++ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, ++ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, ++ {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, ++ {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, ++ {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, ++ {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, ++ {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, ++ {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, ++ {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, ++ {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, ++ {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, ++ {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, ++ {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, ++ {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, ++ {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, ++ {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, ++ {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, ++ {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, ++ {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, ++ {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, ++ {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, ++ {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, ++ {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, ++ {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, ++ {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, ++ {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, ++ {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, ++ {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, ++ {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, ++ {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, ++ {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, ++ {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, ++ {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, ++ {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, ++ {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, ++ {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, ++ {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, ++ {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, ++ {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, ++ {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, ++ {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, ++ {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, ++ {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, ++ {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, ++ {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, ++ {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, ++ {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, ++ {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, ++ {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, ++ {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, ++ {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, ++ {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, ++ {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, ++ {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, ++ {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, ++ {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, ++ {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, ++ {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, ++ {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, ++ {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, ++ {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, ++ {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, ++ {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, ++ {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x07, 0, 0}, ++ {0x350c, 0x04, 0, 0}, {0x350d, 0x58, 0, 0}, {0x3801, 0x8a, 0, 0}, ++ {0x3803, 0x0a, 0, 0}, {0x3804, 0x07, 0, 0}, {0x3805, 0x80, 0, 0}, ++ {0x3806, 0x04, 0, 0}, {0x3807, 0x39, 0, 0}, {0x3808, 0x07, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, ++ {0x380c, 0x09, 0, 0}, {0x380d, 0xd6, 0, 0}, {0x380e, 0x04, 0, 0}, ++ {0x380f, 0x58, 0, 0}, {0x381c, 0x11, 0, 0}, {0x381d, 0xba, 0, 0}, ++ {0x381e, 0x04, 0, 0}, {0x381f, 0x48, 0, 0}, {0x3820, 0x04, 0, 0}, ++ {0x3821, 0x18, 0, 0}, {0x3a08, 0x14, 0, 0}, {0x3a09, 0xe0, 0, 0}, ++ {0x3a0a, 0x11, 0, 0}, {0x3a0b, 0x60, 0, 0}, {0x3a0d, 0x04, 0, 0}, ++ {0x3a0e, 0x03, 0, 0}, {0x5682, 0x07, 0, 0}, {0x5683, 0x60, 0, 0}, ++ {0x5686, 0x04, 0, 0}, {0x5687, 0x1c, 0, 0}, {0x5001, 0x7f, 0, 0}, ++ {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0}, ++ {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x471d, 0x05, 0, 0}, ++ {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, {0x501f, 0x00, 0, 0}, ++ {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0}, ++ {0x5002, 0xe0, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_QVGA_320_240[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, ++ {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, ++ {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, ++ {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, ++ {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, ++ {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, ++ {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, ++ {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, ++ {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, ++ {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, ++ {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, ++ {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, ++ {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, ++ {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, ++ {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, ++ {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, ++ {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, ++ {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, ++ {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, ++ {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, ++ {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, ++ {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, ++ {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, ++ {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, ++ {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, ++ {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, ++ {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, ++ {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, ++ {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, ++ {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, ++ {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, ++ {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, ++ {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, ++ {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, ++ {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, ++ {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, ++ {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, ++ {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, ++ {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, ++ {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, ++ {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, ++ {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, ++ {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, ++ {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, ++ {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, ++ {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, ++ {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, ++ {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, ++ {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, ++ {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, ++ {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, ++ {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, ++ {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, ++ {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, ++ {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, ++ {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, ++ {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, ++ {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, ++ {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, ++ {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, ++ {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, ++ {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, ++ {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, ++ {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, ++ {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, ++ {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, ++ {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, ++ {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, ++ {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, ++ {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, ++ {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, ++ {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, ++ {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, ++ {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, ++ {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, ++ {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, ++ {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, ++ {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, ++ {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, ++ {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, ++ {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, ++ {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, ++ {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, ++ {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, ++ {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, ++ {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, ++ {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, ++ {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, ++ {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, ++ {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, ++ {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, ++ {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, ++ {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, ++ {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, ++ {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, ++ {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, ++ {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, ++ {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, ++ {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, ++ {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, ++ {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, ++ {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, ++ {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, ++ {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, ++ {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, ++ {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, ++ {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, ++ {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, ++ {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, ++ {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, ++ {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, ++ {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, ++ {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, ++ {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, ++ {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, ++ {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, ++ {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, ++ {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, ++ {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, ++ {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, ++ {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, ++ {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, ++ {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, ++ {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, ++ {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, ++ {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, ++ {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, ++ {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, ++ {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, ++ {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, ++ {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, ++ {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, ++ {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, ++ {0x380b, 0xf0, 0, 0}, {0x3a00, 0x78, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_NTSC_720_480[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, ++ {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, ++ {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, ++ {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, ++ {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, ++ {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, ++ {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, ++ {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, ++ {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, ++ {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, ++ {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, ++ {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, ++ {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, ++ {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, ++ {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, ++ {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, ++ {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, ++ {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, ++ {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, ++ {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, ++ {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, ++ {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, ++ {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, ++ {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, ++ {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, ++ {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, ++ {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, ++ {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, ++ {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, ++ {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, ++ {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, ++ {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, ++ {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, ++ {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, ++ {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, ++ {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, ++ {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, ++ {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, ++ {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, ++ {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, ++ {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, ++ {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, ++ {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, ++ {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, ++ {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, ++ {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, ++ {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, ++ {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, ++ {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, ++ {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, ++ {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, ++ {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, ++ {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, ++ {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, ++ {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, ++ {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, ++ {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, ++ {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, ++ {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, ++ {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, ++ {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, ++ {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, ++ {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, ++ {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, ++ {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, ++ {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, ++ {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, ++ {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, ++ {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, ++ {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, ++ {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, ++ {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, ++ {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, ++ {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, ++ {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, ++ {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, ++ {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, ++ {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, ++ {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, ++ {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, ++ {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, ++ {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, ++ {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, ++ {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, ++ {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, ++ {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, ++ {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, ++ {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, ++ {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, ++ {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, ++ {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, ++ {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, ++ {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, ++ {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, ++ {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, ++ {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, ++ {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, ++ {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, ++ {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, ++ {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, ++ {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, ++ {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, ++ {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, ++ {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, ++ {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, ++ {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, ++ {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, ++ {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, ++ {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, ++ {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, ++ {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, ++ {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, ++ {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, ++ {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, ++ {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, ++ {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, ++ {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, ++ {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, ++ {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, ++ {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, ++ {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, ++ {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, ++ {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, ++ {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, ++ {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, ++ {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, ++ {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, ++ {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, ++ {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, ++ {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, ++ {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, ++ {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, ++ {0x3824, 0x11, 0, 0}, {0x3825, 0xb4, 0, 0}, {0x3826, 0x00, 0, 0}, ++ {0x3827, 0x3d, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0xd0, 0, 0}, {0x380A, 0x01, 0, 0}, {0x380B, 0xe0, 0, 0}, ++ {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, ++ {0x3807, 0x55, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0x55, 0, 0}, ++ {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, ++}; ++ ++static struct reg_value ov5642_setting_15fps_PAL_720_576[] = { ++ {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, ++ {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, ++ {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, ++ {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, ++ {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, ++ {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, ++ {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, ++ {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, ++ {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, ++ {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, ++ {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, ++ {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, ++ {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, ++ {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, ++ {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, ++ {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, ++ {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, ++ {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, ++ {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, ++ {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, ++ {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, ++ {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, ++ {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, ++ {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, ++ {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, ++ {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, ++ {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, ++ {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, ++ {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, ++ {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, ++ {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, ++ {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, ++ {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, ++ {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, ++ {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, ++ {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, ++ {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, ++ {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, ++ {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, ++ {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, ++ {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, ++ {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, ++ {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, ++ {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, ++ {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, ++ {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, ++ {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, ++ {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, ++ {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, ++ {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, ++ {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, ++ {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, ++ {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, ++ {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, ++ {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, ++ {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, ++ {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, ++ {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, ++ {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, ++ {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, ++ {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, ++ {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, ++ {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, ++ {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, ++ {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, ++ {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, ++ {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, ++ {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, ++ {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, ++ {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, ++ {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, ++ {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, ++ {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, ++ {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, ++ {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, ++ {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, ++ {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, ++ {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, ++ {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, ++ {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, ++ {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, ++ {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, ++ {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, ++ {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, ++ {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, ++ {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, ++ {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, ++ {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, ++ {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, ++ {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, ++ {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, ++ {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, ++ {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, ++ {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, ++ {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, ++ {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, ++ {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, ++ {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, ++ {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, ++ {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, ++ {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, ++ {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, ++ {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, ++ {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, ++ {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, ++ {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, ++ {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, ++ {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, ++ {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, ++ {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, ++ {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, ++ {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, ++ {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, ++ {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, ++ {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, ++ {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, ++ {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, ++ {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, ++ {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, ++ {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, ++ {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, ++ {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, ++ {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, ++ {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, ++ {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, ++ {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, ++ {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, ++ {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, ++ {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, ++ {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, ++ {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, ++ {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, ++ {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, ++ {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, ++ {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, ++ {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, ++ {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, ++ {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, ++ {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, ++ {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, ++ {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, ++ {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, ++ {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, ++ {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, ++ {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, ++ {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, ++ {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, ++ {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, ++ {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, ++ {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, ++ {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, ++ {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, ++ {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, ++ {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, ++ {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, ++ {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, ++ {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, ++ {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, ++ {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, ++ {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, ++ {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, ++ {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, ++ {0x3824, 0x11, 0, 0}, {0x3825, 0xdc, 0, 0}, {0x3826, 0x00, 0, 0}, ++ {0x3827, 0x08, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, ++ {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0}, ++ {0x3809, 0xd0, 0, 0}, {0x380A, 0x02, 0, 0}, {0x380B, 0x40, 0, 0}, ++ {0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0}, ++ {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xc0, 0, 0}, ++ {0x5682, 0x04, 0, 0}, {0x5683, 0xb0, 0, 0}, ++}; ++ ++static struct ov5642_mode_info ov5642_mode_info_data[2][ov5642_mode_MAX + 1] = { ++ { ++ {ov5642_mode_VGA_640_480, 640, 480, ++ ov5642_setting_15fps_VGA_640_480, ++ ARRAY_SIZE(ov5642_setting_15fps_VGA_640_480)}, ++ {ov5642_mode_QVGA_320_240, 320, 240, ++ ov5642_setting_15fps_QVGA_320_240, ++ ARRAY_SIZE(ov5642_setting_15fps_QVGA_320_240)}, ++ {ov5642_mode_NTSC_720_480, 720, 480, ++ ov5642_setting_15fps_NTSC_720_480, ++ ARRAY_SIZE(ov5642_setting_15fps_NTSC_720_480)}, ++ {ov5642_mode_PAL_720_576, 720, 576, ++ ov5642_setting_15fps_PAL_720_576, ++ ARRAY_SIZE(ov5642_setting_15fps_PAL_720_576)}, ++ {ov5642_mode_720P_1280_720, 1280, 720, ++ ov5642_setting_15fps_720P_1280_720, ++ ARRAY_SIZE(ov5642_setting_15fps_720P_1280_720)}, ++ {ov5642_mode_1080P_1920_1080, 1920, 1080, ++ ov5642_setting_15fps_1080P_1920_1080, ++ ARRAY_SIZE(ov5642_setting_15fps_1080P_1920_1080)}, ++ {ov5642_mode_QSXGA_2592_1944, 2592, 1944, ++ ov5642_setting_15fps_QSXGA_2592_1944, ++ ARRAY_SIZE(ov5642_setting_15fps_QSXGA_2592_1944)}, ++ {ov5642_mode_QCIF_176_144, 176, 144, ++ ov5642_setting_15fps_QCIF_176_144, ++ ARRAY_SIZE(ov5642_setting_15fps_QCIF_176_144)}, ++ {ov5642_mode_XGA_1024_768, 1024, 768, ++ ov5642_setting_15fps_XGA_1024_768, ++ ARRAY_SIZE(ov5642_setting_15fps_XGA_1024_768)}, ++ }, ++ { ++ {ov5642_mode_VGA_640_480, 640, 480, ++ ov5642_setting_30fps_VGA_640_480, ++ ARRAY_SIZE(ov5642_setting_30fps_VGA_640_480)}, ++ {ov5642_mode_QVGA_320_240, 320, 240, ++ ov5642_setting_30fps_QVGA_320_240, ++ ARRAY_SIZE(ov5642_setting_30fps_QVGA_320_240)}, ++ {ov5642_mode_NTSC_720_480, 720, 480, ++ ov5642_setting_30fps_NTSC_720_480, ++ ARRAY_SIZE(ov5642_setting_30fps_NTSC_720_480)}, ++ {ov5642_mode_PAL_720_576, 720, 576, ++ ov5642_setting_30fps_PAL_720_576, ++ ARRAY_SIZE(ov5642_setting_30fps_PAL_720_576)}, ++ {ov5642_mode_720P_1280_720, 1280, 720, ++ ov5642_setting_30fps_720P_1280_720, ++ ARRAY_SIZE(ov5642_setting_30fps_720P_1280_720)}, ++ {ov5642_mode_1080P_1920_1080, 0, 0, NULL, 0}, ++ {ov5642_mode_QSXGA_2592_1944, 0, 0, NULL, 0}, ++ {ov5642_mode_QCIF_176_144, 176, 144, ++ ov5642_setting_30fps_QCIF_176_144, ++ ARRAY_SIZE(ov5642_setting_30fps_QCIF_176_144)}, ++ {ov5642_mode_XGA_1024_768, 1024, 768, ++ ov5642_setting_30fps_XGA_1024_768, ++ ARRAY_SIZE(ov5642_setting_30fps_XGA_1024_768)}, ++ }, ++}; ++ ++static struct regulator *io_regulator; ++static struct regulator *core_regulator; ++static struct regulator *analog_regulator; ++static struct regulator *gpo_regulator; ++ ++static int ov5642_probe(struct i2c_client *adapter, ++ const struct i2c_device_id *device_id); ++static int ov5642_remove(struct i2c_client *client); ++ ++static s32 ov5642_read_reg(u16 reg, u8 *val); ++static s32 ov5642_write_reg(u16 reg, u8 val); ++ ++static const struct i2c_device_id ov5642_id[] = { ++ {"ov5642", 0}, ++ {"ov564x", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, ov5642_id); ++ ++static struct i2c_driver ov5642_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ov5642", ++ }, ++ .probe = ov5642_probe, ++ .remove = ov5642_remove, ++ .id_table = ov5642_id, ++}; ++ ++static void ov5642_standby(s32 enable) ++{ ++ if (enable) ++ gpio_set_value(pwn_gpio, 1); ++ else ++ gpio_set_value(pwn_gpio, 0); ++ ++ msleep(2); ++} ++ ++static void ov5642_reset(void) ++{ ++ /* camera reset */ ++ gpio_set_value(rst_gpio, 1); ++ ++ /* camera power down */ ++ gpio_set_value(pwn_gpio, 1); ++ msleep(5); ++ ++ gpio_set_value(pwn_gpio, 0); ++ msleep(5); ++ ++ gpio_set_value(rst_gpio, 0); ++ msleep(1); ++ ++ gpio_set_value(rst_gpio, 1); ++ msleep(5); ++ ++ gpio_set_value(pwn_gpio, 1); ++} ++ ++static int ov5642_power_on(struct device *dev) ++{ ++ int ret = 0; ++ ++ io_regulator = devm_regulator_get(dev, "DOVDD"); ++ if (!IS_ERR(io_regulator)) { ++ regulator_set_voltage(io_regulator, ++ OV5642_VOLTAGE_DIGITAL_IO, ++ OV5642_VOLTAGE_DIGITAL_IO); ++ ret = regulator_enable(io_regulator); ++ if (ret) { ++ pr_err("%s:io set voltage error\n", __func__); ++ return ret; ++ } else { ++ dev_dbg(dev, ++ "%s:io set voltage ok\n", __func__); ++ } ++ } else { ++ pr_err("%s: cannot get io voltage error\n", __func__); ++ io_regulator = NULL; ++ } ++ ++ core_regulator = devm_regulator_get(dev, "DVDD"); ++ if (!IS_ERR(core_regulator)) { ++ regulator_set_voltage(core_regulator, ++ OV5642_VOLTAGE_DIGITAL_CORE, ++ OV5642_VOLTAGE_DIGITAL_CORE); ++ ret = regulator_enable(core_regulator); ++ if (ret) { ++ pr_err("%s:core set voltage error\n", __func__); ++ return ret; ++ } else { ++ dev_dbg(dev, ++ "%s:core set voltage ok\n", __func__); ++ } ++ } else { ++ core_regulator = NULL; ++ pr_err("%s: cannot get core voltage error\n", __func__); ++ } ++ ++ analog_regulator = devm_regulator_get(dev, "AVDD"); ++ if (!IS_ERR(analog_regulator)) { ++ regulator_set_voltage(analog_regulator, ++ OV5642_VOLTAGE_ANALOG, ++ OV5642_VOLTAGE_ANALOG); ++ ret = regulator_enable(analog_regulator); ++ if (ret) { ++ pr_err("%s:analog set voltage error\n", ++ __func__); ++ return ret; ++ } else { ++ dev_dbg(dev, ++ "%s:analog set voltage ok\n", __func__); ++ } ++ } else { ++ analog_regulator = NULL; ++ pr_err("%s: cannot get analog voltage error\n", __func__); ++ } ++ ++ return ret; ++} ++ ++static s32 ov5642_write_reg(u16 reg, u8 val) ++{ ++ u8 au8Buf[3] = {0}; ++ ++ au8Buf[0] = reg >> 8; ++ au8Buf[1] = reg & 0xff; ++ au8Buf[2] = val; ++ ++ if (i2c_master_send(ov5642_data.i2c_client, au8Buf, 3) < 0) { ++ pr_err("%s:write reg error:reg=%x,val=%x\n", ++ __func__, reg, val); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static s32 ov5642_read_reg(u16 reg, u8 *val) ++{ ++ u8 au8RegBuf[2] = {0}; ++ u8 u8RdVal = 0; ++ ++ au8RegBuf[0] = reg >> 8; ++ au8RegBuf[1] = reg & 0xff; ++ ++ if (2 != i2c_master_send(ov5642_data.i2c_client, au8RegBuf, 2)) { ++ pr_err("%s:write reg error:reg=%x\n", ++ __func__, reg); ++ return -1; ++ } ++ ++ if (1 != i2c_master_recv(ov5642_data.i2c_client, &u8RdVal, 1)) { ++ pr_err("%s:read reg error:reg=%x,val=%x\n", ++ __func__, reg, u8RdVal); ++ return -1; ++ } ++ ++ *val = u8RdVal; ++ ++ return u8RdVal; ++} ++ ++static int ov5642_set_rot_mode(struct reg_value *rot_mode) ++{ ++ s32 i = 0; ++ s32 iModeSettingArySize = 2; ++ register u32 Delay_ms = 0; ++ register u16 RegAddr = 0; ++ register u8 Mask = 0; ++ register u8 Val = 0; ++ u8 RegVal = 0; ++ int retval = 0; ++ for (i = 0; i < iModeSettingArySize; ++i, ++rot_mode) { ++ Delay_ms = rot_mode->u32Delay_ms; ++ RegAddr = rot_mode->u16RegAddr; ++ Val = rot_mode->u8Val; ++ Mask = rot_mode->u8Mask; ++ ++ if (Mask) { ++ retval = ov5642_read_reg(RegAddr, &RegVal); ++ if (retval < 0) { ++ pr_err("%s, read reg 0x%x failed\n", ++ __func__, RegAddr); ++ goto err; ++ } ++ ++ Val |= RegVal; ++ Val &= Mask; ++ } ++ ++ retval = ov5642_write_reg(RegAddr, Val); ++ if (retval < 0) { ++ pr_err("%s, write reg 0x%x failed\n", ++ __func__, RegAddr); ++ goto err; ++ } ++ ++ if (Delay_ms) ++ mdelay(Delay_ms); ++ } ++err: ++ return retval; ++} ++static int ov5642_init_mode(enum ov5642_frame_rate frame_rate, ++ enum ov5642_mode mode); ++static int ov5642_write_snapshot_para(enum ov5642_frame_rate frame_rate, ++ enum ov5642_mode mode); ++static int ov5642_change_mode(enum ov5642_frame_rate new_frame_rate, ++ enum ov5642_frame_rate old_frame_rate, ++ enum ov5642_mode new_mode, ++ enum ov5642_mode orig_mode) ++{ ++ struct reg_value *pModeSetting = NULL; ++ s32 i = 0; ++ s32 iModeSettingArySize = 0; ++ register u32 Delay_ms = 0; ++ register u16 RegAddr = 0; ++ register u8 Mask = 0; ++ register u8 Val = 0; ++ u8 RegVal = 0; ++ int retval = 0; ++ ++ if (new_mode > ov5642_mode_MAX || new_mode < ov5642_mode_MIN) { ++ pr_err("Wrong ov5642 mode detected!\n"); ++ return -1; ++ } ++ ++ if ((new_frame_rate == old_frame_rate) && ++ (new_mode == ov5642_mode_VGA_640_480) && ++ (orig_mode == ov5642_mode_QSXGA_2592_1944)) { ++ pModeSetting = ov5642_setting_QSXGA_2_VGA; ++ iModeSettingArySize = ARRAY_SIZE(ov5642_setting_QSXGA_2_VGA); ++ ov5642_data.pix.width = 640; ++ ov5642_data.pix.height = 480; ++ } else if ((new_frame_rate == old_frame_rate) && ++ (new_mode == ov5642_mode_QVGA_320_240) && ++ (orig_mode == ov5642_mode_VGA_640_480)) { ++ pModeSetting = ov5642_setting_VGA_2_QVGA; ++ iModeSettingArySize = ARRAY_SIZE(ov5642_setting_VGA_2_QVGA); ++ ov5642_data.pix.width = 320; ++ ov5642_data.pix.height = 240; ++ } else { ++ retval = ov5642_write_snapshot_para(new_frame_rate, new_mode); ++ goto err; ++ } ++ ++ if (ov5642_data.pix.width == 0 || ov5642_data.pix.height == 0 || ++ pModeSetting == NULL || iModeSettingArySize == 0) ++ return -EINVAL; ++ ++ for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) { ++ Delay_ms = pModeSetting->u32Delay_ms; ++ RegAddr = pModeSetting->u16RegAddr; ++ Val = pModeSetting->u8Val; ++ Mask = pModeSetting->u8Mask; ++ ++ if (Mask) { ++ retval = ov5642_read_reg(RegAddr, &RegVal); ++ if (retval < 0) { ++ pr_err("read reg error addr=0x%x", RegAddr); ++ goto err; ++ } ++ ++ RegVal &= ~(u8)Mask; ++ Val &= Mask; ++ Val |= RegVal; ++ } ++ ++ retval = ov5642_write_reg(RegAddr, Val); ++ if (retval < 0) { ++ pr_err("write reg error addr=0x%x", RegAddr); ++ goto err; ++ } ++ ++ if (Delay_ms) ++ msleep(Delay_ms); ++ } ++err: ++ return retval; ++} ++static int ov5642_init_mode(enum ov5642_frame_rate frame_rate, ++ enum ov5642_mode mode) ++{ ++ struct reg_value *pModeSetting = NULL; ++ s32 i = 0; ++ s32 iModeSettingArySize = 0; ++ register u32 Delay_ms = 0; ++ register u16 RegAddr = 0; ++ register u8 Mask = 0; ++ register u8 Val = 0; ++ u8 RegVal = 0; ++ int retval = 0; ++ ++ if (mode > ov5642_mode_MAX || mode < ov5642_mode_MIN) { ++ pr_err("Wrong ov5642 mode detected!\n"); ++ return -1; ++ } ++ ++ pModeSetting = ov5642_mode_info_data[frame_rate][mode].init_data_ptr; ++ iModeSettingArySize = ++ ov5642_mode_info_data[frame_rate][mode].init_data_size; ++ ++ ov5642_data.pix.width = ov5642_mode_info_data[frame_rate][mode].width; ++ ov5642_data.pix.height = ov5642_mode_info_data[frame_rate][mode].height; ++ ++ if (ov5642_data.pix.width == 0 || ov5642_data.pix.height == 0 || ++ pModeSetting == NULL || iModeSettingArySize == 0) ++ return -EINVAL; ++ ++ for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) { ++ Delay_ms = pModeSetting->u32Delay_ms; ++ RegAddr = pModeSetting->u16RegAddr; ++ Val = pModeSetting->u8Val; ++ Mask = pModeSetting->u8Mask; ++ ++ if (Mask) { ++ retval = ov5642_read_reg(RegAddr, &RegVal); ++ if (retval < 0) { ++ pr_err("read reg error addr=0x%x", RegAddr); ++ goto err; ++ } ++ ++ RegVal &= ~(u8)Mask; ++ Val &= Mask; ++ Val |= RegVal; ++ } ++ ++ retval = ov5642_write_reg(RegAddr, Val); ++ if (retval < 0) { ++ pr_err("write reg error addr=0x%x", RegAddr); ++ goto err; ++ } ++ ++ if (Delay_ms) ++ msleep(Delay_ms); ++ } ++err: ++ return retval; ++} ++ ++static int ov5642_write_snapshot_para(enum ov5642_frame_rate frame_rate, ++ enum ov5642_mode mode) ++{ ++ int ret = 0; ++ bool m_60Hz = false; ++ u16 cap_frame_rate = 50; ++ u16 g_prev_frame_rate = 225; ++ ++ u8 ev_low, ev_mid, ev_high; ++ u8 ret_l, ret_m, ret_h, gain, lines_10ms; ++ u16 ulcap_ev, icap_gain, prev_maxlines; ++ u32 ulcap_ev_gain, cap_maxlines, g_prev_ev; ++ ++ ov5642_write_reg(0x3503, 0x07); ++ ++ ret_h = ret_m = ret_l = 0; ++ g_prev_ev = 0; ++ ov5642_read_reg(0x3500, &ret_h); ++ ov5642_read_reg(0x3501, &ret_m); ++ ov5642_read_reg(0x3502, &ret_l); ++ g_prev_ev = (ret_h << 12) + (ret_m << 4) + (ret_l >> 4); ++ ++ ret_h = ret_m = ret_l = 0; ++ prev_maxlines = 0; ++ ov5642_read_reg(0x380e, &ret_h); ++ ov5642_read_reg(0x380f, &ret_l); ++ prev_maxlines = (ret_h << 8) + ret_l; ++ /*Read back AGC Gain for preview*/ ++ gain = 0; ++ ov5642_read_reg(0x350b, &gain); ++ ++ ret = ov5642_init_mode(frame_rate, mode); ++ if (ret < 0) ++ return ret; ++ ++ ret_h = ret_m = ret_l = 0; ++ ov5642_read_reg(0x380e, &ret_h); ++ ov5642_read_reg(0x380f, &ret_l); ++ cap_maxlines = (ret_h << 8) + ret_l; ++ if (m_60Hz == true) ++ lines_10ms = cap_frame_rate * cap_maxlines/12000; ++ else ++ lines_10ms = cap_frame_rate * cap_maxlines/10000; ++ ++ if (prev_maxlines == 0) ++ prev_maxlines = 1; ++ ++ ulcap_ev = (g_prev_ev*(cap_frame_rate)*(cap_maxlines))/ ++ (((prev_maxlines)*(g_prev_frame_rate))); ++ icap_gain = (gain & 0x0f) + 16; ++ if (gain & 0x10) ++ icap_gain = icap_gain << 1; ++ ++ if (gain & 0x20) ++ icap_gain = icap_gain << 1; ++ ++ if (gain & 0x40) ++ icap_gain = icap_gain << 1; ++ ++ if (gain & 0x80) ++ icap_gain = icap_gain << 1; ++ ++ ulcap_ev_gain = 2 * ulcap_ev * icap_gain; ++ ++ if (ulcap_ev_gain < cap_maxlines*16) { ++ ulcap_ev = ulcap_ev_gain/16; ++ if (ulcap_ev > lines_10ms) { ++ ulcap_ev /= lines_10ms; ++ ulcap_ev *= lines_10ms; ++ } ++ } else ++ ulcap_ev = cap_maxlines; ++ ++ if (ulcap_ev == 0) ++ ulcap_ev = 1; ++ ++ icap_gain = (ulcap_ev_gain*2/ulcap_ev + 1)/2; ++ ev_low = ((unsigned char)ulcap_ev)<<4; ++ ev_mid = (unsigned char)(ulcap_ev >> 4) & 0xff; ++ ev_high = (unsigned char)(ulcap_ev >> 12); ++ ++ gain = 0; ++ if (icap_gain > 31) { ++ gain |= 0x10; ++ icap_gain = icap_gain >> 1; ++ } ++ if (icap_gain > 31) { ++ gain |= 0x20; ++ icap_gain = icap_gain >> 1; ++ } ++ if (icap_gain > 31) { ++ gain |= 0x40; ++ icap_gain = icap_gain >> 1; ++ } ++ if (icap_gain > 31) { ++ gain |= 0x80; ++ icap_gain = icap_gain >> 1; ++ } ++ if (icap_gain > 16) ++ gain |= ((icap_gain - 16) & 0x0f); ++ ++ if (gain == 0x10) ++ gain = 0x11; ++ ++ ov5642_write_reg(0x350b, gain); ++ ov5642_write_reg(0x3502, ev_low); ++ ov5642_write_reg(0x3501, ev_mid); ++ ov5642_write_reg(0x3500, ev_high); ++ msleep(500); ++ ++ return ret ; ++} ++ ++ ++/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ ++ ++static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) ++{ ++ if (s == NULL) { ++ pr_err(" ERROR!! no slave device set!\n"); ++ return -1; ++ } ++ ++ memset(p, 0, sizeof(*p)); ++ p->u.bt656.clock_curr = ov5642_data.mclk; ++ pr_debug(" clock_curr=mclk=%d\n", ov5642_data.mclk); ++ p->if_type = V4L2_IF_TYPE_BT656; ++ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; ++ p->u.bt656.clock_min = OV5642_XCLK_MIN; ++ p->u.bt656.clock_max = OV5642_XCLK_MAX; ++ p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @on: indicates power mode (on or off) ++ * ++ * Turns the power on or off, depending on the value of on and returns the ++ * appropriate error code. ++ */ ++static int ioctl_s_power(struct v4l2_int_device *s, int on) ++{ ++ struct sensor_data *sensor = s->priv; ++ ++ if (on && !sensor->on) { ++ if (io_regulator) ++ if (regulator_enable(io_regulator) != 0) ++ return -EIO; ++ if (core_regulator) ++ if (regulator_enable(core_regulator) != 0) ++ return -EIO; ++ if (gpo_regulator) ++ if (regulator_enable(gpo_regulator) != 0) ++ return -EIO; ++ if (analog_regulator) ++ if (regulator_enable(analog_regulator) != 0) ++ return -EIO; ++ /* Make sure power on */ ++ ov5642_standby(0); ++ } else if (!on && sensor->on) { ++ if (analog_regulator) ++ regulator_disable(analog_regulator); ++ if (core_regulator) ++ regulator_disable(core_regulator); ++ if (io_regulator) ++ regulator_disable(io_regulator); ++ if (gpo_regulator) ++ regulator_disable(gpo_regulator); ++ ++ ov5642_standby(1); ++ } ++ ++ sensor->on = on; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure ++ * ++ * Returns the sensor's video CAPTURE parameters. ++ */ ++static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ struct sensor_data *sensor = s->priv; ++ struct v4l2_captureparm *cparm = &a->parm.capture; ++ int ret = 0; ++ ++ switch (a->type) { ++ /* This is the only case currently handled. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ memset(a, 0, sizeof(*a)); ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ cparm->capability = sensor->streamcap.capability; ++ cparm->timeperframe = sensor->streamcap.timeperframe; ++ cparm->capturemode = sensor->streamcap.capturemode; ++ ret = 0; ++ break; ++ ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ ret = -EINVAL; ++ break; ++ ++ default: ++ pr_debug(" type is unknown - %d\n", a->type); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure ++ * ++ * Configures the sensor to use the input parameters, if possible. If ++ * not possible, reverts to the old parameters and returns the ++ * appropriate error code. ++ */ ++static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ++{ ++ struct sensor_data *sensor = s->priv; ++ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; ++ u32 tgt_fps, old_fps; /* target frames per secound */ ++ enum ov5642_frame_rate new_frame_rate, old_frame_rate; ++ int ret = 0; ++ ++ /* Make sure power on */ ++ ov5642_standby(0); ++ ++ switch (a->type) { ++ /* This is the only case currently handled. */ ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ /* Check that the new frame rate is allowed. */ ++ if ((timeperframe->numerator == 0) || ++ (timeperframe->denominator == 0)) { ++ timeperframe->denominator = DEFAULT_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ tgt_fps = timeperframe->denominator / ++ timeperframe->numerator; ++ ++ if (tgt_fps > MAX_FPS) { ++ timeperframe->denominator = MAX_FPS; ++ timeperframe->numerator = 1; ++ } else if (tgt_fps < MIN_FPS) { ++ timeperframe->denominator = MIN_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ /* Actual frame rate we use */ ++ tgt_fps = timeperframe->denominator / ++ timeperframe->numerator; ++ ++ if (tgt_fps == 15) ++ new_frame_rate = ov5642_15_fps; ++ else if (tgt_fps == 30) ++ new_frame_rate = ov5642_30_fps; ++ else { ++ pr_err(" The camera frame rate is not supported!\n"); ++ return -EINVAL; ++ } ++ ++ if (sensor->streamcap.timeperframe.numerator != 0) ++ old_fps = sensor->streamcap.timeperframe.denominator / ++ sensor->streamcap.timeperframe.numerator; ++ else ++ old_fps = 30; ++ ++ if (old_fps == 15) ++ old_frame_rate = ov5642_15_fps; ++ else if (old_fps == 30) ++ old_frame_rate = ov5642_30_fps; ++ else { ++ pr_warning(" No valid frame rate set!\n"); ++ old_frame_rate = ov5642_30_fps; ++ } ++ ++ ret = ov5642_change_mode(new_frame_rate, old_frame_rate, ++ a->parm.capture.capturemode, ++ sensor->streamcap.capturemode); ++ if (ret < 0) ++ return ret; ++ ++ sensor->streamcap.timeperframe = *timeperframe; ++ sensor->streamcap.capturemode = ++ (u32)a->parm.capture.capturemode; ++ break; ++ ++ /* These are all the possible cases. */ ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ case V4L2_BUF_TYPE_VIDEO_OVERLAY: ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_VBI_OUTPUT: ++ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: ++ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: ++ pr_debug(" type is not " \ ++ "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", ++ a->type); ++ ret = -EINVAL; ++ break; ++ ++ default: ++ pr_debug(" type is unknown - %d\n", a->type); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap ++ * @s: pointer to standard V4L2 device structure ++ * @f: pointer to standard V4L2 v4l2_format structure ++ * ++ * Returns the sensor's current pixel format in the v4l2_format ++ * parameter. ++ */ ++static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) ++{ ++ struct sensor_data *sensor = s->priv; ++ ++ f->fmt.pix = sensor->pix; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure ++ * ++ * If the requested control is supported, returns the control's current ++ * value from the video_control[] array. Otherwise, returns -EINVAL ++ * if the control is not supported. ++ */ ++static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int ret = 0; ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ vc->value = ov5642_data.brightness; ++ break; ++ case V4L2_CID_HUE: ++ vc->value = ov5642_data.hue; ++ break; ++ case V4L2_CID_CONTRAST: ++ vc->value = ov5642_data.contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ vc->value = ov5642_data.saturation; ++ break; ++ case V4L2_CID_RED_BALANCE: ++ vc->value = ov5642_data.red; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ vc->value = ov5642_data.blue; ++ break; ++ case V4L2_CID_EXPOSURE: ++ vc->value = ov5642_data.ae_mode; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/*! ++ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure ++ * ++ * If the requested control is supported, sets the control's current ++ * value in HW (and updates the video_control[] array). Otherwise, ++ * returns -EINVAL if the control is not supported. ++ */ ++static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) ++{ ++ int retval = 0; ++ struct sensor_data *sensor = s->priv; ++ __u32 captureMode = sensor->streamcap.capturemode; ++ struct reg_value *rot_mode = NULL; ++ ++ pr_debug("In ov5642:ioctl_s_ctrl %d\n", ++ vc->id); ++ ++ switch (vc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ break; ++ case V4L2_CID_CONTRAST: ++ break; ++ case V4L2_CID_SATURATION: ++ break; ++ case V4L2_CID_HUE: ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ break; ++ case V4L2_CID_RED_BALANCE: ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ break; ++ case V4L2_CID_GAMMA: ++ break; ++ case V4L2_CID_EXPOSURE: ++ break; ++ case V4L2_CID_AUTOGAIN: ++ break; ++ case V4L2_CID_GAIN: ++ break; ++ case V4L2_CID_HFLIP: ++ break; ++ case V4L2_CID_VFLIP: ++ break; ++ case V4L2_CID_MXC_ROT: ++ case V4L2_CID_MXC_VF_ROT: ++ switch (vc->value) { ++ case V4L2_MXC_ROTATE_NONE: ++ if (captureMode == ov5642_mode_QSXGA_2592_1944) ++ rot_mode = ov5642_rot_none_FULL; ++ else ++ rot_mode = ov5642_rot_none_VGA; ++ ++ if (ov5642_set_rot_mode(rot_mode)) ++ retval = -EPERM; ++ break; ++ case V4L2_MXC_ROTATE_VERT_FLIP: ++ if (captureMode == ov5642_mode_QSXGA_2592_1944) ++ rot_mode = ov5642_rot_vert_flip_FULL; ++ else ++ rot_mode = ov5642_rot_vert_flip_VGA ; ++ ++ if (ov5642_set_rot_mode(rot_mode)) ++ retval = -EPERM; ++ break; ++ case V4L2_MXC_ROTATE_HORIZ_FLIP: ++ if (captureMode == ov5642_mode_QSXGA_2592_1944) ++ rot_mode = ov5642_rot_horiz_flip_FULL; ++ else ++ rot_mode = ov5642_rot_horiz_flip_VGA; ++ ++ if (ov5642_set_rot_mode(rot_mode)) ++ retval = -EPERM; ++ break; ++ case V4L2_MXC_ROTATE_180: ++ if (captureMode == ov5642_mode_QSXGA_2592_1944) ++ rot_mode = ov5642_rot_180_FULL; ++ else ++ rot_mode = ov5642_rot_180_VGA; ++ ++ if (ov5642_set_rot_mode(rot_mode)) ++ retval = -EPERM; ++ break; ++ default: ++ retval = -EPERM; ++ break; ++ } ++ break; ++ default: ++ retval = -EPERM; ++ break; ++ } ++ ++ return retval; ++} ++ ++/*! ++ * ioctl_enum_framesizes - V4L2 sensor interface handler for ++ * VIDIOC_ENUM_FRAMESIZES ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure ++ * ++ * Return 0 if successful, otherwise -EINVAL. ++ */ ++static int ioctl_enum_framesizes(struct v4l2_int_device *s, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ if (fsize->index > ov5642_mode_MAX) ++ return -EINVAL; ++ ++ fsize->pixel_format = ov5642_data.pix.pixelformat; ++ fsize->discrete.width = ++ max(ov5642_mode_info_data[0][fsize->index].width, ++ ov5642_mode_info_data[1][fsize->index].width); ++ fsize->discrete.height = ++ max(ov5642_mode_info_data[0][fsize->index].height, ++ ov5642_mode_info_data[1][fsize->index].height); ++ return 0; ++} ++ ++/*! ++ * ioctl_enum_frameintervals - V4L2 sensor interface handler for ++ * VIDIOC_ENUM_FRAMEINTERVALS ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure ++ * ++ * Return 0 if successful, otherwise -EINVAL. ++ */ ++static int ioctl_enum_frameintervals(struct v4l2_int_device *s, ++ struct v4l2_frmivalenum *fival) ++{ ++ int i, j, count; ++ ++ if (fival->index < 0 || fival->index > ov5642_mode_MAX) ++ return -EINVAL; ++ ++ if (fival->pixel_format == 0 || fival->width == 0 || ++ fival->height == 0) { ++ pr_warning("Please assign pixelformat, width and height.\n"); ++ return -EINVAL; ++ } ++ ++ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ fival->discrete.numerator = 1; ++ ++ count = 0; ++ for (i = 0; i < ARRAY_SIZE(ov5642_mode_info_data); i++) { ++ for (j = 0; j < (ov5642_mode_MAX + 1); j++) { ++ if (fival->pixel_format == ov5642_data.pix.pixelformat ++ && fival->width == ov5642_mode_info_data[i][j].width ++ && fival->height == ov5642_mode_info_data[i][j].height ++ && ov5642_mode_info_data[i][j].init_data_ptr != NULL) { ++ count++; ++ } ++ if (fival->index == (count - 1)) { ++ fival->discrete.denominator = ++ ov5642_framerates[i]; ++ return 0; ++ } ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++/*! ++ * ioctl_g_chip_ident - V4L2 sensor interface handler for ++ * VIDIOC_DBG_G_CHIP_IDENT ioctl ++ * @s: pointer to standard V4L2 device structure ++ * @id: pointer to int ++ * ++ * Return 0. ++ */ ++static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) ++{ ++ ((struct v4l2_dbg_chip_ident *)id)->match.type = ++ V4L2_CHIP_MATCH_I2C_DRIVER; ++ strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "ov5642_camera"); ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT ++ * @s: pointer to standard V4L2 device structure ++ */ ++static int ioctl_init(struct v4l2_int_device *s) ++{ ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT ++ * @s: pointer to standard V4L2 device structure ++ * @fmt: pointer to standard V4L2 fmt description structure ++ * ++ * Return 0. ++ */ ++static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, ++ struct v4l2_fmtdesc *fmt) ++{ ++ if (fmt->index > 0) /* only 1 pixelformat support so far */ ++ return -EINVAL; ++ ++ fmt->pixelformat = ov5642_data.pix.pixelformat; ++ ++ return 0; ++} ++ ++/*! ++ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num ++ * @s: pointer to standard V4L2 device structure ++ * ++ * Initialise the device when slave attaches to the master. ++ */ ++static int ioctl_dev_init(struct v4l2_int_device *s) ++{ ++ struct reg_value *pModeSetting = NULL; ++ s32 i = 0; ++ s32 iModeSettingArySize = 0; ++ register u32 Delay_ms = 0; ++ register u16 RegAddr = 0; ++ register u8 Mask = 0; ++ register u8 Val = 0; ++ u8 RegVal = 0; ++ int retval = 0; ++ ++ struct sensor_data *sensor = s->priv; ++ u32 tgt_xclk; /* target xclk */ ++ u32 tgt_fps; /* target frames per secound */ ++ enum ov5642_frame_rate frame_rate; ++ ++ ov5642_data.on = true; ++ ++ /* mclk */ ++ tgt_xclk = ov5642_data.mclk; ++ tgt_xclk = min(tgt_xclk, (u32)OV5642_XCLK_MAX); ++ tgt_xclk = max(tgt_xclk, (u32)OV5642_XCLK_MIN); ++ ov5642_data.mclk = tgt_xclk; ++ ++ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); ++ ++ /* Default camera frame rate is set in probe */ ++ tgt_fps = sensor->streamcap.timeperframe.denominator / ++ sensor->streamcap.timeperframe.numerator; ++ ++ if (tgt_fps == 15) ++ frame_rate = ov5642_15_fps; ++ else if (tgt_fps == 30) ++ frame_rate = ov5642_30_fps; ++ else ++ return -EINVAL; /* Only support 15fps or 30fps now. */ ++ ++ pModeSetting = ov5642_initial_setting; ++ iModeSettingArySize = ARRAY_SIZE(ov5642_initial_setting); ++ ++ for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) { ++ Delay_ms = pModeSetting->u32Delay_ms; ++ RegAddr = pModeSetting->u16RegAddr; ++ Val = pModeSetting->u8Val; ++ Mask = pModeSetting->u8Mask; ++ if (Mask) { ++ retval = ov5642_read_reg(RegAddr, &RegVal); ++ if (retval < 0) ++ goto err; ++ ++ RegVal &= ~(u8)Mask; ++ Val &= Mask; ++ Val |= RegVal; ++ } ++ ++ retval = ov5642_write_reg(RegAddr, Val); ++ if (retval < 0) ++ goto err; ++ ++ if (Delay_ms) ++ msleep(Delay_ms); ++ } ++err: ++ return retval; ++} ++ ++/*! ++ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num ++ * @s: pointer to standard V4L2 device structure ++ * ++ * Delinitialise the device when slave detaches to the master. ++ */ ++static int ioctl_dev_exit(struct v4l2_int_device *s) ++{ ++ return 0; ++} ++ ++/*! ++ * This structure defines all the ioctls for this module and links them to the ++ * enumeration. ++ */ ++static struct v4l2_int_ioctl_desc ov5642_ioctl_desc[] = { ++ { vidioc_int_dev_init_num, ++ (v4l2_int_ioctl_func *)ioctl_dev_init }, ++ { vidioc_int_dev_exit_num, ioctl_dev_exit}, ++ { vidioc_int_s_power_num, ++ (v4l2_int_ioctl_func *)ioctl_s_power }, ++ { vidioc_int_g_ifparm_num, ++ (v4l2_int_ioctl_func *)ioctl_g_ifparm }, ++/* { vidioc_int_g_needs_reset_num, ++ (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, */ ++/* { vidioc_int_reset_num, ++ (v4l2_int_ioctl_func *)ioctl_reset }, */ ++ { vidioc_int_init_num, ++ (v4l2_int_ioctl_func *)ioctl_init }, ++ { vidioc_int_enum_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, ++/* { vidioc_int_try_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, */ ++ { vidioc_int_g_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, ++/* { vidioc_int_s_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, */ ++ { vidioc_int_g_parm_num, ++ (v4l2_int_ioctl_func *)ioctl_g_parm }, ++ { vidioc_int_s_parm_num, ++ (v4l2_int_ioctl_func *)ioctl_s_parm }, ++/* { vidioc_int_queryctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_queryctrl }, */ ++ { vidioc_int_g_ctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_g_ctrl }, ++ { vidioc_int_s_ctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_s_ctrl }, ++ { vidioc_int_enum_framesizes_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_framesizes }, ++ { vidioc_int_enum_frameintervals_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_frameintervals }, ++ { vidioc_int_g_chip_ident_num, ++ (v4l2_int_ioctl_func *)ioctl_g_chip_ident }, ++}; ++ ++static struct v4l2_int_slave ov5642_slave = { ++ .ioctls = ov5642_ioctl_desc, ++ .num_ioctls = ARRAY_SIZE(ov5642_ioctl_desc), ++}; ++ ++static struct v4l2_int_device ov5642_int_device = { ++ .module = THIS_MODULE, ++ .name = "ov5642", ++ .type = v4l2_int_type_slave, ++ .u = { ++ .slave = &ov5642_slave, ++ }, ++}; ++ ++/*! ++ * ov5642 I2C probe function ++ * ++ * @param adapter struct i2c_adapter * ++ * @return Error code indicating success or failure ++ */ ++static int ov5642_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct pinctrl *pinctrl; ++ struct device *dev = &client->dev; ++ int retval; ++ u8 chip_id_high, chip_id_low; ++ ++ /* ov5642 pinctrl */ ++ pinctrl = devm_pinctrl_get_select_default(dev); ++ if (IS_ERR(pinctrl)) { ++ dev_err(dev, "ov5642 setup pinctrl failed!"); ++ return PTR_ERR(pinctrl); ++ } ++ ++ /* request power down pin */ ++ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); ++ if (!gpio_is_valid(pwn_gpio)) { ++ dev_warn(dev, "no sensor pwdn pin available"); ++ return -EINVAL; ++ } ++ retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, ++ "ov5642_pwdn"); ++ if (retval < 0) ++ return retval; ++ ++ /* request reset pin */ ++ rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); ++ if (!gpio_is_valid(rst_gpio)) { ++ dev_warn(dev, "no sensor reset pin available"); ++ return -EINVAL; ++ } ++ retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, ++ "ov5642_reset"); ++ if (retval < 0) ++ return retval; ++ ++ /* Set initial values for the sensor struct. */ ++ memset(&ov5642_data, 0, sizeof(ov5642_data)); ++ ov5642_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); ++ if (IS_ERR(ov5642_data.sensor_clk)) { ++ /* assuming clock enabled by default */ ++ ov5642_data.sensor_clk = NULL; ++ dev_err(dev, "clock-frequency missing or invalid\n"); ++ return PTR_ERR(ov5642_data.sensor_clk); ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "mclk", ++ (u32 *) &(ov5642_data.mclk)); ++ if (retval) { ++ dev_err(dev, "mclk missing or invalid\n"); ++ return retval; ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "mclk_source", ++ (u32 *) &(ov5642_data.mclk_source)); ++ if (retval) { ++ dev_err(dev, "mclk_source missing or invalid\n"); ++ return retval; ++ } ++ ++ retval = of_property_read_u32(dev->of_node, "csi_id", ++ &(ov5642_data.csi)); ++ if (retval) { ++ dev_err(dev, "csi_id missing or invalid\n"); ++ return retval; ++ } ++ ++ clk_prepare_enable(ov5642_data.sensor_clk); ++ ++ ov5642_data.io_init = ov5642_reset; ++ ov5642_data.i2c_client = client; ++ ov5642_data.pix.pixelformat = V4L2_PIX_FMT_YUYV; ++ ov5642_data.pix.width = 640; ++ ov5642_data.pix.height = 480; ++ ov5642_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | ++ V4L2_CAP_TIMEPERFRAME; ++ ov5642_data.streamcap.capturemode = 0; ++ ov5642_data.streamcap.timeperframe.denominator = DEFAULT_FPS; ++ ov5642_data.streamcap.timeperframe.numerator = 1; ++ ++ ov5642_power_on(&client->dev); ++ ++ ov5642_reset(); ++ ++ ov5642_standby(0); ++ ++ retval = ov5642_read_reg(OV5642_CHIP_ID_HIGH_BYTE, &chip_id_high); ++ if (retval < 0 || chip_id_high != 0x56) { ++ pr_warning("camera ov5642 is not found\n"); ++ clk_disable_unprepare(ov5642_data.sensor_clk); ++ return -ENODEV; ++ } ++ retval = ov5642_read_reg(OV5642_CHIP_ID_LOW_BYTE, &chip_id_low); ++ if (retval < 0 || chip_id_low != 0x42) { ++ pr_warning("camera ov5642 is not found\n"); ++ clk_disable_unprepare(ov5642_data.sensor_clk); ++ return -ENODEV; ++ } ++ ++ ov5642_standby(1); ++ ++ ov5642_int_device.priv = &ov5642_data; ++ retval = v4l2_int_device_register(&ov5642_int_device); ++ ++ clk_disable_unprepare(ov5642_data.sensor_clk); ++ ++ pr_info("camera ov5642 is found\n"); ++ return retval; ++} ++ ++/*! ++ * ov5642 I2C detach function ++ * ++ * @param client struct i2c_client * ++ * @return Error code indicating success or failure ++ */ ++static int ov5642_remove(struct i2c_client *client) ++{ ++ v4l2_int_device_unregister(&ov5642_int_device); ++ ++ if (gpo_regulator) ++ regulator_disable(gpo_regulator); ++ ++ if (analog_regulator) ++ regulator_disable(analog_regulator); ++ ++ if (core_regulator) ++ regulator_disable(core_regulator); ++ ++ if (io_regulator) ++ regulator_disable(io_regulator); ++ ++ return 0; ++} ++ ++/*! ++ * ov5642 init function ++ * Called by insmod ov5642_camera.ko. ++ * ++ * @return Error code indicating success or failure ++ */ ++static __init int ov5642_init(void) ++{ ++ u8 err; ++ ++ err = i2c_add_driver(&ov5642_i2c_driver); ++ if (err != 0) ++ pr_err("%s:driver registration failed, error=%d\n", ++ __func__, err); ++ ++ return err; ++} ++ ++/*! ++ * OV5642 cleanup function ++ * Called on rmmod ov5642_camera.ko ++ * ++ * @return Error code indicating success or failure ++ */ ++static void __exit ov5642_clean(void) ++{ ++ i2c_del_driver(&ov5642_i2c_driver); ++} ++ ++module_init(ov5642_init); ++module_exit(ov5642_clean); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("OV5642 Camera Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++MODULE_ALIAS("CSI"); +diff -Nur linux-3.14.36/drivers/media/platform/mxc/output/Kconfig linux-openelec/drivers/media/platform/mxc/output/Kconfig +--- linux-3.14.36/drivers/media/platform/mxc/output/Kconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/output/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,5 @@ ++config VIDEO_MXC_IPU_OUTPUT ++ tristate "IPU v4l2 output support" ++ depends on VIDEO_MXC_OUTPUT && MXC_IPU ++ ---help--- ++ This is the video4linux2 driver for IPU post processing video output. +diff -Nur linux-3.14.36/drivers/media/platform/mxc/output/Makefile linux-openelec/drivers/media/platform/mxc/output/Makefile +--- linux-3.14.36/drivers/media/platform/mxc/output/Makefile 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/output/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1 @@ ++obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc_vout.o +diff -Nur linux-3.14.36/drivers/media/platform/mxc/output/mxc_vout.c linux-openelec/drivers/media/platform/mxc/output/mxc_vout.c +--- linux-3.14.36/drivers/media/platform/mxc/output/mxc_vout.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/platform/mxc/output/mxc_vout.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,2265 @@ ++/* ++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define UYVY_BLACK (0x00800080) ++#define RGB_BLACK (0x0) ++#define UV_BLACK (0x80) ++#define Y_BLACK (0x0) ++ ++#define MAX_FB_NUM 6 ++#define FB_BUFS 3 ++#define VDOA_FB_BUFS (FB_BUFS - 1) ++#define VALID_HEIGHT_1080P (1080) ++#define FRAME_HEIGHT_1080P (1088) ++#define FRAME_WIDTH_1080P (1920) ++#define CHECK_TILED_1080P_DISPLAY(vout) \ ++ ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \ ++ ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\ ++ ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ ++ ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ ++ ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ ++ (((vout)->task.input.crop.h == FRAME_HEIGHT_1080P) || \ ++ ((vout)->task.input.crop.h == VALID_HEIGHT_1080P)) && \ ++ ((vout)->task.output.width == FRAME_WIDTH_1080P) && \ ++ ((vout)->task.output.height == VALID_HEIGHT_1080P) && \ ++ ((vout)->task.output.crop.w == FRAME_WIDTH_1080P) && \ ++ ((vout)->task.output.crop.h == VALID_HEIGHT_1080P)) ++#define CHECK_TILED_1080P_STREAM(vout) \ ++ ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \ ++ ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\ ++ ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ ++ ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ ++ ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ ++ ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P)) ++#define IS_PLANAR_PIXEL_FORMAT(format) \ ++ (format == IPU_PIX_FMT_NV12 || \ ++ format == IPU_PIX_FMT_YUV420P2 || \ ++ format == IPU_PIX_FMT_YUV420P || \ ++ format == IPU_PIX_FMT_YVU420P || \ ++ format == IPU_PIX_FMT_YUV422P || \ ++ format == IPU_PIX_FMT_YVU422P || \ ++ format == IPU_PIX_FMT_YUV444P) ++ ++#define NSEC_PER_FRAME_30FPS (33333333) ++ ++struct mxc_vout_fb { ++ char *name; ++ int ipu_id; ++ struct v4l2_rect crop_bounds; ++ unsigned int disp_fmt; ++ bool disp_support_csc; ++ bool disp_support_windows; ++}; ++ ++struct dma_mem { ++ void *vaddr; ++ dma_addr_t paddr; ++ size_t size; ++}; ++ ++struct mxc_vout_output { ++ int open_cnt; ++ struct fb_info *fbi; ++ unsigned long fb_smem_start; ++ unsigned long fb_smem_len; ++ struct video_device *vfd; ++ struct mutex mutex; ++ struct mutex task_lock; ++ enum v4l2_buf_type type; ++ ++ struct videobuf_queue vbq; ++ spinlock_t vbq_lock; ++ ++ struct list_head queue_list; ++ struct list_head active_list; ++ ++ struct v4l2_rect crop_bounds; ++ unsigned int disp_fmt; ++ struct mxcfb_pos win_pos; ++ bool disp_support_windows; ++ bool disp_support_csc; ++ ++ bool fmt_init; ++ bool release; ++ bool linear_bypass_pp; ++ bool vdoa_1080p; ++ bool tiled_bypass_pp; ++ struct v4l2_rect in_rect; ++ struct ipu_task task; ++ struct ipu_task vdoa_task; ++ struct dma_mem vdoa_work; ++ struct dma_mem vdoa_output[VDOA_FB_BUFS]; ++ ++ bool timer_stop; ++ struct hrtimer timer; ++ struct workqueue_struct *v4l_wq; ++ struct work_struct disp_work; ++ unsigned long frame_count; ++ unsigned long vdi_frame_cnt; ++ ktime_t start_ktime; ++ ++ int ctrl_rotate; ++ int ctrl_vflip; ++ int ctrl_hflip; ++ ++ dma_addr_t disp_bufs[FB_BUFS]; ++ ++ struct videobuf_buffer *pre1_vb; ++ struct videobuf_buffer *pre2_vb; ++}; ++ ++struct mxc_vout_dev { ++ struct device *dev; ++ struct v4l2_device v4l2_dev; ++ struct mxc_vout_output *out[MAX_FB_NUM]; ++ int out_num; ++}; ++ ++/* Driver Configuration macros */ ++#define VOUT_NAME "mxc_vout" ++ ++/* Variables configurable through module params*/ ++static int debug; ++static int vdi_rate_double; ++static int video_nr = 16; ++ ++/* Module parameters */ ++module_param(video_nr, int, S_IRUGO); ++MODULE_PARM_DESC(video_nr, "video device numbers"); ++module_param(debug, int, 0600); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++module_param(vdi_rate_double, int, 0600); ++MODULE_PARM_DESC(vdi_rate_double, "vdi frame rate double on/off"); ++ ++static const struct v4l2_fmtdesc mxc_formats[] = { ++ { ++ .description = "RGB565", ++ .pixelformat = V4L2_PIX_FMT_RGB565, ++ }, ++ { ++ .description = "BGR24", ++ .pixelformat = V4L2_PIX_FMT_BGR24, ++ }, ++ { ++ .description = "RGB24", ++ .pixelformat = V4L2_PIX_FMT_RGB24, ++ }, ++ { ++ .description = "RGB32", ++ .pixelformat = V4L2_PIX_FMT_RGB32, ++ }, ++ { ++ .description = "BGR32", ++ .pixelformat = V4L2_PIX_FMT_BGR32, ++ }, ++ { ++ .description = "NV12", ++ .pixelformat = V4L2_PIX_FMT_NV12, ++ }, ++ { ++ .description = "UYVY", ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ }, ++ { ++ .description = "YUYV", ++ .pixelformat = V4L2_PIX_FMT_YUYV, ++ }, ++ { ++ .description = "YUV422 planar", ++ .pixelformat = V4L2_PIX_FMT_YUV422P, ++ }, ++ { ++ .description = "YUV444", ++ .pixelformat = V4L2_PIX_FMT_YUV444, ++ }, ++ { ++ .description = "YUV420", ++ .pixelformat = V4L2_PIX_FMT_YUV420, ++ }, ++ { ++ .description = "YVU420", ++ .pixelformat = V4L2_PIX_FMT_YVU420, ++ }, ++ { ++ .description = "TILED NV12P", ++ .pixelformat = IPU_PIX_FMT_TILED_NV12, ++ }, ++ { ++ .description = "TILED NV12F", ++ .pixelformat = IPU_PIX_FMT_TILED_NV12F, ++ }, ++ { ++ .description = "YUV444 planar", ++ .pixelformat = IPU_PIX_FMT_YUV444P, ++ }, ++}; ++ ++#define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats)) ++ ++#define DEF_INPUT_WIDTH 320 ++#define DEF_INPUT_HEIGHT 240 ++ ++static int mxc_vidioc_streamoff(struct file *file, void *fh, ++ enum v4l2_buf_type i); ++ ++static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM]; ++static int config_disp_output(struct mxc_vout_output *vout); ++static void release_disp_output(struct mxc_vout_output *vout); ++ ++static unsigned int get_frame_size(struct mxc_vout_output *vout) ++{ ++ unsigned int size; ++ ++ if (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) ++ size = TILED_NV12_FRAME_SIZE(vout->task.input.width, ++ vout->task.input.height); ++ else if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { ++ size = TILED_NV12_FRAME_SIZE(vout->task.input.width, ++ vout->task.input.height/2); ++ size *= 2; ++ } else ++ size = vout->task.input.width * vout->task.input.height * ++ fmt_to_bpp(vout->task.input.format)/8; ++ ++ return size; ++} ++ ++static void free_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf) ++{ ++ dma_free_coherent(vout->vbq.dev, buf->size, buf->vaddr, buf->paddr); ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "free dma size:0x%x, paddr:0x%x\n", ++ buf->size, buf->paddr); ++ memset(buf, 0, sizeof(*buf)); ++} ++ ++static int alloc_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf) ++{ ++ ++ buf->vaddr = dma_alloc_coherent(vout->vbq.dev, buf->size, &buf->paddr, ++ GFP_DMA | GFP_KERNEL); ++ if (!buf->vaddr) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "cannot get dma buf size:0x%x\n", buf->size); ++ return -ENOMEM; ++ } ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "alloc dma buf size:0x%x, paddr:0x%x\n", buf->size, buf->paddr); ++ return 0; ++} ++ ++static ipu_channel_t get_ipu_channel(struct fb_info *fbi) ++{ ++ ipu_channel_t ipu_ch = CHAN_NONE; ++ mm_segment_t old_fs; ++ ++ if (fbi->fbops->fb_ioctl) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN, ++ (unsigned long)&ipu_ch); ++ set_fs(old_fs); ++ } ++ ++ return ipu_ch; ++} ++ ++static unsigned int get_ipu_fmt(struct fb_info *fbi) ++{ ++ mm_segment_t old_fs; ++ unsigned int fb_fmt; ++ ++ if (fbi->fbops->fb_ioctl) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT, ++ (unsigned long)&fb_fmt); ++ set_fs(old_fs); ++ } ++ ++ return fb_fmt; ++} ++ ++static void update_display_setting(void) ++{ ++ int i; ++ struct fb_info *fbi; ++ struct v4l2_rect bg_crop_bounds[2]; ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ fbi = registered_fb[i]; ++ ++ memset(&g_fb_setting[i], 0, sizeof(struct mxc_vout_fb)); ++ ++ if (!strncmp(fbi->fix.id, "DISP3", 5)) ++ g_fb_setting[i].ipu_id = 0; ++ else ++ g_fb_setting[i].ipu_id = 1; ++ ++ g_fb_setting[i].name = fbi->fix.id; ++ g_fb_setting[i].crop_bounds.left = 0; ++ g_fb_setting[i].crop_bounds.top = 0; ++ g_fb_setting[i].crop_bounds.width = fbi->var.xres; ++ g_fb_setting[i].crop_bounds.height = fbi->var.yres; ++ g_fb_setting[i].disp_fmt = get_ipu_fmt(fbi); ++ ++ if (get_ipu_channel(fbi) == MEM_BG_SYNC) { ++ bg_crop_bounds[g_fb_setting[i].ipu_id] = ++ g_fb_setting[i].crop_bounds; ++ g_fb_setting[i].disp_support_csc = true; ++ } else if (get_ipu_channel(fbi) == MEM_FG_SYNC) { ++ g_fb_setting[i].disp_support_csc = true; ++ g_fb_setting[i].disp_support_windows = true; ++ } ++ } ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ fbi = registered_fb[i]; ++ ++ if (get_ipu_channel(fbi) == MEM_FG_SYNC) ++ g_fb_setting[i].crop_bounds = ++ bg_crop_bounds[g_fb_setting[i].ipu_id]; ++ } ++} ++ ++/* called after g_fb_setting filled by update_display_setting */ ++static int update_setting_from_fbi(struct mxc_vout_output *vout, ++ struct fb_info *fbi) ++{ ++ int i; ++ bool found = false; ++ ++ for (i = 0; i < MAX_FB_NUM; i++) { ++ if (g_fb_setting[i].name) { ++ if (!strcmp(fbi->fix.id, g_fb_setting[i].name)) { ++ vout->crop_bounds = g_fb_setting[i].crop_bounds; ++ vout->disp_fmt = g_fb_setting[i].disp_fmt; ++ vout->disp_support_csc = ++ g_fb_setting[i].disp_support_csc; ++ vout->disp_support_windows = ++ g_fb_setting[i].disp_support_windows; ++ found = true; ++ break; ++ } ++ } ++ } ++ ++ if (!found) { ++ v4l2_err(vout->vfd->v4l2_dev, "can not find output\n"); ++ return -EINVAL; ++ } ++ strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); ++ ++ memset(&vout->task, 0, sizeof(struct ipu_task)); ++ ++ vout->task.input.width = DEF_INPUT_WIDTH; ++ vout->task.input.height = DEF_INPUT_HEIGHT; ++ vout->task.input.crop.pos.x = 0; ++ vout->task.input.crop.pos.y = 0; ++ vout->task.input.crop.w = DEF_INPUT_WIDTH; ++ vout->task.input.crop.h = DEF_INPUT_HEIGHT; ++ ++ vout->task.output.width = vout->crop_bounds.width; ++ vout->task.output.height = vout->crop_bounds.height; ++ vout->task.output.crop.pos.x = 0; ++ vout->task.output.crop.pos.y = 0; ++ vout->task.output.crop.w = vout->crop_bounds.width; ++ vout->task.output.crop.h = vout->crop_bounds.height; ++ if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) ++ vout->task.output.format = IPU_PIX_FMT_UYVY; ++ else ++ vout->task.output.format = IPU_PIX_FMT_RGB565; ++ ++ return 0; ++} ++ ++static inline unsigned long get_jiffies(struct timeval *t) ++{ ++ struct timeval cur; ++ ++ if (t->tv_usec >= 1000000) { ++ t->tv_sec += t->tv_usec / 1000000; ++ t->tv_usec = t->tv_usec % 1000000; ++ } ++ ++ do_gettimeofday(&cur); ++ if ((t->tv_sec < cur.tv_sec) ++ || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) ++ return jiffies; ++ ++ if (t->tv_usec < cur.tv_usec) { ++ cur.tv_sec = t->tv_sec - cur.tv_sec - 1; ++ cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; ++ } else { ++ cur.tv_sec = t->tv_sec - cur.tv_sec; ++ cur.tv_usec = t->tv_usec - cur.tv_usec; ++ } ++ ++ return jiffies + timeval_to_jiffies(&cur); ++} ++ ++static bool deinterlace_3_field(struct mxc_vout_output *vout) ++{ ++ return (vout->task.input.deinterlace.enable && ++ (vout->task.input.deinterlace.motion != HIGH_MOTION)); ++} ++ ++static int set_field_fmt(struct mxc_vout_output *vout, enum v4l2_field field) ++{ ++ struct ipu_deinterlace *deinterlace = &vout->task.input.deinterlace; ++ ++ switch (field) { ++ /* Images are in progressive format, not interlaced */ ++ case V4L2_FIELD_NONE: ++ case V4L2_FIELD_ANY: ++ deinterlace->enable = false; ++ deinterlace->field_fmt = 0; ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "Progressive frame.\n"); ++ break; ++ case V4L2_FIELD_INTERLACED_TB: ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "Enable deinterlace TB.\n"); ++ deinterlace->enable = true; ++ deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_TOP; ++ break; ++ case V4L2_FIELD_INTERLACED_BT: ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "Enable deinterlace BT.\n"); ++ deinterlace->enable = true; ++ deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_BOTTOM; ++ break; ++ default: ++ v4l2_err(vout->vfd->v4l2_dev, ++ "field format:%d not supported yet!\n", field); ++ return -EINVAL; ++ } ++ ++ if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "tiled fmt enable deinterlace.\n"); ++ deinterlace->enable = true; ++ } ++ ++ if (deinterlace->enable && vdi_rate_double) ++ deinterlace->field_fmt |= IPU_DEINTERLACE_RATE_EN; ++ ++ return 0; ++} ++ ++static bool is_pp_bypass(struct mxc_vout_output *vout) ++{ ++ if ((IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || ++ (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format)) ++ return false; ++ if ((vout->task.input.width == vout->task.output.width) && ++ (vout->task.input.height == vout->task.output.height) && ++ (vout->task.input.crop.w == vout->task.output.crop.w) && ++ (vout->task.input.crop.h == vout->task.output.crop.h) && ++ (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) && ++ !vout->task.input.deinterlace.enable) { ++ if (vout->disp_support_csc) ++ return true; ++ else if (!need_csc(vout->task.input.format, vout->disp_fmt)) ++ return true; ++ /* ++ * input crop show to full output which can show based on ++ * xres_virtual/yres_virtual ++ */ ++ } else if ((vout->task.input.crop.w == vout->task.output.crop.w) && ++ (vout->task.output.crop.w == vout->task.output.width) && ++ (vout->task.input.crop.h == vout->task.output.crop.h) && ++ (vout->task.output.crop.h == ++ vout->task.output.height) && ++ (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) && ++ !vout->task.input.deinterlace.enable) { ++ if (vout->disp_support_csc) ++ return true; ++ else if (!need_csc(vout->task.input.format, vout->disp_fmt)) ++ return true; ++ } ++ return false; ++} ++ ++static void setup_buf_timer(struct mxc_vout_output *vout, ++ struct videobuf_buffer *vb) ++{ ++ ktime_t expiry_time, now; ++ ++ /* if timestamp is 0, then default to 30fps */ ++ if ((vb->ts.tv_sec == 0) && (vb->ts.tv_usec == 0)) ++ expiry_time = ktime_add_ns(vout->start_ktime, ++ NSEC_PER_FRAME_30FPS * vout->frame_count); ++ else ++ expiry_time = timeval_to_ktime(vb->ts); ++ ++ now = hrtimer_cb_get_time(&vout->timer); ++ if ((now.tv64 > expiry_time.tv64)) { ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "warning: timer timeout already expired.\n"); ++ expiry_time = now; ++ } ++ ++ hrtimer_start(&vout->timer, expiry_time, HRTIMER_MODE_ABS); ++ ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "timer handler next " ++ "schedule: %lldnsecs\n", expiry_time.tv64); ++} ++ ++static int show_buf(struct mxc_vout_output *vout, int idx, ++ struct ipu_pos *ipos) ++{ ++ struct fb_info *fbi = vout->fbi; ++ struct fb_var_screeninfo var; ++ int ret; ++ u32 fb_base = 0; ++ ++ memcpy(&var, &fbi->var, sizeof(var)); ++ ++ if (vout->linear_bypass_pp || vout->tiled_bypass_pp) { ++ /* ++ * crack fb base ++ * NOTE: should not do other fb operation during v4l2 ++ */ ++ console_lock(); ++ fb_base = fbi->fix.smem_start; ++ fbi->fix.smem_start = vout->task.output.paddr; ++ fbi->var.yoffset = ipos->y + 1; ++ var.xoffset = ipos->x; ++ var.yoffset = ipos->y; ++ var.vmode |= FB_VMODE_YWRAP; ++ ret = fb_pan_display(fbi, &var); ++ fbi->fix.smem_start = fb_base; ++ console_unlock(); ++ } else { ++ console_lock(); ++ var.yoffset = idx * fbi->var.yres; ++ var.vmode &= ~FB_VMODE_YWRAP; ++ ret = fb_pan_display(fbi, &var); ++ console_unlock(); ++ } ++ ++ return ret; ++} ++ ++static void disp_work_func(struct work_struct *work) ++{ ++ struct mxc_vout_output *vout = ++ container_of(work, struct mxc_vout_output, disp_work); ++ struct videobuf_queue *q = &vout->vbq; ++ struct videobuf_buffer *vb, *vb_next = NULL; ++ unsigned long flags = 0; ++ struct ipu_pos ipos; ++ int ret = 0; ++ u32 in_fmt = 0; ++ u32 vdi_cnt = 0; ++ u32 vdi_frame; ++ u32 index = 0; ++ u32 ocrop_h = 0; ++ u32 o_height = 0; ++ u32 tiled_interlaced = 0; ++ bool tiled_fmt = false; ++ ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n"); ++ ++ spin_lock_irqsave(q->irqlock, flags); ++ ++ if (list_empty(&vout->active_list)) { ++ v4l2_warn(vout->vfd->v4l2_dev, ++ "no entry in active_list, should not be here\n"); ++ spin_unlock_irqrestore(q->irqlock, flags); ++ return; ++ } ++ ++ vb = list_first_entry(&vout->active_list, ++ struct videobuf_buffer, queue); ++ ret = set_field_fmt(vout, vb->field); ++ if (ret < 0) { ++ spin_unlock_irqrestore(q->irqlock, flags); ++ return; ++ } ++ if (deinterlace_3_field(vout)) { ++ if (list_is_singular(&vout->active_list)) { ++ if (list_empty(&vout->queue_list)) { ++ vout->timer_stop = true; ++ spin_unlock_irqrestore(q->irqlock, flags); ++ v4l2_warn(vout->vfd->v4l2_dev, ++ "no enough entry for 3 fields " ++ "deinterlacer\n"); ++ return; ++ } ++ ++ /* ++ * We need to use the next vb even if it is ++ * not on the active list. ++ */ ++ vb_next = list_first_entry(&vout->queue_list, ++ struct videobuf_buffer, queue); ++ } else ++ vb_next = list_first_entry(vout->active_list.next, ++ struct videobuf_buffer, queue); ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "cur field_fmt:%d, next field_fmt:%d.\n", ++ vb->field, vb_next->field); ++ /* repeat the last field during field format changing */ ++ if ((vb->field != vb_next->field) && ++ (vb_next->field != V4L2_FIELD_NONE)) ++ vb_next = vb; ++ } ++ ++ spin_unlock_irqrestore(q->irqlock, flags); ++ ++vdi_frame_rate_double: ++ mutex_lock(&vout->task_lock); ++ ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "v4l2 frame_cnt:%ld, vb_field:%d, fmt:%d\n", ++ vout->frame_count, vb->field, ++ vout->task.input.deinterlace.field_fmt); ++ if (vb->memory == V4L2_MEMORY_USERPTR) ++ vout->task.input.paddr = vb->baddr; ++ else ++ vout->task.input.paddr = videobuf_to_dma_contig(vb); ++ ++ if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) ++ index = vout->vdi_frame_cnt % FB_BUFS; ++ else ++ index = vout->frame_count % FB_BUFS; ++ if (vout->linear_bypass_pp) { ++ vout->task.output.paddr = vout->task.input.paddr; ++ ipos.x = vout->task.input.crop.pos.x; ++ ipos.y = vout->task.input.crop.pos.y; ++ } else { ++ if (deinterlace_3_field(vout)) { ++ if (vb->memory == V4L2_MEMORY_USERPTR) ++ vout->task.input.paddr_n = vb_next->baddr; ++ else ++ vout->task.input.paddr_n = ++ videobuf_to_dma_contig(vb_next); ++ } ++ vout->task.output.paddr = vout->disp_bufs[index]; ++ if (vout->vdoa_1080p) { ++ o_height = vout->task.output.height; ++ ocrop_h = vout->task.output.crop.h; ++ vout->task.output.height = FRAME_HEIGHT_1080P; ++ vout->task.output.crop.h = FRAME_HEIGHT_1080P; ++ } ++ tiled_fmt = ++ (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || ++ (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format); ++ if (vout->tiled_bypass_pp) { ++ ipos.x = vout->task.input.crop.pos.x; ++ ipos.y = vout->task.input.crop.pos.y; ++ } else if (tiled_fmt) { ++ vout->vdoa_task.input.paddr = vout->task.input.paddr; ++ if (deinterlace_3_field(vout)) ++ vout->vdoa_task.input.paddr_n = ++ vout->task.input.paddr_n; ++ vout->vdoa_task.output.paddr = vout->vdoa_work.paddr; ++ ret = ipu_queue_task(&vout->vdoa_task); ++ if (ret < 0) { ++ mutex_unlock(&vout->task_lock); ++ goto err; ++ } ++ vout->task.input.paddr = vout->vdoa_task.output.paddr; ++ in_fmt = vout->task.input.format; ++ vout->task.input.format = vout->vdoa_task.output.format; ++ if (vout->task.input.deinterlace.enable) { ++ tiled_interlaced = 1; ++ vout->task.input.deinterlace.enable = 0; ++ } ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "tiled queue task\n"); ++ } ++ ret = ipu_queue_task(&vout->task); ++ if ((!vout->tiled_bypass_pp) && tiled_fmt) ++ vout->task.input.format = in_fmt; ++ if (tiled_interlaced) ++ vout->task.input.deinterlace.enable = 1; ++ if (ret < 0) { ++ mutex_unlock(&vout->task_lock); ++ goto err; ++ } ++ if (vout->vdoa_1080p) { ++ vout->task.output.crop.h = ocrop_h; ++ vout->task.output.height = o_height; ++ } ++ } ++ ++ mutex_unlock(&vout->task_lock); ++ ++ ret = show_buf(vout, index, &ipos); ++ if (ret < 0) ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "show buf with ret %d\n", ret); ++ ++ if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) { ++ vdi_frame = vout->task.input.deinterlace.field_fmt ++ & IPU_DEINTERLACE_RATE_FRAME1; ++ if (vdi_frame) ++ vout->task.input.deinterlace.field_fmt &= ++ ~IPU_DEINTERLACE_RATE_FRAME1; ++ else ++ vout->task.input.deinterlace.field_fmt |= ++ IPU_DEINTERLACE_RATE_FRAME1; ++ vout->vdi_frame_cnt++; ++ vdi_cnt++; ++ if (vdi_cnt < IPU_DEINTERLACE_MAX_FRAME) ++ goto vdi_frame_rate_double; ++ } ++ spin_lock_irqsave(q->irqlock, flags); ++ ++ list_del(&vb->queue); ++ ++ /* ++ * The videobuf before the last one has been shown. Set ++ * VIDEOBUF_DONE state here to avoid tearing issue in ic bypass ++ * case, which makes sure a buffer being shown will not be ++ * dequeued to be overwritten. It also brings side-effect that ++ * the last 2 buffers can not be dequeued correctly, apps need ++ * to take care of it. ++ */ ++ if (vout->pre2_vb) { ++ vout->pre2_vb->state = VIDEOBUF_DONE; ++ wake_up_interruptible(&vout->pre2_vb->done); ++ vout->pre2_vb = NULL; ++ } ++ ++ if (vout->linear_bypass_pp) { ++ vout->pre2_vb = vout->pre1_vb; ++ vout->pre1_vb = vb; ++ } else { ++ if (vout->pre1_vb) { ++ vout->pre1_vb->state = VIDEOBUF_DONE; ++ wake_up_interruptible(&vout->pre1_vb->done); ++ vout->pre1_vb = NULL; ++ } ++ vb->state = VIDEOBUF_DONE; ++ wake_up_interruptible(&vb->done); ++ } ++ ++ vout->frame_count++; ++ ++ /* pick next queue buf to setup timer */ ++ if (list_empty(&vout->queue_list)) ++ vout->timer_stop = true; ++ else { ++ vb = list_first_entry(&vout->queue_list, ++ struct videobuf_buffer, queue); ++ setup_buf_timer(vout, vb); ++ } ++ ++ spin_unlock_irqrestore(q->irqlock, flags); ++ ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work finish one frame\n"); ++ ++ return; ++err: ++ v4l2_err(vout->vfd->v4l2_dev, "display work fail ret = %d\n", ret); ++ vout->timer_stop = true; ++ vb->state = VIDEOBUF_ERROR; ++ return; ++} ++ ++static enum hrtimer_restart mxc_vout_timer_handler(struct hrtimer *timer) ++{ ++ struct mxc_vout_output *vout = container_of(timer, ++ struct mxc_vout_output, ++ timer); ++ struct videobuf_queue *q = &vout->vbq; ++ struct videobuf_buffer *vb; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(q->irqlock, flags); ++ ++ /* ++ * put first queued entry into active, if previous entry did not ++ * finish, setup current entry's timer again. ++ */ ++ if (list_empty(&vout->queue_list)) { ++ spin_unlock_irqrestore(q->irqlock, flags); ++ return HRTIMER_NORESTART; ++ } ++ ++ /* move videobuf from queued list to active list */ ++ vb = list_first_entry(&vout->queue_list, ++ struct videobuf_buffer, queue); ++ list_del(&vb->queue); ++ list_add_tail(&vb->queue, &vout->active_list); ++ ++ if (queue_work(vout->v4l_wq, &vout->disp_work) == 0) { ++ v4l2_warn(vout->vfd->v4l2_dev, ++ "disp work was in queue already, queue buf again next time\n"); ++ list_del(&vb->queue); ++ list_add(&vb->queue, &vout->queue_list); ++ spin_unlock_irqrestore(q->irqlock, flags); ++ return HRTIMER_NORESTART; ++ } ++ ++ vb->state = VIDEOBUF_ACTIVE; ++ ++ spin_unlock_irqrestore(q->irqlock, flags); ++ ++ return HRTIMER_NORESTART; ++} ++ ++/* Video buffer call backs */ ++ ++/* ++ * Buffer setup function is called by videobuf layer when REQBUF ioctl is ++ * called. This is used to setup buffers and return size and count of ++ * buffers allocated. After the call to this buffer, videobuf layer will ++ * setup buffer queue depending on the size and count of buffers ++ */ ++static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, ++ unsigned int *size) ++{ ++ struct mxc_vout_output *vout = q->priv_data; ++ unsigned int frame_size; ++ ++ if (!vout) ++ return -EINVAL; ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) ++ return -EINVAL; ++ ++ frame_size = get_frame_size(vout); ++ *size = PAGE_ALIGN(frame_size); ++ ++ return 0; ++} ++ ++/* ++ * This function will be called when VIDIOC_QBUF ioctl is called. ++ * It prepare buffers before give out for the display. This function ++ * converts user space virtual address into physical address if userptr memory ++ * exchange mechanism is used. ++ */ ++static int mxc_vout_buffer_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ vb->state = VIDEOBUF_PREPARED; ++ return 0; ++} ++ ++/* ++ * Buffer queue funtion will be called from the videobuf layer when _QBUF ++ * ioctl is called. It is used to enqueue buffer, which is ready to be ++ * displayed. ++ * This function is protected by q->irqlock. ++ */ ++static void mxc_vout_buffer_queue(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ struct mxc_vout_output *vout = q->priv_data; ++ struct videobuf_buffer *active_vb; ++ ++ list_add_tail(&vb->queue, &vout->queue_list); ++ vb->state = VIDEOBUF_QUEUED; ++ ++ if (vout->timer_stop) { ++ if (deinterlace_3_field(vout) && ++ !list_empty(&vout->active_list)) { ++ active_vb = list_first_entry(&vout->active_list, ++ struct videobuf_buffer, queue); ++ setup_buf_timer(vout, active_vb); ++ } else { ++ setup_buf_timer(vout, vb); ++ } ++ vout->timer_stop = false; ++ } ++} ++ ++/* ++ * Buffer release function is called from videobuf layer to release buffer ++ * which are already allocated ++ */ ++static void mxc_vout_buffer_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ vb->state = VIDEOBUF_NEEDS_INIT; ++} ++ ++static int mxc_vout_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int ret; ++ struct mxc_vout_output *vout = file->private_data; ++ ++ if (!vout) ++ return -ENODEV; ++ ++ ret = videobuf_mmap_mapper(&vout->vbq, vma); ++ if (ret < 0) ++ v4l2_err(vout->vfd->v4l2_dev, ++ "offset invalid [offset=0x%lx]\n", ++ (vma->vm_pgoff << PAGE_SHIFT)); ++ ++ return ret; ++} ++ ++static int mxc_vout_release(struct file *file) ++{ ++ unsigned int ret = 0; ++ struct videobuf_queue *q; ++ struct mxc_vout_output *vout = file->private_data; ++ ++ if (!vout) ++ return 0; ++ ++ if (--vout->open_cnt == 0) { ++ q = &vout->vbq; ++ if (q->streaming) ++ mxc_vidioc_streamoff(file, vout, vout->type); ++ else { ++ release_disp_output(vout); ++ videobuf_queue_cancel(q); ++ } ++ destroy_workqueue(vout->v4l_wq); ++ ret = videobuf_mmap_free(q); ++ } ++ ++ return ret; ++} ++ ++static int mxc_vout_open(struct file *file) ++{ ++ struct mxc_vout_output *vout = NULL; ++ int ret = 0; ++ ++ vout = video_drvdata(file); ++ ++ if (vout == NULL) ++ return -ENODEV; ++ ++ if (vout->open_cnt++ == 0) { ++ vout->ctrl_rotate = 0; ++ vout->ctrl_vflip = 0; ++ vout->ctrl_hflip = 0; ++ update_display_setting(); ++ ret = update_setting_from_fbi(vout, vout->fbi); ++ if (ret < 0) ++ goto err; ++ ++ vout->v4l_wq = create_singlethread_workqueue("v4l2q"); ++ if (!vout->v4l_wq) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "Could not create work queue\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ INIT_WORK(&vout->disp_work, disp_work_func); ++ ++ INIT_LIST_HEAD(&vout->queue_list); ++ INIT_LIST_HEAD(&vout->active_list); ++ ++ vout->fmt_init = false; ++ vout->frame_count = 0; ++ vout->vdi_frame_cnt = 0; ++ ++ vout->win_pos.x = 0; ++ vout->win_pos.y = 0; ++ vout->release = true; ++ } ++ ++ file->private_data = vout; ++ ++err: ++ return ret; ++} ++ ++/* ++ * V4L2 ioctls ++ */ ++static int mxc_vidioc_querycap(struct file *file, void *fh, ++ struct v4l2_capability *cap) ++{ ++ struct mxc_vout_output *vout = fh; ++ ++ strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); ++ strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); ++ cap->bus_info[0] = '\0'; ++ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; ++ ++ return 0; ++} ++ ++static int mxc_vidioc_enum_fmt_vid_out(struct file *file, void *fh, ++ struct v4l2_fmtdesc *fmt) ++{ ++ if (fmt->index >= NUM_MXC_VOUT_FORMATS) ++ return -EINVAL; ++ ++ strlcpy(fmt->description, mxc_formats[fmt->index].description, ++ sizeof(fmt->description)); ++ fmt->pixelformat = mxc_formats[fmt->index].pixelformat; ++ ++ return 0; ++} ++ ++static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct mxc_vout_output *vout = fh; ++ struct v4l2_rect rect; ++ ++ f->fmt.pix.width = vout->task.input.width; ++ f->fmt.pix.height = vout->task.input.height; ++ f->fmt.pix.pixelformat = vout->task.input.format; ++ f->fmt.pix.sizeimage = get_frame_size(vout); ++ ++ if (f->fmt.pix.priv) { ++ rect.left = vout->task.input.crop.pos.x; ++ rect.top = vout->task.input.crop.pos.y; ++ rect.width = vout->task.input.crop.w; ++ rect.height = vout->task.input.crop.h; ++ if (copy_to_user((void __user *)f->fmt.pix.priv, ++ &rect, sizeof(rect))) ++ return -EFAULT; ++ } ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "frame_size:0x%x, pix_fmt:0x%x\n", ++ f->fmt.pix.sizeimage, ++ vout->task.input.format); ++ ++ return 0; ++} ++ ++static inline int ipu_try_task(struct mxc_vout_output *vout) ++{ ++ int ret; ++ struct ipu_task *task = &vout->task; ++ ++again: ++ ret = ipu_check_task(task); ++ if (ret != IPU_CHECK_OK) { ++ if (ret > IPU_CHECK_ERR_MIN) { ++ if (ret == IPU_CHECK_ERR_SPLIT_INPUTW_OVER || ++ ret == IPU_CHECK_ERR_W_DOWNSIZE_OVER) { ++ task->input.crop.w -= 8; ++ goto again; ++ } ++ if (ret == IPU_CHECK_ERR_SPLIT_INPUTH_OVER || ++ ret == IPU_CHECK_ERR_H_DOWNSIZE_OVER) { ++ task->input.crop.h -= 8; ++ goto again; ++ } ++ if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { ++ if (vout->disp_support_windows) { ++ task->output.width -= 8; ++ task->output.crop.w = ++ task->output.width; ++ } else ++ task->output.crop.w -= 8; ++ goto again; ++ } ++ if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { ++ if (vout->disp_support_windows) { ++ task->output.height -= 8; ++ task->output.crop.h = ++ task->output.height; ++ } else ++ task->output.crop.h -= 8; ++ goto again; ++ } ++ ret = -EINVAL; ++ } ++ } else ++ ret = 0; ++ ++ return ret; ++} ++ ++static inline int vdoaipu_try_task(struct mxc_vout_output *vout) ++{ ++ int ret; ++ int is_1080p_stream; ++ size_t size; ++ struct ipu_task *ipu_task = &vout->task; ++ struct ipu_crop *icrop = &ipu_task->input.crop; ++ struct ipu_task *vdoa_task = &vout->vdoa_task; ++ u32 deinterlace = 0; ++ u32 in_fmt; ++ ++ if (vout->task.input.deinterlace.enable) ++ deinterlace = 1; ++ ++ memset(vdoa_task, 0, sizeof(*vdoa_task)); ++ vdoa_task->output.format = IPU_PIX_FMT_NV12; ++ memcpy(&vdoa_task->input, &ipu_task->input, ++ sizeof(ipu_task->input)); ++ if ((icrop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || ++ (icrop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { ++ vdoa_task->input.crop.w = ++ ALIGN(icrop->w, IPU_PIX_FMT_TILED_NV12_MBALIGN); ++ vdoa_task->input.crop.h = ++ ALIGN(icrop->h, IPU_PIX_FMT_TILED_NV12_MBALIGN); ++ } ++ vdoa_task->output.width = vdoa_task->input.crop.w; ++ vdoa_task->output.height = vdoa_task->input.crop.h; ++ vdoa_task->output.crop.w = vdoa_task->input.crop.w; ++ vdoa_task->output.crop.h = vdoa_task->input.crop.h; ++ ++ size = PAGE_ALIGN(vdoa_task->input.crop.w * ++ vdoa_task->input.crop.h * ++ fmt_to_bpp(vdoa_task->output.format)/8); ++ if (size > vout->vdoa_work.size) { ++ if (vout->vdoa_work.vaddr) ++ free_dma_buf(vout, &vout->vdoa_work); ++ vout->vdoa_work.size = size; ++ ret = alloc_dma_buf(vout, &vout->vdoa_work); ++ if (ret < 0) ++ return ret; ++ } ++ ret = ipu_check_task(vdoa_task); ++ if (ret != IPU_CHECK_OK) ++ return -EINVAL; ++ ++ is_1080p_stream = CHECK_TILED_1080P_STREAM(vout); ++ if (is_1080p_stream) ++ ipu_task->input.crop.h = VALID_HEIGHT_1080P; ++ in_fmt = ipu_task->input.format; ++ ipu_task->input.format = vdoa_task->output.format; ++ ipu_task->input.height = vdoa_task->output.height; ++ ipu_task->input.width = vdoa_task->output.width; ++ if (deinterlace) ++ ipu_task->input.deinterlace.enable = 0; ++ ret = ipu_try_task(vout); ++ if (deinterlace) ++ ipu_task->input.deinterlace.enable = 1; ++ ipu_task->input.format = in_fmt; ++ ++ return ret; ++} ++ ++static int mxc_vout_try_task(struct mxc_vout_output *vout) ++{ ++ int ret = 0; ++ struct ipu_output *output = &vout->task.output; ++ struct ipu_input *input = &vout->task.input; ++ struct ipu_crop *crop = &input->crop; ++ u32 o_height = 0; ++ u32 ocrop_h = 0; ++ bool tiled_fmt = false; ++ bool tiled_need_pp = false; ++ ++ vout->vdoa_1080p = CHECK_TILED_1080P_DISPLAY(vout); ++ if (vout->vdoa_1080p) { ++ input->crop.h = FRAME_HEIGHT_1080P; ++ o_height = output->height; ++ ocrop_h = output->crop.h; ++ output->height = FRAME_HEIGHT_1080P; ++ output->crop.h = FRAME_HEIGHT_1080P; ++ } ++ ++ if ((IPU_PIX_FMT_TILED_NV12 == input->format) || ++ (IPU_PIX_FMT_TILED_NV12F == input->format)) { ++ if ((input->width % IPU_PIX_FMT_TILED_NV12_MBALIGN) || ++ (input->height % IPU_PIX_FMT_TILED_NV12_MBALIGN) || ++ (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN) || ++ (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "ERR: tiled fmt needs 16 pixel align.\n"); ++ return -EINVAL; ++ } ++ if ((crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || ++ (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) ++ tiled_need_pp = true; ++ } else { ++ crop->w -= crop->w % 8; ++ crop->h -= crop->h % 8; ++ } ++ /* assume task.output already set by S_CROP */ ++ vout->linear_bypass_pp = is_pp_bypass(vout); ++ if (vout->linear_bypass_pp) { ++ v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n"); ++ output->format = input->format; ++ } else { ++ /* if need CSC, choose IPU-DP or IPU_IC do it */ ++ if (vout->disp_support_csc) { ++ if (colorspaceofpixel(input->format) == YUV_CS) ++ output->format = IPU_PIX_FMT_UYVY; ++ else ++ output->format = IPU_PIX_FMT_RGB565; ++ } else { ++ if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) ++ output->format = IPU_PIX_FMT_UYVY; ++ else ++ output->format = IPU_PIX_FMT_RGB565; ++ } ++ ++ vout->tiled_bypass_pp = false; ++ if ((IPU_PIX_FMT_TILED_NV12 == input->format) || ++ (IPU_PIX_FMT_TILED_NV12F == input->format)) { ++ /* check resize/rotate/flip, or csc task */ ++ if (!(tiled_need_pp || ++ (IPU_ROTATE_NONE != output->rotate) || ++ (input->crop.w != output->crop.w) || ++ (input->crop.h != output->crop.h) || ++ (!vout->disp_support_csc && ++ (colorspaceofpixel(vout->disp_fmt) == RGB_CS))) ++ ) { ++ /* IC bypass */ ++ output->format = IPU_PIX_FMT_NV12; ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "tiled bypass pp\n"); ++ vout->tiled_bypass_pp = true; ++ } ++ tiled_fmt = true; ++ } ++ ++ if ((!vout->tiled_bypass_pp) && tiled_fmt) ++ ret = vdoaipu_try_task(vout); ++ else ++ ret = ipu_try_task(vout); ++ } ++ ++ if (vout->vdoa_1080p) { ++ output->height = o_height; ++ output->crop.h = ocrop_h; ++ } ++ ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "icrop.w:%u, icrop.h:%u, iw:%u, ih:%u," ++ "ocrop.w:%u, ocrop.h:%u, ow:%u, oh:%u\n", ++ input->crop.w, input->crop.h, ++ input->width, input->height, ++ output->crop.w, output->crop.h, ++ output->width, output->height); ++ return ret; ++} ++ ++static int mxc_vout_try_format(struct mxc_vout_output *vout, ++ struct v4l2_format *f) ++{ ++ int ret = 0; ++ struct v4l2_rect rect; ++ ++ if ((f->fmt.pix.field != V4L2_FIELD_NONE) && ++ (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format)) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "progressive tiled fmt should used V4L2_FIELD_NONE!\n"); ++ return -EINVAL; ++ } ++ ++ if (f->fmt.pix.priv && copy_from_user(&rect, ++ (void __user *)f->fmt.pix.priv, sizeof(rect))) ++ return -EFAULT; ++ ++ vout->task.input.width = f->fmt.pix.width; ++ vout->task.input.height = f->fmt.pix.height; ++ vout->task.input.format = f->fmt.pix.pixelformat; ++ ++ ret = set_field_fmt(vout, f->fmt.pix.field); ++ if (ret < 0) ++ return ret; ++ ++ if (f->fmt.pix.priv) { ++ vout->task.input.crop.pos.x = rect.left; ++ vout->task.input.crop.pos.y = rect.top; ++ vout->task.input.crop.w = rect.width; ++ vout->task.input.crop.h = rect.height; ++ } else { ++ vout->task.input.crop.pos.x = 0; ++ vout->task.input.crop.pos.y = 0; ++ vout->task.input.crop.w = f->fmt.pix.width; ++ vout->task.input.crop.h = f->fmt.pix.height; ++ } ++ memcpy(&vout->in_rect, &vout->task.input.crop, sizeof(vout->in_rect)); ++ ++ ret = mxc_vout_try_task(vout); ++ if (!ret) { ++ if (f->fmt.pix.priv) { ++ rect.width = vout->task.input.crop.w; ++ rect.height = vout->task.input.crop.h; ++ if (copy_to_user((void __user *)f->fmt.pix.priv, ++ &rect, sizeof(rect))) ++ ret = -EFAULT; ++ } else { ++ f->fmt.pix.width = vout->task.input.crop.w; ++ f->fmt.pix.height = vout->task.input.crop.h; ++ } ++ } ++ ++ return ret; ++} ++ ++static bool mxc_vout_need_fb_reconfig(struct mxc_vout_output *vout, ++ struct mxc_vout_output *pre_vout) ++{ ++ if (!vout->vbq.streaming) ++ return false; ++ ++ if (vout->tiled_bypass_pp) ++ return true; ++ ++ if (vout->linear_bypass_pp != pre_vout->linear_bypass_pp) ++ return true; ++ ++ /* cropped output resolution or format are changed */ ++ if (vout->task.output.format != pre_vout->task.output.format || ++ vout->task.output.crop.w != pre_vout->task.output.crop.w || ++ vout->task.output.crop.h != pre_vout->task.output.crop.h) ++ return true; ++ ++ /* overlay: window position or resolution are changed */ ++ if (vout->disp_support_windows && ++ (vout->win_pos.x != pre_vout->win_pos.x || ++ vout->win_pos.y != pre_vout->win_pos.y || ++ vout->task.output.width != pre_vout->task.output.width || ++ vout->task.output.height != pre_vout->task.output.height)) ++ return true; ++ ++ /* background: cropped position is changed */ ++ if (!vout->disp_support_windows && ++ (vout->task.output.crop.pos.x != ++ pre_vout->task.output.crop.pos.x || ++ vout->task.output.crop.pos.y != ++ pre_vout->task.output.crop.pos.y)) ++ return true; ++ ++ return false; ++} ++ ++static int mxc_vidioc_s_fmt_vid_out(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct mxc_vout_output *vout = fh; ++ int ret = 0; ++ ++ if (vout->vbq.streaming) ++ return -EBUSY; ++ ++ mutex_lock(&vout->task_lock); ++ ret = mxc_vout_try_format(vout, f); ++ if (ret >= 0) ++ vout->fmt_init = true; ++ mutex_unlock(&vout->task_lock); ++ ++ return ret; ++} ++ ++static int mxc_vidioc_cropcap(struct file *file, void *fh, ++ struct v4l2_cropcap *cropcap) ++{ ++ struct mxc_vout_output *vout = fh; ++ ++ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ return -EINVAL; ++ ++ cropcap->bounds = vout->crop_bounds; ++ cropcap->defrect = vout->crop_bounds; ++ ++ return 0; ++} ++ ++static int mxc_vidioc_g_crop(struct file *file, void *fh, ++ struct v4l2_crop *crop) ++{ ++ struct mxc_vout_output *vout = fh; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ return -EINVAL; ++ ++ if (vout->disp_support_windows) { ++ crop->c.left = vout->win_pos.x; ++ crop->c.top = vout->win_pos.y; ++ crop->c.width = vout->task.output.width; ++ crop->c.height = vout->task.output.height; ++ } else { ++ if (vout->task.output.crop.w && vout->task.output.crop.h) { ++ crop->c.left = vout->task.output.crop.pos.x; ++ crop->c.top = vout->task.output.crop.pos.y; ++ crop->c.width = vout->task.output.crop.w; ++ crop->c.height = vout->task.output.crop.h; ++ } else { ++ crop->c.left = 0; ++ crop->c.top = 0; ++ crop->c.width = vout->task.output.width; ++ crop->c.height = vout->task.output.height; ++ } ++ } ++ ++ return 0; ++} ++ ++static int mxc_vidioc_s_crop(struct file *file, void *fh, ++ const struct v4l2_crop *crop) ++{ ++ struct mxc_vout_output *vout = fh, *pre_vout; ++ struct v4l2_rect *b = &vout->crop_bounds; ++ struct v4l2_crop fix_up_crop; ++ int ret = 0; ++ ++ memcpy(&fix_up_crop, crop, sizeof(*crop)); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ return -EINVAL; ++ ++ if (crop->c.width < 0 || crop->c.height < 0) ++ return -EINVAL; ++ ++ if (crop->c.width == 0) ++ fix_up_crop.c.width = b->width - b->left; ++ if (crop->c.height == 0) ++ fix_up_crop.c.height = b->height - b->top; ++ ++ if (crop->c.top < b->top) ++ fix_up_crop.c.top = b->top; ++ if (crop->c.top >= b->top + b->height) ++ fix_up_crop.c.top = b->top + b->height - 1; ++ if (crop->c.height > b->top - crop->c.top + b->height) ++ fix_up_crop.c.height = ++ b->top - fix_up_crop.c.top + b->height; ++ ++ if (crop->c.left < b->left) ++ fix_up_crop.c.left = b->left; ++ if (crop->c.left >= b->left + b->width) ++ fix_up_crop.c.left = b->left + b->width - 1; ++ if (crop->c.width > b->left - crop->c.left + b->width) ++ fix_up_crop.c.width = ++ b->left - fix_up_crop.c.left + b->width; ++ ++ /* stride line limitation */ ++ fix_up_crop.c.height -= fix_up_crop.c.height % 8; ++ fix_up_crop.c.width -= fix_up_crop.c.width % 8; ++ if ((fix_up_crop.c.width <= 0) || (fix_up_crop.c.height <= 0) || ++ ((fix_up_crop.c.left + fix_up_crop.c.width) > ++ (b->left + b->width)) || ++ ((fix_up_crop.c.top + fix_up_crop.c.height) > ++ (b->top + b->height))) { ++ v4l2_err(vout->vfd->v4l2_dev, "s_crop err: %d, %d, %d, %d", ++ fix_up_crop.c.left, fix_up_crop.c.top, ++ fix_up_crop.c.width, fix_up_crop.c.height); ++ return -EINVAL; ++ } ++ ++ /* the same setting, return */ ++ if (vout->disp_support_windows) { ++ if ((vout->win_pos.x == fix_up_crop.c.left) && ++ (vout->win_pos.y == fix_up_crop.c.top) && ++ (vout->task.output.crop.w == fix_up_crop.c.width) && ++ (vout->task.output.crop.h == fix_up_crop.c.height)) ++ return 0; ++ } else { ++ if ((vout->task.output.crop.pos.x == fix_up_crop.c.left) && ++ (vout->task.output.crop.pos.y == fix_up_crop.c.top) && ++ (vout->task.output.crop.w == fix_up_crop.c.width) && ++ (vout->task.output.crop.h == fix_up_crop.c.height)) ++ return 0; ++ } ++ ++ pre_vout = vmalloc(sizeof(*pre_vout)); ++ if (!pre_vout) ++ return -ENOMEM; ++ ++ /* wait current work finish */ ++ if (vout->vbq.streaming) ++ flush_workqueue(vout->v4l_wq); ++ ++ mutex_lock(&vout->task_lock); ++ ++ memcpy(pre_vout, vout, sizeof(*vout)); ++ ++ if (vout->disp_support_windows) { ++ vout->task.output.crop.pos.x = 0; ++ vout->task.output.crop.pos.y = 0; ++ vout->win_pos.x = fix_up_crop.c.left; ++ vout->win_pos.y = fix_up_crop.c.top; ++ vout->task.output.width = fix_up_crop.c.width; ++ vout->task.output.height = fix_up_crop.c.height; ++ } else { ++ vout->task.output.crop.pos.x = fix_up_crop.c.left; ++ vout->task.output.crop.pos.y = fix_up_crop.c.top; ++ } ++ ++ vout->task.output.crop.w = fix_up_crop.c.width; ++ vout->task.output.crop.h = fix_up_crop.c.height; ++ ++ /* ++ * must S_CROP before S_FMT, for fist time S_CROP, will not check ++ * ipu task, it will check in S_FMT, after S_FMT, S_CROP should ++ * check ipu task too. ++ */ ++ if (vout->fmt_init) { ++ memcpy(&vout->task.input.crop, &vout->in_rect, ++ sizeof(vout->in_rect)); ++ ret = mxc_vout_try_task(vout); ++ if (ret < 0) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "vout check task failed\n"); ++ memcpy(vout, pre_vout, sizeof(*vout)); ++ goto done; ++ } ++ ++ if (mxc_vout_need_fb_reconfig(vout, pre_vout)) { ++ ret = config_disp_output(vout); ++ if (ret < 0) ++ v4l2_err(vout->vfd->v4l2_dev, ++ "Config display output failed\n"); ++ } ++ } ++ ++done: ++ vfree(pre_vout); ++ mutex_unlock(&vout->task_lock); ++ ++ return ret; ++} ++ ++static int mxc_vidioc_queryctrl(struct file *file, void *fh, ++ struct v4l2_queryctrl *ctrl) ++{ ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ROTATE: ++ ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); ++ break; ++ case V4L2_CID_VFLIP: ++ ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); ++ break; ++ case V4L2_CID_HFLIP: ++ ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); ++ break; ++ case V4L2_CID_MXC_MOTION: ++ ret = v4l2_ctrl_query_fill(ctrl, 0, 2, 1, 0); ++ break; ++ default: ++ ctrl->name[0] = '\0'; ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++static int mxc_vidioc_g_ctrl(struct file *file, void *fh, ++ struct v4l2_control *ctrl) ++{ ++ int ret = 0; ++ struct mxc_vout_output *vout = fh; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ROTATE: ++ ctrl->value = vout->ctrl_rotate; ++ break; ++ case V4L2_CID_VFLIP: ++ ctrl->value = vout->ctrl_vflip; ++ break; ++ case V4L2_CID_HFLIP: ++ ctrl->value = vout->ctrl_hflip; ++ break; ++ case V4L2_CID_MXC_MOTION: ++ if (vout->task.input.deinterlace.enable) ++ ctrl->value = vout->task.input.deinterlace.motion; ++ else ++ ctrl->value = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++static void setup_task_rotation(struct mxc_vout_output *vout) ++{ ++ if (vout->ctrl_rotate == 0) { ++ if (vout->ctrl_vflip && vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_180; ++ else if (vout->ctrl_vflip) ++ vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; ++ else if (vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; ++ else ++ vout->task.output.rotate = IPU_ROTATE_NONE; ++ } else if (vout->ctrl_rotate == 90) { ++ if (vout->ctrl_vflip && vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_90_LEFT; ++ else if (vout->ctrl_vflip) ++ vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; ++ else if (vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; ++ else ++ vout->task.output.rotate = IPU_ROTATE_90_RIGHT; ++ } else if (vout->ctrl_rotate == 180) { ++ if (vout->ctrl_vflip && vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_NONE; ++ else if (vout->ctrl_vflip) ++ vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; ++ else if (vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; ++ else ++ vout->task.output.rotate = IPU_ROTATE_180; ++ } else if (vout->ctrl_rotate == 270) { ++ if (vout->ctrl_vflip && vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_90_RIGHT; ++ else if (vout->ctrl_vflip) ++ vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; ++ else if (vout->ctrl_hflip) ++ vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; ++ else ++ vout->task.output.rotate = IPU_ROTATE_90_LEFT; ++ } ++} ++ ++static int mxc_vidioc_s_ctrl(struct file *file, void *fh, ++ struct v4l2_control *ctrl) ++{ ++ int ret = 0; ++ struct mxc_vout_output *vout = fh, *pre_vout; ++ ++ pre_vout = vmalloc(sizeof(*pre_vout)); ++ if (!pre_vout) ++ return -ENOMEM; ++ ++ /* wait current work finish */ ++ if (vout->vbq.streaming) ++ flush_workqueue(vout->v4l_wq); ++ ++ mutex_lock(&vout->task_lock); ++ ++ memcpy(pre_vout, vout, sizeof(*vout)); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ROTATE: ++ { ++ vout->ctrl_rotate = (ctrl->value/90) * 90; ++ if (vout->ctrl_rotate > 270) ++ vout->ctrl_rotate = 270; ++ setup_task_rotation(vout); ++ break; ++ } ++ case V4L2_CID_VFLIP: ++ { ++ vout->ctrl_vflip = ctrl->value; ++ setup_task_rotation(vout); ++ break; ++ } ++ case V4L2_CID_HFLIP: ++ { ++ vout->ctrl_hflip = ctrl->value; ++ setup_task_rotation(vout); ++ break; ++ } ++ case V4L2_CID_MXC_MOTION: ++ { ++ vout->task.input.deinterlace.motion = ctrl->value; ++ break; ++ } ++ default: ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ if (vout->fmt_init) { ++ memcpy(&vout->task.input.crop, &vout->in_rect, ++ sizeof(vout->in_rect)); ++ ret = mxc_vout_try_task(vout); ++ if (ret < 0) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "vout check task failed\n"); ++ memcpy(vout, pre_vout, sizeof(*vout)); ++ goto done; ++ } ++ ++ if (mxc_vout_need_fb_reconfig(vout, pre_vout)) { ++ ret = config_disp_output(vout); ++ if (ret < 0) ++ v4l2_err(vout->vfd->v4l2_dev, ++ "Config display output failed\n"); ++ } ++ } ++ ++done: ++ vfree(pre_vout); ++ mutex_unlock(&vout->task_lock); ++ ++ return ret; ++} ++ ++static int mxc_vidioc_reqbufs(struct file *file, void *fh, ++ struct v4l2_requestbuffers *req) ++{ ++ int ret = 0; ++ struct mxc_vout_output *vout = fh; ++ struct videobuf_queue *q = &vout->vbq; ++ ++ if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ return -EINVAL; ++ ++ /* should not be here after streaming, videobuf_reqbufs will control */ ++ mutex_lock(&vout->task_lock); ++ ++ ret = videobuf_reqbufs(q, req); ++ ++ mutex_unlock(&vout->task_lock); ++ return ret; ++} ++ ++static int mxc_vidioc_querybuf(struct file *file, void *fh, ++ struct v4l2_buffer *b) ++{ ++ int ret; ++ struct mxc_vout_output *vout = fh; ++ ++ ret = videobuf_querybuf(&vout->vbq, b); ++ if (!ret) { ++ /* return physical address */ ++ struct videobuf_buffer *vb = vout->vbq.bufs[b->index]; ++ if (b->flags & V4L2_BUF_FLAG_MAPPED) ++ b->m.offset = videobuf_to_dma_contig(vb); ++ } ++ ++ return ret; ++} ++ ++static int mxc_vidioc_qbuf(struct file *file, void *fh, ++ struct v4l2_buffer *buffer) ++{ ++ struct mxc_vout_output *vout = fh; ++ ++ return videobuf_qbuf(&vout->vbq, buffer); ++} ++ ++static int mxc_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct mxc_vout_output *vout = fh; ++ ++ if (!vout->vbq.streaming) ++ return -EINVAL; ++ ++ if (file->f_flags & O_NONBLOCK) ++ return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 1); ++ else ++ return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 0); ++} ++ ++static int set_window_position(struct mxc_vout_output *vout, ++ struct mxcfb_pos *pos) ++{ ++ struct fb_info *fbi = vout->fbi; ++ mm_segment_t old_fs; ++ int ret = 0; ++ ++ if (vout->disp_support_windows) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ret = fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS, ++ (unsigned long)pos); ++ set_fs(old_fs); ++ } ++ ++ return ret; ++} ++ ++static int config_disp_output(struct mxc_vout_output *vout) ++{ ++ struct dma_mem *buf = NULL; ++ struct fb_info *fbi = vout->fbi; ++ struct fb_var_screeninfo var; ++ struct mxcfb_pos pos; ++ int i, fb_num, ret; ++ u32 fb_base; ++ u32 size; ++ u32 display_buf_size; ++ u32 *pixel = NULL; ++ u32 color; ++ int j; ++ ++ memcpy(&var, &fbi->var, sizeof(var)); ++ fb_base = fbi->fix.smem_start; ++ ++ var.xres = vout->task.output.width; ++ var.yres = vout->task.output.height; ++ if (vout->linear_bypass_pp || vout->tiled_bypass_pp) { ++ fb_num = 1; ++ /* input crop */ ++ if (vout->task.input.width > vout->task.output.width) ++ var.xres_virtual = vout->task.input.width; ++ else ++ var.xres_virtual = var.xres; ++ if (vout->task.input.height > vout->task.output.height) ++ var.yres_virtual = vout->task.input.height; ++ else ++ var.yres_virtual = var.yres; ++ var.rotate = vout->task.output.rotate; ++ var.vmode |= FB_VMODE_YWRAP; ++ } else { ++ fb_num = FB_BUFS; ++ var.xres_virtual = var.xres; ++ var.yres_virtual = fb_num * var.yres; ++ var.vmode &= ~FB_VMODE_YWRAP; ++ } ++ var.bits_per_pixel = fmt_to_bpp(vout->task.output.format); ++ var.nonstd = vout->task.output.format; ++ ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "set display fb to %d %d\n", ++ var.xres, var.yres); ++ ++ /* ++ * To setup the overlay fb from scratch without ++ * the last time overlay fb position or resolution's ++ * impact, we take the following steps: ++ * - blank fb ++ * - set fb position to the starting point ++ * - reconfigure fb ++ * - set fb position to a specific point ++ * - unblank fb ++ * This procedure applies to non-overlay fbs as well. ++ */ ++ console_lock(); ++ fbi->flags |= FBINFO_MISC_USEREVENT; ++ fb_blank(fbi, FB_BLANK_POWERDOWN); ++ fbi->flags &= ~FBINFO_MISC_USEREVENT; ++ console_unlock(); ++ ++ pos.x = 0; ++ pos.y = 0; ++ ret = set_window_position(vout, &pos); ++ if (ret < 0) { ++ v4l2_err(vout->vfd->v4l2_dev, "failed to set fb position " ++ "to starting point\n"); ++ return ret; ++ } ++ ++ /* Init display channel through fb API */ ++ var.yoffset = 0; ++ var.activate |= FB_ACTIVATE_FORCE; ++ console_lock(); ++ fbi->flags |= FBINFO_MISC_USEREVENT; ++ ret = fb_set_var(fbi, &var); ++ fbi->flags &= ~FBINFO_MISC_USEREVENT; ++ console_unlock(); ++ if (ret < 0) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "ERR:%s fb_set_var ret:%d\n", __func__, ret); ++ return ret; ++ } ++ ++ ret = set_window_position(vout, &vout->win_pos); ++ if (ret < 0) { ++ v4l2_err(vout->vfd->v4l2_dev, "failed to set fb position\n"); ++ return ret; ++ } ++ ++ if (vout->linear_bypass_pp || vout->tiled_bypass_pp) ++ display_buf_size = fbi->fix.line_length * fbi->var.yres_virtual; ++ else ++ display_buf_size = fbi->fix.line_length * fbi->var.yres; ++ for (i = 0; i < fb_num; i++) ++ vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; ++ if (vout->tiled_bypass_pp) { ++ size = PAGE_ALIGN(vout->task.input.crop.w * ++ vout->task.input.crop.h * ++ fmt_to_bpp(vout->task.output.format)/8); ++ if (size > vout->vdoa_output[0].size) { ++ for (i = 0; i < VDOA_FB_BUFS; i++) { ++ buf = &vout->vdoa_output[i]; ++ if (buf->vaddr) ++ free_dma_buf(vout, buf); ++ buf->size = size; ++ ret = alloc_dma_buf(vout, buf); ++ if (ret < 0) ++ goto err; ++ } ++ } ++ for (i = fb_num; i < (fb_num + VDOA_FB_BUFS); i++) ++ vout->disp_bufs[i] = ++ vout->vdoa_output[i - fb_num].paddr; ++ } ++ vout->fb_smem_len = fbi->fix.smem_len; ++ vout->fb_smem_start = fbi->fix.smem_start; ++ if (fb_base != fbi->fix.smem_start) { ++ v4l2_dbg(1, debug, vout->vfd->v4l2_dev, ++ "realloc fb mem size:0x%x@0x%lx,old paddr @0x%x\n", ++ fbi->fix.smem_len, fbi->fix.smem_start, fb_base); ++ } ++ ++ /* fill black when video config changed */ ++ color = colorspaceofpixel(vout->task.output.format) == YUV_CS ? ++ UYVY_BLACK : RGB_BLACK; ++ if (IS_PLANAR_PIXEL_FORMAT(vout->task.output.format)) { ++ size = display_buf_size * 8 / ++ fmt_to_bpp(vout->task.output.format); ++ memset(fbi->screen_base, Y_BLACK, size); ++ memset(fbi->screen_base + size, UV_BLACK, ++ display_buf_size - size); ++ } else { ++ pixel = (u32 *)fbi->screen_base; ++ for (i = 0; i < (display_buf_size >> 2); i++) ++ *pixel++ = color; ++ } ++ console_lock(); ++ fbi->flags |= FBINFO_MISC_USEREVENT; ++ ret = fb_blank(fbi, FB_BLANK_UNBLANK); ++ fbi->flags &= ~FBINFO_MISC_USEREVENT; ++ console_unlock(); ++ vout->release = false; ++ ++ return ret; ++err: ++ for (j = i - 1; j >= 0; j--) { ++ buf = &vout->vdoa_output[j]; ++ if (buf->vaddr) ++ free_dma_buf(vout, buf); ++ } ++ return ret; ++} ++ ++static inline void wait_for_vsync(struct mxc_vout_output *vout) ++{ ++ struct fb_info *fbi = vout->fbi; ++ mm_segment_t old_fs; ++ ++ if (fbi->fbops->fb_ioctl) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC, ++ (unsigned long)NULL); ++ set_fs(old_fs); ++ } ++ ++ return; ++} ++ ++static void release_disp_output(struct mxc_vout_output *vout) ++{ ++ struct fb_info *fbi = vout->fbi; ++ struct mxcfb_pos pos; ++ ++ if (vout->release) ++ return; ++ console_lock(); ++ fbi->flags |= FBINFO_MISC_USEREVENT; ++ fb_blank(fbi, FB_BLANK_POWERDOWN); ++ fbi->flags &= ~FBINFO_MISC_USEREVENT; ++ console_unlock(); ++ ++ /* restore pos to 0,0 avoid fb pan display hang? */ ++ pos.x = 0; ++ pos.y = 0; ++ set_window_position(vout, &pos); ++ ++ if (get_ipu_channel(fbi) == MEM_BG_SYNC) { ++ console_lock(); ++ fbi->fix.smem_start = vout->disp_bufs[0]; ++ fbi->flags |= FBINFO_MISC_USEREVENT; ++ fb_blank(fbi, FB_BLANK_UNBLANK); ++ fbi->flags &= ~FBINFO_MISC_USEREVENT; ++ console_unlock(); ++ ++ } ++ ++ vout->release = true; ++} ++ ++static int mxc_vidioc_streamon(struct file *file, void *fh, ++ enum v4l2_buf_type i) ++{ ++ struct mxc_vout_output *vout = fh; ++ struct videobuf_queue *q = &vout->vbq; ++ int ret; ++ ++ if (q->streaming) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "video output already run\n"); ++ ret = -EBUSY; ++ goto done; ++ } ++ ++ if (deinterlace_3_field(vout) && list_is_singular(&q->stream)) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "deinterlacing: need queue 2 frame before streamon\n"); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = config_disp_output(vout); ++ if (ret < 0) { ++ v4l2_err(vout->vfd->v4l2_dev, ++ "Config display output failed\n"); ++ goto done; ++ } ++ ++ hrtimer_init(&vout->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); ++ vout->timer.function = mxc_vout_timer_handler; ++ vout->timer_stop = true; ++ ++ vout->start_ktime = hrtimer_cb_get_time(&vout->timer); ++ ++ vout->pre1_vb = NULL; ++ vout->pre2_vb = NULL; ++ ++ ret = videobuf_streamon(q); ++done: ++ return ret; ++} ++ ++static int mxc_vidioc_streamoff(struct file *file, void *fh, ++ enum v4l2_buf_type i) ++{ ++ struct mxc_vout_output *vout = fh; ++ struct videobuf_queue *q = &vout->vbq; ++ int ret = 0; ++ ++ if (q->streaming) { ++ flush_workqueue(vout->v4l_wq); ++ ++ hrtimer_cancel(&vout->timer); ++ ++ /* ++ * Wait for 2 vsyncs to make sure ++ * frames are drained on triple ++ * buffer. ++ */ ++ wait_for_vsync(vout); ++ wait_for_vsync(vout); ++ ++ release_disp_output(vout); ++ ++ ret = videobuf_streamoff(&vout->vbq); ++ } ++ INIT_LIST_HEAD(&vout->queue_list); ++ INIT_LIST_HEAD(&vout->active_list); ++ ++ return ret; ++} ++ ++static const struct v4l2_ioctl_ops mxc_vout_ioctl_ops = { ++ .vidioc_querycap = mxc_vidioc_querycap, ++ .vidioc_enum_fmt_vid_out = mxc_vidioc_enum_fmt_vid_out, ++ .vidioc_g_fmt_vid_out = mxc_vidioc_g_fmt_vid_out, ++ .vidioc_s_fmt_vid_out = mxc_vidioc_s_fmt_vid_out, ++ .vidioc_cropcap = mxc_vidioc_cropcap, ++ .vidioc_g_crop = mxc_vidioc_g_crop, ++ .vidioc_s_crop = mxc_vidioc_s_crop, ++ .vidioc_queryctrl = mxc_vidioc_queryctrl, ++ .vidioc_g_ctrl = mxc_vidioc_g_ctrl, ++ .vidioc_s_ctrl = mxc_vidioc_s_ctrl, ++ .vidioc_reqbufs = mxc_vidioc_reqbufs, ++ .vidioc_querybuf = mxc_vidioc_querybuf, ++ .vidioc_qbuf = mxc_vidioc_qbuf, ++ .vidioc_dqbuf = mxc_vidioc_dqbuf, ++ .vidioc_streamon = mxc_vidioc_streamon, ++ .vidioc_streamoff = mxc_vidioc_streamoff, ++}; ++ ++static const struct v4l2_file_operations mxc_vout_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = mxc_vout_mmap, ++ .open = mxc_vout_open, ++ .release = mxc_vout_release, ++}; ++ ++static struct video_device mxc_vout_template = { ++ .name = "MXC Video Output", ++ .fops = &mxc_vout_fops, ++ .ioctl_ops = &mxc_vout_ioctl_ops, ++ .release = video_device_release, ++}; ++ ++static struct videobuf_queue_ops mxc_vout_vbq_ops = { ++ .buf_setup = mxc_vout_buffer_setup, ++ .buf_prepare = mxc_vout_buffer_prepare, ++ .buf_release = mxc_vout_buffer_release, ++ .buf_queue = mxc_vout_buffer_queue, ++}; ++ ++static void mxc_vout_free_output(struct mxc_vout_dev *dev) ++{ ++ int i; ++ int j; ++ struct mxc_vout_output *vout; ++ struct video_device *vfd; ++ ++ for (i = 0; i < dev->out_num; i++) { ++ vout = dev->out[i]; ++ vfd = vout->vfd; ++ if (vout->vdoa_work.vaddr) ++ free_dma_buf(vout, &vout->vdoa_work); ++ for (j = 0; j < VDOA_FB_BUFS; j++) { ++ if (vout->vdoa_output[j].vaddr) ++ free_dma_buf(vout, &vout->vdoa_output[j]); ++ } ++ if (vfd) { ++ if (!video_is_registered(vfd)) ++ video_device_release(vfd); ++ else ++ video_unregister_device(vfd); ++ } ++ kfree(vout); ++ } ++} ++ ++static int mxc_vout_setup_output(struct mxc_vout_dev *dev) ++{ ++ struct videobuf_queue *q; ++ struct fb_info *fbi; ++ struct mxc_vout_output *vout; ++ int i, ret = 0; ++ ++ update_display_setting(); ++ ++ /* all output/overlay based on fb */ ++ for (i = 0; i < num_registered_fb; i++) { ++ fbi = registered_fb[i]; ++ ++ vout = kzalloc(sizeof(struct mxc_vout_output), GFP_KERNEL); ++ if (!vout) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ dev->out[dev->out_num] = vout; ++ dev->out_num++; ++ ++ vout->fbi = fbi; ++ vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ vout->vfd = video_device_alloc(); ++ if (!vout->vfd) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ *vout->vfd = mxc_vout_template; ++ vout->vfd->debug = debug; ++ vout->vfd->v4l2_dev = &dev->v4l2_dev; ++ vout->vfd->lock = &vout->mutex; ++ vout->vfd->vfl_dir = VFL_DIR_TX; ++ ++ mutex_init(&vout->mutex); ++ mutex_init(&vout->task_lock); ++ ++ strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); ++ ++ video_set_drvdata(vout->vfd, vout); ++ ++ if (video_register_device(vout->vfd, ++ VFL_TYPE_GRABBER, video_nr + i) < 0) { ++ ret = -ENODEV; ++ break; ++ } ++ ++ q = &vout->vbq; ++ q->dev = dev->dev; ++ spin_lock_init(&vout->vbq_lock); ++ videobuf_queue_dma_contig_init(q, &mxc_vout_vbq_ops, q->dev, ++ &vout->vbq_lock, vout->type, V4L2_FIELD_NONE, ++ sizeof(struct videobuf_buffer), vout, NULL); ++ ++ v4l2_info(vout->vfd->v4l2_dev, "V4L2 device registered as %s\n", ++ video_device_node_name(vout->vfd)); ++ ++ } ++ ++ return ret; ++} ++ ++static int mxc_vout_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct mxc_vout_dev *dev; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ dev->dev = &pdev->dev; ++ dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), GFP_KERNEL); ++ *dev->dev->dma_mask = DMA_BIT_MASK(32); ++ dev->dev->coherent_dma_mask = DMA_BIT_MASK(32); ++ ++ ret = v4l2_device_register(dev->dev, &dev->v4l2_dev); ++ if (ret) { ++ dev_err(dev->dev, "v4l2_device_register failed\n"); ++ goto free_dev; ++ } ++ ++ ret = mxc_vout_setup_output(dev); ++ if (ret < 0) ++ goto rel_vdev; ++ ++ return 0; ++ ++rel_vdev: ++ mxc_vout_free_output(dev); ++ v4l2_device_unregister(&dev->v4l2_dev); ++free_dev: ++ kfree(dev); ++ return ret; ++} ++ ++static int mxc_vout_remove(struct platform_device *pdev) ++{ ++ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); ++ struct mxc_vout_dev *dev = container_of(v4l2_dev, struct ++ mxc_vout_dev, v4l2_dev); ++ ++ mxc_vout_free_output(dev); ++ v4l2_device_unregister(v4l2_dev); ++ kfree(dev); ++ return 0; ++} ++ ++static const struct of_device_id mxc_v4l2_dt_ids[] = { ++ { .compatible = "fsl,mxc_v4l2_output", }, ++ { /* sentinel */ } ++}; ++ ++static struct platform_driver mxc_vout_driver = { ++ .driver = { ++ .name = "mxc_v4l2_output", ++ .of_match_table = mxc_v4l2_dt_ids, ++ }, ++ .probe = mxc_vout_probe, ++ .remove = mxc_vout_remove, ++}; ++ ++static int __init mxc_vout_init(void) ++{ ++ if (platform_driver_register(&mxc_vout_driver) != 0) { ++ printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void mxc_vout_cleanup(void) ++{ ++ platform_driver_unregister(&mxc_vout_driver); ++} ++ ++module_init(mxc_vout_init); ++module_exit(mxc_vout_cleanup); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("V4L2-driver for MXC video output"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/rc/imon.c linux-openelec/drivers/media/rc/imon.c +--- linux-3.14.36/drivers/media/rc/imon.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/rc/imon.c 2015-07-24 18:03:30.068842002 -0500 +@@ -1344,6 +1344,17 @@ + } + } else { + /* ++ * For users without stabilized, just ignore any value getting ++ * to close to the diagonal. ++ */ ++ if ((abs(rel_y) < 2 && abs(rel_x) < 2) || ++ abs(abs(rel_y) - abs(rel_x)) < 2 ) { ++ spin_lock_irqsave(&ictx->kc_lock, flags); ++ ictx->kc = KEY_UNKNOWN; ++ spin_unlock_irqrestore(&ictx->kc_lock, flags); ++ return; ++ } ++ /* + * Hack alert: instead of using keycodes, we have + * to use hard-coded scancodes here... + */ +diff -Nur linux-3.14.36/drivers/media/rc/ir-rc6-decoder.c linux-openelec/drivers/media/rc/ir-rc6-decoder.c +--- linux-3.14.36/drivers/media/rc/ir-rc6-decoder.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/rc/ir-rc6-decoder.c 2015-07-24 18:03:30.040842002 -0500 +@@ -39,7 +39,6 @@ + #define RC6_STARTBIT_MASK 0x08 /* for the header bits */ + #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ + #define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */ +-#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */ + #ifndef CHAR_BIT + #define CHAR_BIT 8 /* Normally in */ + #endif +@@ -244,9 +243,8 @@ + } + + scancode = data->body; +- if (data->count == RC6_6A_32_NBITS && +- (scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { +- /* MCE RC */ ++ if (data->count == RC6_6A_32_NBITS) { ++ /* MCE compatible RC */ + toggle = (scancode & RC6_6A_MCE_TOGGLE_MASK) ? 1 : 0; + scancode &= ~RC6_6A_MCE_TOGGLE_MASK; + } else { +diff -Nur linux-3.14.36/drivers/media/rc/ir-rc6-decoder.c.orig linux-openelec/drivers/media/rc/ir-rc6-decoder.c.orig +--- linux-3.14.36/drivers/media/rc/ir-rc6-decoder.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/rc/ir-rc6-decoder.c.orig 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,300 @@ ++/* ir-rc6-decoder.c - A decoder for the RC6 IR protocol ++ * ++ * Copyright (C) 2010 by David Härdeman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include "rc-core-priv.h" ++#include ++ ++/* ++ * This decoder currently supports: ++ * RC6-0-16 (standard toggle bit in header) ++ * RC6-6A-20 (no toggle bit) ++ * RC6-6A-24 (no toggle bit) ++ * RC6-6A-32 (MCE version with toggle bit in body) ++ */ ++ ++#define RC6_UNIT 444444 /* nanosecs */ ++#define RC6_HEADER_NBITS 4 /* not including toggle bit */ ++#define RC6_0_NBITS 16 ++#define RC6_6A_32_NBITS 32 ++#define RC6_6A_NBITS 128 /* Variable 8..128 */ ++#define RC6_PREFIX_PULSE (6 * RC6_UNIT) ++#define RC6_PREFIX_SPACE (2 * RC6_UNIT) ++#define RC6_BIT_START (1 * RC6_UNIT) ++#define RC6_BIT_END (1 * RC6_UNIT) ++#define RC6_TOGGLE_START (2 * RC6_UNIT) ++#define RC6_TOGGLE_END (2 * RC6_UNIT) ++#define RC6_SUFFIX_SPACE (6 * RC6_UNIT) ++#define RC6_MODE_MASK 0x07 /* for the header bits */ ++#define RC6_STARTBIT_MASK 0x08 /* for the header bits */ ++#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ ++#define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */ ++#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */ ++#ifndef CHAR_BIT ++#define CHAR_BIT 8 /* Normally in */ ++#endif ++ ++enum rc6_mode { ++ RC6_MODE_0, ++ RC6_MODE_6A, ++ RC6_MODE_UNKNOWN, ++}; ++ ++enum rc6_state { ++ STATE_INACTIVE, ++ STATE_PREFIX_SPACE, ++ STATE_HEADER_BIT_START, ++ STATE_HEADER_BIT_END, ++ STATE_TOGGLE_START, ++ STATE_TOGGLE_END, ++ STATE_BODY_BIT_START, ++ STATE_BODY_BIT_END, ++ STATE_FINISHED, ++}; ++ ++static enum rc6_mode rc6_mode(struct rc6_dec *data) ++{ ++ switch (data->header & RC6_MODE_MASK) { ++ case 0: ++ return RC6_MODE_0; ++ case 6: ++ if (!data->toggle) ++ return RC6_MODE_6A; ++ /* fall through */ ++ default: ++ return RC6_MODE_UNKNOWN; ++ } ++} ++ ++/** ++ * ir_rc6_decode() - Decode one RC6 pulse or space ++ * @dev: the struct rc_dev descriptor of the device ++ * @ev: the struct ir_raw_event descriptor of the pulse/space ++ * ++ * This function returns -EINVAL if the pulse violates the state machine ++ */ ++static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) ++{ ++ struct rc6_dec *data = &dev->raw->rc6; ++ u32 scancode; ++ u8 toggle; ++ ++ if (!(dev->enabled_protocols & ++ (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | ++ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE))) ++ return 0; ++ ++ if (!is_timing_event(ev)) { ++ if (ev.reset) ++ data->state = STATE_INACTIVE; ++ return 0; ++ } ++ ++ if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) ++ goto out; ++ ++again: ++ IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n", ++ data->state, TO_US(ev.duration), TO_STR(ev.pulse)); ++ ++ if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) ++ return 0; ++ ++ switch (data->state) { ++ ++ case STATE_INACTIVE: ++ if (!ev.pulse) ++ break; ++ ++ /* Note: larger margin on first pulse since each RC6_UNIT ++ is quite short and some hardware takes some time to ++ adjust to the signal */ ++ if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT)) ++ break; ++ ++ data->state = STATE_PREFIX_SPACE; ++ data->count = 0; ++ return 0; ++ ++ case STATE_PREFIX_SPACE: ++ if (ev.pulse) ++ break; ++ ++ if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2)) ++ break; ++ ++ data->state = STATE_HEADER_BIT_START; ++ data->header = 0; ++ return 0; ++ ++ case STATE_HEADER_BIT_START: ++ if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) ++ break; ++ ++ data->header <<= 1; ++ if (ev.pulse) ++ data->header |= 1; ++ data->count++; ++ data->state = STATE_HEADER_BIT_END; ++ return 0; ++ ++ case STATE_HEADER_BIT_END: ++ if (!is_transition(&ev, &dev->raw->prev_ev)) ++ break; ++ ++ if (data->count == RC6_HEADER_NBITS) ++ data->state = STATE_TOGGLE_START; ++ else ++ data->state = STATE_HEADER_BIT_START; ++ ++ decrease_duration(&ev, RC6_BIT_END); ++ goto again; ++ ++ case STATE_TOGGLE_START: ++ if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2)) ++ break; ++ ++ data->toggle = ev.pulse; ++ data->state = STATE_TOGGLE_END; ++ return 0; ++ ++ case STATE_TOGGLE_END: ++ if (!is_transition(&ev, &dev->raw->prev_ev) || ++ !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) ++ break; ++ ++ if (!(data->header & RC6_STARTBIT_MASK)) { ++ IR_dprintk(1, "RC6 invalid start bit\n"); ++ break; ++ } ++ ++ data->state = STATE_BODY_BIT_START; ++ decrease_duration(&ev, RC6_TOGGLE_END); ++ data->count = 0; ++ data->body = 0; ++ ++ switch (rc6_mode(data)) { ++ case RC6_MODE_0: ++ data->wanted_bits = RC6_0_NBITS; ++ break; ++ case RC6_MODE_6A: ++ data->wanted_bits = RC6_6A_NBITS; ++ break; ++ default: ++ IR_dprintk(1, "RC6 unknown mode\n"); ++ goto out; ++ } ++ goto again; ++ ++ case STATE_BODY_BIT_START: ++ if (eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) { ++ /* Discard LSB's that won't fit in data->body */ ++ if (data->count++ < CHAR_BIT * sizeof data->body) { ++ data->body <<= 1; ++ if (ev.pulse) ++ data->body |= 1; ++ } ++ data->state = STATE_BODY_BIT_END; ++ return 0; ++ } else if (RC6_MODE_6A == rc6_mode(data) && !ev.pulse && ++ geq_margin(ev.duration, RC6_SUFFIX_SPACE, RC6_UNIT / 2)) { ++ data->state = STATE_FINISHED; ++ goto again; ++ } ++ break; ++ ++ case STATE_BODY_BIT_END: ++ if (!is_transition(&ev, &dev->raw->prev_ev)) ++ break; ++ ++ if (data->count == data->wanted_bits) ++ data->state = STATE_FINISHED; ++ else ++ data->state = STATE_BODY_BIT_START; ++ ++ decrease_duration(&ev, RC6_BIT_END); ++ goto again; ++ ++ case STATE_FINISHED: ++ if (ev.pulse) ++ break; ++ ++ switch (rc6_mode(data)) { ++ case RC6_MODE_0: ++ scancode = data->body; ++ toggle = data->toggle; ++ IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", ++ scancode, toggle); ++ break; ++ case RC6_MODE_6A: ++ if (data->count > CHAR_BIT * sizeof data->body) { ++ IR_dprintk(1, "RC6 too many (%u) data bits\n", ++ data->count); ++ goto out; ++ } ++ ++ scancode = data->body; ++ if (data->count == RC6_6A_32_NBITS && ++ (scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { ++ /* MCE RC */ ++ toggle = (scancode & RC6_6A_MCE_TOGGLE_MASK) ? 1 : 0; ++ scancode &= ~RC6_6A_MCE_TOGGLE_MASK; ++ } else { ++ toggle = 0; ++ } ++ IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", ++ scancode, toggle); ++ break; ++ default: ++ IR_dprintk(1, "RC6 unknown mode\n"); ++ goto out; ++ } ++ ++ rc_keydown(dev, scancode, toggle); ++ data->state = STATE_INACTIVE; ++ return 0; ++ } ++ ++out: ++ IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n", ++ data->state, TO_US(ev.duration), TO_STR(ev.pulse)); ++ data->state = STATE_INACTIVE; ++ return -EINVAL; ++} ++ ++static struct ir_raw_handler rc6_handler = { ++ .protocols = RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | ++ RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | ++ RC_BIT_RC6_MCE, ++ .decode = ir_rc6_decode, ++}; ++ ++static int __init ir_rc6_decode_init(void) ++{ ++ ir_raw_handler_register(&rc6_handler); ++ ++ printk(KERN_INFO "IR RC6 protocol handler initialized\n"); ++ return 0; ++} ++ ++static void __exit ir_rc6_decode_exit(void) ++{ ++ ir_raw_handler_unregister(&rc6_handler); ++} ++ ++module_init(ir_rc6_decode_init); ++module_exit(ir_rc6_decode_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Härdeman "); ++MODULE_DESCRIPTION("RC6 IR protocol decoder"); +diff -Nur linux-3.14.36/drivers/media/rc/keymaps/Makefile linux-openelec/drivers/media/rc/keymaps/Makefile +--- linux-3.14.36/drivers/media/rc/keymaps/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/rc/keymaps/Makefile 2015-07-24 18:03:30.128842002 -0500 +@@ -28,6 +28,7 @@ + rc-dm1105-nec.o \ + rc-dntv-live-dvb-t.o \ + rc-dntv-live-dvbt-pro.o \ ++ rc-dvbsky.o \ + rc-em-terratec.o \ + rc-encore-enltv2.o \ + rc-encore-enltv.o \ +diff -Nur linux-3.14.36/drivers/media/rc/keymaps/rc-dvbsky.c linux-openelec/drivers/media/rc/keymaps/rc-dvbsky.c +--- linux-3.14.36/drivers/media/rc/keymaps/rc-dvbsky.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/rc/keymaps/rc-dvbsky.c 2015-07-24 18:03:30.128842002 -0500 +@@ -0,0 +1,78 @@ ++/* rc-dvbsky.c - Keytable for Dvbsky Remote Controllers ++ * ++ * keymap imported from ir-keymaps.c ++ * ++ * ++ * Copyright (c) 2010-2012 by Nibble Max ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++/* ++ * This table contains the complete RC5 code, instead of just the data part ++ */ ++ ++static struct rc_map_table rc5_dvbsky[] = { ++ { 0x0000, KEY_0 }, ++ { 0x0001, KEY_1 }, ++ { 0x0002, KEY_2 }, ++ { 0x0003, KEY_3 }, ++ { 0x0004, KEY_4 }, ++ { 0x0005, KEY_5 }, ++ { 0x0006, KEY_6 }, ++ { 0x0007, KEY_7 }, ++ { 0x0008, KEY_8 }, ++ { 0x0009, KEY_9 }, ++ { 0x000a, KEY_MUTE }, ++ { 0x000d, KEY_OK }, ++ { 0x000b, KEY_STOP }, ++ { 0x000c, KEY_EXIT }, ++ { 0x000e, KEY_CAMERA }, /*Snap shot*/ ++ { 0x000f, KEY_SUBTITLE }, /*PIP*/ ++ { 0x0010, KEY_VOLUMEUP }, ++ { 0x0011, KEY_VOLUMEDOWN }, ++ { 0x0012, KEY_FAVORITES }, ++ { 0x0013, KEY_LIST }, /*Info*/ ++ { 0x0016, KEY_PAUSE }, ++ { 0x0017, KEY_PLAY }, ++ { 0x001f, KEY_RECORD }, ++ { 0x0020, KEY_CHANNELDOWN }, ++ { 0x0021, KEY_CHANNELUP }, ++ { 0x0025, KEY_POWER2 }, ++ { 0x0026, KEY_REWIND }, ++ { 0x0027, KEY_FASTFORWARD }, ++ { 0x0029, KEY_LAST }, ++ { 0x002b, KEY_MENU }, ++ { 0x002c, KEY_EPG }, ++ { 0x002d, KEY_ZOOM }, ++}; ++ ++static struct rc_map_list rc5_dvbsky_map = { ++ .map = { ++ .scan = rc5_dvbsky, ++ .size = ARRAY_SIZE(rc5_dvbsky), ++ .rc_type = RC_TYPE_RC5, ++ .name = RC_MAP_DVBSKY, ++ } ++}; ++ ++static int __init init_rc_map_rc5_dvbsky(void) ++{ ++ return rc_map_register(&rc5_dvbsky_map); ++} ++ ++static void __exit exit_rc_map_rc5_dvbsky(void) ++{ ++ rc_map_unregister(&rc5_dvbsky_map); ++} ++ ++module_init(init_rc_map_rc5_dvbsky) ++module_exit(exit_rc_map_rc5_dvbsky) ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Nibble Max "); +diff -Nur linux-3.14.36/drivers/media/rc/mceusb.c linux-openelec/drivers/media/rc/mceusb.c +--- linux-3.14.36/drivers/media/rc/mceusb.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/rc/mceusb.c 2015-07-24 18:03:30.372842002 -0500 +@@ -200,6 +200,7 @@ + #define VENDOR_CONEXANT 0x0572 + #define VENDOR_TWISTEDMELON 0x2596 + #define VENDOR_HAUPPAUGE 0x2040 ++#define VENDOR_ADAPTEC 0x03f3 + + enum mceusb_model_type { + MCE_GEN2 = 0, /* Most boards */ +@@ -316,6 +317,9 @@ + /* SMK/I-O Data GV-MC7/RCKIT Receiver */ + { USB_DEVICE(VENDOR_SMK, 0x0353), + .driver_info = MCE_GEN2_NO_TX }, ++ /* SMK Manufacturing, Inc. Receiver */ ++ { USB_DEVICE(VENDOR_SMK, 0x0357), ++ .driver_info = MCE_GEN2_NO_TX }, + /* Tatung eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TATUNG, 0x9150) }, + /* Shuttle eHome Infrared Transceiver */ +@@ -358,6 +362,8 @@ + { USB_DEVICE(VENDOR_FORMOSA, 0xe015) }, + /* Formosa21 / eHome Infrared Receiver */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe016) }, ++ /* Formosa21 / eHome Infrared Receiver */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) }, + /* Formosa aim / Trust MCE Infrared Receiver */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe017), + .driver_info = MCE_GEN2_NO_TX }, +@@ -409,6 +415,8 @@ + /* Hauppauge WINTV-HVR-HVR 930C-HD - based on cx231xx */ + { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb130), + .driver_info = HAUPPAUGE_CX_HYBRID_TV }, ++ /* Adaptec / HP eHome Receiver */ ++ { USB_DEVICE(VENDOR_ADAPTEC, 0x0094) }, + /* Terminating entry */ + { } + }; +@@ -753,11 +761,18 @@ + } + + /* outbound data */ +- pipe = usb_sndintpipe(ir->usbdev, +- ir->usb_ep_out->bEndpointAddress); +- usb_fill_int_urb(async_urb, ir->usbdev, pipe, +- async_buf, size, mce_async_callback, +- ir, ir->usb_ep_out->bInterval); ++ if (usb_endpoint_xfer_int(ir->usb_ep_out)) { ++ pipe = usb_sndintpipe(ir->usbdev, ++ ir->usb_ep_out->bEndpointAddress); ++ usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf, ++ size, mce_async_callback, ir, ++ ir->usb_ep_out->bInterval); ++ } else { ++ pipe = usb_sndbulkpipe(ir->usbdev, ++ ir->usb_ep_out->bEndpointAddress); ++ usb_fill_bulk_urb(async_urb, ir->usbdev, pipe, async_buf, ++ size, mce_async_callback, ir); ++ } + memcpy(async_buf, data, size); + + } else if (urb_type == MCEUSB_RX) { +@@ -1275,34 +1290,26 @@ + for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { + ep = &idesc->endpoint[i].desc; + +- if ((ep_in == NULL) +- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) +- == USB_DIR_IN) +- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) +- == USB_ENDPOINT_XFER_BULK) +- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) +- == USB_ENDPOINT_XFER_INT))) { +- +- ep_in = ep; +- ep_in->bmAttributes = USB_ENDPOINT_XFER_INT; +- ep_in->bInterval = 1; +- mce_dbg(&intf->dev, "acceptable inbound endpoint " +- "found\n"); ++ if (ep_in == NULL) { ++ if (usb_endpoint_is_bulk_in(ep)) { ++ ep_in = ep; ++ mce_dbg(&intf->dev, "acceptable bulk inbound endpoint found\n"); ++ } else if (usb_endpoint_is_int_in(ep)) { ++ ep_in = ep; ++ ep_in->bInterval = 1; ++ mce_dbg(&intf->dev, "acceptable interrupt inbound endpoint found\n"); ++ } + } + +- if ((ep_out == NULL) +- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) +- == USB_DIR_OUT) +- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) +- == USB_ENDPOINT_XFER_BULK) +- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) +- == USB_ENDPOINT_XFER_INT))) { +- +- ep_out = ep; +- ep_out->bmAttributes = USB_ENDPOINT_XFER_INT; +- ep_out->bInterval = 1; +- mce_dbg(&intf->dev, "acceptable outbound endpoint " +- "found\n"); ++ if (ep_out == NULL) { ++ if (usb_endpoint_is_bulk_out(ep)) { ++ ep_out = ep; ++ mce_dbg(&intf->dev, "acceptable bulk outbound endpoint found\n"); ++ } else if (usb_endpoint_is_int_out(ep)) { ++ ep_out = ep; ++ ep_out->bInterval = 1; ++ mce_dbg(&intf->dev, "acceptable interrupt outbound endpoint found\n"); ++ } + } + } + if (ep_in == NULL) { +@@ -1310,7 +1317,11 @@ + return -ENODEV; + } + +- pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); ++ if (usb_endpoint_xfer_int(ep_in)) { ++ pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); ++ } else { ++ pipe = usb_rcvbulkpipe(dev, ep_in->bEndpointAddress); ++ } + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL); +diff -Nur linux-3.14.36/drivers/media/rc/mceusb.c.orig linux-openelec/drivers/media/rc/mceusb.c.orig +--- linux-3.14.36/drivers/media/rc/mceusb.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/rc/mceusb.c.orig 2015-07-24 18:03:29.988842002 -0500 +@@ -0,0 +1,1467 @@ ++/* ++ * Driver for USB Windows Media Center Ed. eHome Infrared Transceivers ++ * ++ * Copyright (c) 2010-2011, Jarod Wilson ++ * ++ * Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan ++ * Conti, Martin Blatter and Daniel Melander, the latter of which was ++ * in turn also based on the lirc_atiusb driver by Paul Miller. The ++ * two mce drivers were merged into one by Jarod Wilson, with transmit ++ * support for the 1st-gen device added primarily by Patrick Calhoun, ++ * with a bit of tweaks by Jarod. Debugging improvements and proper ++ * support for what appears to be 3rd-gen hardware added by Jarod. ++ * Initial port from lirc driver to ir-core drivery by Jarod, based ++ * partially on a port to an earlier proposed IR infrastructure by ++ * Jon Smirl, which included enhancements and simplifications to the ++ * incoming IR buffer parsing routines. ++ * ++ * Updated in July of 2011 with the aid of Microsoft's official ++ * remote/transceiver requirements and specification document, found at ++ * download.microsoft.com, title ++ * Windows-Media-Center-RC-IR-Collection-Green-Button-Specification-03-08-2011-V2.pdf ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "1.92" ++#define DRIVER_AUTHOR "Jarod Wilson " ++#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \ ++ "device driver" ++#define DRIVER_NAME "mceusb" ++ ++#define USB_BUFLEN 32 /* USB reception buffer length */ ++#define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ ++#define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ ++ ++/* MCE constants */ ++#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */ ++#define MCE_TIME_UNIT 50 /* Approx 50us resolution */ ++#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */ ++#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */ ++#define MCE_IRDATA_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */ ++#define MCE_IRDATA_TRAILER 0x80 /* End of IR data */ ++#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */ ++#define MCE_DEFAULT_TX_MASK 0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */ ++#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */ ++#define MCE_PULSE_MASK 0x7f /* Pulse mask */ ++#define MCE_MAX_PULSE_LENGTH 0x7f /* Longest transmittable pulse symbol */ ++ ++/* ++ * The interface between the host and the IR hardware is command-response ++ * based. All commands and responses have a consistent format, where a lead ++ * byte always identifies the type of data following it. The lead byte has ++ * a port value in the 3 highest bits and a length value in the 5 lowest ++ * bits. ++ * ++ * The length field is overloaded, with a value of 11111 indicating that the ++ * following byte is a command or response code, and the length of the entire ++ * message is determined by the code. If the length field is not 11111, then ++ * it specifies the number of bytes of port data that follow. ++ */ ++#define MCE_CMD 0x1f ++#define MCE_PORT_IR 0x4 /* (0x4 << 5) | MCE_CMD = 0x9f */ ++#define MCE_PORT_SYS 0x7 /* (0x7 << 5) | MCE_CMD = 0xff */ ++#define MCE_PORT_SER 0x6 /* 0xc0 thru 0xdf flush & 0x1f bytes */ ++#define MCE_PORT_MASK 0xe0 /* Mask out command bits */ ++ ++/* Command port headers */ ++#define MCE_CMD_PORT_IR 0x9f /* IR-related cmd/rsp */ ++#define MCE_CMD_PORT_SYS 0xff /* System (non-IR) device cmd/rsp */ ++ ++/* Commands that set device state (2-4 bytes in length) */ ++#define MCE_CMD_RESET 0xfe /* Reset device, 2 bytes */ ++#define MCE_CMD_RESUME 0xaa /* Resume device after error, 2 bytes */ ++#define MCE_CMD_SETIRCFS 0x06 /* Set tx carrier, 4 bytes */ ++#define MCE_CMD_SETIRTIMEOUT 0x0c /* Set timeout, 4 bytes */ ++#define MCE_CMD_SETIRTXPORTS 0x08 /* Set tx ports, 3 bytes */ ++#define MCE_CMD_SETIRRXPORTEN 0x14 /* Set rx ports, 3 bytes */ ++#define MCE_CMD_FLASHLED 0x23 /* Flash receiver LED, 2 bytes */ ++ ++/* Commands that query device state (all 2 bytes, unless noted) */ ++#define MCE_CMD_GETIRCFS 0x07 /* Get carrier */ ++#define MCE_CMD_GETIRTIMEOUT 0x0d /* Get timeout */ ++#define MCE_CMD_GETIRTXPORTS 0x13 /* Get tx ports */ ++#define MCE_CMD_GETIRRXPORTEN 0x15 /* Get rx ports */ ++#define MCE_CMD_GETPORTSTATUS 0x11 /* Get tx port status, 3 bytes */ ++#define MCE_CMD_GETIRNUMPORTS 0x16 /* Get number of ports */ ++#define MCE_CMD_GETWAKESOURCE 0x17 /* Get wake source */ ++#define MCE_CMD_GETEMVER 0x22 /* Get emulator interface version */ ++#define MCE_CMD_GETDEVDETAILS 0x21 /* Get device details (em ver2 only) */ ++#define MCE_CMD_GETWAKESUPPORT 0x20 /* Get wake details (em ver2 only) */ ++#define MCE_CMD_GETWAKEVERSION 0x18 /* Get wake pattern (em ver2 only) */ ++ ++/* Misc commands */ ++#define MCE_CMD_NOP 0xff /* No operation */ ++ ++/* Responses to commands (non-error cases) */ ++#define MCE_RSP_EQIRCFS 0x06 /* tx carrier, 4 bytes */ ++#define MCE_RSP_EQIRTIMEOUT 0x0c /* rx timeout, 4 bytes */ ++#define MCE_RSP_GETWAKESOURCE 0x17 /* wake source, 3 bytes */ ++#define MCE_RSP_EQIRTXPORTS 0x08 /* tx port mask, 3 bytes */ ++#define MCE_RSP_EQIRRXPORTEN 0x14 /* rx port mask, 3 bytes */ ++#define MCE_RSP_GETPORTSTATUS 0x11 /* tx port status, 7 bytes */ ++#define MCE_RSP_EQIRRXCFCNT 0x15 /* rx carrier count, 4 bytes */ ++#define MCE_RSP_EQIRNUMPORTS 0x16 /* number of ports, 4 bytes */ ++#define MCE_RSP_EQWAKESUPPORT 0x20 /* wake capabilities, 3 bytes */ ++#define MCE_RSP_EQWAKEVERSION 0x18 /* wake pattern details, 6 bytes */ ++#define MCE_RSP_EQDEVDETAILS 0x21 /* device capabilities, 3 bytes */ ++#define MCE_RSP_EQEMVER 0x22 /* emulator interface ver, 3 bytes */ ++#define MCE_RSP_FLASHLED 0x23 /* success flashing LED, 2 bytes */ ++ ++/* Responses to error cases, must send MCE_CMD_RESUME to clear them */ ++#define MCE_RSP_CMD_ILLEGAL 0xfe /* illegal command for port, 2 bytes */ ++#define MCE_RSP_TX_TIMEOUT 0x81 /* tx timed out, 2 bytes */ ++ ++/* Misc commands/responses not defined in the MCE remote/transceiver spec */ ++#define MCE_CMD_SIG_END 0x01 /* End of signal */ ++#define MCE_CMD_PING 0x03 /* Ping device */ ++#define MCE_CMD_UNKNOWN 0x04 /* Unknown */ ++#define MCE_CMD_UNKNOWN2 0x05 /* Unknown */ ++#define MCE_CMD_UNKNOWN3 0x09 /* Unknown */ ++#define MCE_CMD_UNKNOWN4 0x0a /* Unknown */ ++#define MCE_CMD_G_REVISION 0x0b /* Get hw/sw revision */ ++#define MCE_CMD_UNKNOWN5 0x0e /* Unknown */ ++#define MCE_CMD_UNKNOWN6 0x0f /* Unknown */ ++#define MCE_CMD_UNKNOWN8 0x19 /* Unknown */ ++#define MCE_CMD_UNKNOWN9 0x1b /* Unknown */ ++#define MCE_CMD_NULL 0x00 /* These show up various places... */ ++ ++/* if buf[i] & MCE_PORT_MASK == 0x80 and buf[i] != MCE_CMD_PORT_IR, ++ * then we're looking at a raw IR data sample */ ++#define MCE_COMMAND_IRDATA 0x80 ++#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */ ++ ++/* module parameters */ ++#ifdef CONFIG_USB_DEBUG ++static bool debug = 1; ++#else ++static bool debug; ++#endif ++ ++#define mce_dbg(dev, fmt, ...) \ ++ do { \ ++ if (debug) \ ++ dev_info(dev, fmt, ## __VA_ARGS__); \ ++ } while (0) ++ ++/* general constants */ ++#define SEND_FLAG_IN_PROGRESS 1 ++#define SEND_FLAG_COMPLETE 2 ++#define RECV_FLAG_IN_PROGRESS 3 ++#define RECV_FLAG_COMPLETE 4 ++ ++#define MCEUSB_RX 1 ++#define MCEUSB_TX 2 ++ ++#define VENDOR_PHILIPS 0x0471 ++#define VENDOR_SMK 0x0609 ++#define VENDOR_TATUNG 0x1460 ++#define VENDOR_GATEWAY 0x107b ++#define VENDOR_SHUTTLE 0x1308 ++#define VENDOR_SHUTTLE2 0x051c ++#define VENDOR_MITSUMI 0x03ee ++#define VENDOR_TOPSEED 0x1784 ++#define VENDOR_RICAVISION 0x179d ++#define VENDOR_ITRON 0x195d ++#define VENDOR_FIC 0x1509 ++#define VENDOR_LG 0x043e ++#define VENDOR_MICROSOFT 0x045e ++#define VENDOR_FORMOSA 0x147a ++#define VENDOR_FINTEK 0x1934 ++#define VENDOR_PINNACLE 0x2304 ++#define VENDOR_ECS 0x1019 ++#define VENDOR_WISTRON 0x0fb8 ++#define VENDOR_COMPRO 0x185b ++#define VENDOR_NORTHSTAR 0x04eb ++#define VENDOR_REALTEK 0x0bda ++#define VENDOR_TIVO 0x105a ++#define VENDOR_CONEXANT 0x0572 ++#define VENDOR_TWISTEDMELON 0x2596 ++#define VENDOR_HAUPPAUGE 0x2040 ++#define VENDOR_ADAPTEC 0x03f3 ++ ++enum mceusb_model_type { ++ MCE_GEN2 = 0, /* Most boards */ ++ MCE_GEN1, ++ MCE_GEN3, ++ MCE_GEN2_TX_INV, ++ POLARIS_EVK, ++ CX_HYBRID_TV, ++ MULTIFUNCTION, ++ TIVO_KIT, ++ MCE_GEN2_NO_TX, ++ HAUPPAUGE_CX_HYBRID_TV, ++}; ++ ++struct mceusb_model { ++ u32 mce_gen1:1; ++ u32 mce_gen2:1; ++ u32 mce_gen3:1; ++ u32 tx_mask_normal:1; ++ u32 no_tx:1; ++ ++ int ir_intfnum; ++ ++ const char *rc_map; /* Allow specify a per-board map */ ++ const char *name; /* per-board name */ ++}; ++ ++static const struct mceusb_model mceusb_model[] = { ++ [MCE_GEN1] = { ++ .mce_gen1 = 1, ++ .tx_mask_normal = 1, ++ }, ++ [MCE_GEN2] = { ++ .mce_gen2 = 1, ++ }, ++ [MCE_GEN2_NO_TX] = { ++ .mce_gen2 = 1, ++ .no_tx = 1, ++ }, ++ [MCE_GEN2_TX_INV] = { ++ .mce_gen2 = 1, ++ .tx_mask_normal = 1, ++ }, ++ [MCE_GEN3] = { ++ .mce_gen3 = 1, ++ .tx_mask_normal = 1, ++ }, ++ [POLARIS_EVK] = { ++ /* ++ * In fact, the EVK is shipped without ++ * remotes, but we should have something handy, ++ * to allow testing it ++ */ ++ .rc_map = RC_MAP_HAUPPAUGE, ++ .name = "Conexant Hybrid TV (cx231xx) MCE IR", ++ }, ++ [CX_HYBRID_TV] = { ++ .no_tx = 1, /* tx isn't wired up at all */ ++ .name = "Conexant Hybrid TV (cx231xx) MCE IR", ++ }, ++ [HAUPPAUGE_CX_HYBRID_TV] = { ++ .rc_map = RC_MAP_HAUPPAUGE, ++ .no_tx = 1, /* eeprom says it has no tx */ ++ .name = "Conexant Hybrid TV (cx231xx) MCE IR no TX", ++ }, ++ [MULTIFUNCTION] = { ++ .mce_gen2 = 1, ++ .ir_intfnum = 2, ++ }, ++ [TIVO_KIT] = { ++ .mce_gen2 = 1, ++ .rc_map = RC_MAP_TIVO, ++ }, ++}; ++ ++static struct usb_device_id mceusb_dev_table[] = { ++ /* Original Microsoft MCE IR Transceiver (often HP-branded) */ ++ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d), ++ .driver_info = MCE_GEN1 }, ++ /* Philips Infrared Transceiver - Sahara branded */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x0608) }, ++ /* Philips Infrared Transceiver - HP branded */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x060c), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* Philips SRM5100 */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x060d) }, ++ /* Philips Infrared Transceiver - Omaura */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x060f) }, ++ /* Philips Infrared Transceiver - Spinel plus */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x0613) }, ++ /* Philips eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x0815) }, ++ /* Philips/Spinel plus IR transceiver for ASUS */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x206c) }, ++ /* Philips/Spinel plus IR transceiver for ASUS */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x2088) }, ++ /* Philips IR transceiver (Dell branded) */ ++ { USB_DEVICE(VENDOR_PHILIPS, 0x2093), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* Realtek MCE IR Receiver and card reader */ ++ { USB_DEVICE(VENDOR_REALTEK, 0x0161), ++ .driver_info = MULTIFUNCTION }, ++ /* SMK/Toshiba G83C0004D410 */ ++ { USB_DEVICE(VENDOR_SMK, 0x031d), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* SMK eHome Infrared Transceiver (Sony VAIO) */ ++ { USB_DEVICE(VENDOR_SMK, 0x0322), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* bundled with Hauppauge PVR-150 */ ++ { USB_DEVICE(VENDOR_SMK, 0x0334), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* SMK eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_SMK, 0x0338) }, ++ /* SMK/I-O Data GV-MC7/RCKIT Receiver */ ++ { USB_DEVICE(VENDOR_SMK, 0x0353), ++ .driver_info = MCE_GEN2_NO_TX }, ++ /* Tatung eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_TATUNG, 0x9150) }, ++ /* Shuttle eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) }, ++ /* Shuttle eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) }, ++ /* Gateway eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_GATEWAY, 0x3009) }, ++ /* Mitsumi */ ++ { USB_DEVICE(VENDOR_MITSUMI, 0x2501) }, ++ /* Topseed eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_TOPSEED, 0x0001), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* Topseed HP eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_TOPSEED, 0x0006), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* Topseed eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_TOPSEED, 0x0007), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* Topseed eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_TOPSEED, 0x0008), ++ .driver_info = MCE_GEN3 }, ++ /* Topseed eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_TOPSEED, 0x000a), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* Topseed eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_TOPSEED, 0x0011), ++ .driver_info = MCE_GEN3 }, ++ /* Ricavision internal Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_RICAVISION, 0x0010) }, ++ /* Itron ione Libra Q-11 */ ++ { USB_DEVICE(VENDOR_ITRON, 0x7002) }, ++ /* FIC eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_FIC, 0x9242) }, ++ /* LG eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_LG, 0x9803) }, ++ /* Microsoft MCE Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) }, ++ /* Formosa eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe015) }, ++ /* Formosa21 / eHome Infrared Receiver */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe016) }, ++ /* Formosa21 / eHome Infrared Receiver */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) }, ++ /* Formosa aim / Trust MCE Infrared Receiver */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe017), ++ .driver_info = MCE_GEN2_NO_TX }, ++ /* Formosa Industrial Computing / Beanbag Emulation Device */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe018) }, ++ /* Formosa21 / eHome Infrared Receiver */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03a) }, ++ /* Formosa Industrial Computing AIM IR605/A */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) }, ++ /* Formosa Industrial Computing */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03e) }, ++ /* Formosa Industrial Computing */ ++ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) }, ++ /* Fintek eHome Infrared Transceiver (HP branded) */ ++ { USB_DEVICE(VENDOR_FINTEK, 0x5168), ++ .driver_info = MCE_GEN2_TX_INV }, ++ /* Fintek eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_FINTEK, 0x0602) }, ++ /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */ ++ { USB_DEVICE(VENDOR_FINTEK, 0x0702) }, ++ /* Pinnacle Remote Kit */ ++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225), ++ .driver_info = MCE_GEN3 }, ++ /* Elitegroup Computer Systems IR */ ++ { USB_DEVICE(VENDOR_ECS, 0x0f38) }, ++ /* Wistron Corp. eHome Infrared Receiver */ ++ { USB_DEVICE(VENDOR_WISTRON, 0x0002) }, ++ /* Compro K100 */ ++ { USB_DEVICE(VENDOR_COMPRO, 0x3020) }, ++ /* Compro K100 v2 */ ++ { USB_DEVICE(VENDOR_COMPRO, 0x3082) }, ++ /* Northstar Systems, Inc. eHome Infrared Transceiver */ ++ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, ++ /* TiVo PC IR Receiver */ ++ { USB_DEVICE(VENDOR_TIVO, 0x2000), ++ .driver_info = TIVO_KIT }, ++ /* Conexant Hybrid TV "Shelby" Polaris SDK */ ++ { USB_DEVICE(VENDOR_CONEXANT, 0x58a1), ++ .driver_info = POLARIS_EVK }, ++ /* Conexant Hybrid TV RDU253S Polaris */ ++ { USB_DEVICE(VENDOR_CONEXANT, 0x58a5), ++ .driver_info = CX_HYBRID_TV }, ++ /* Twisted Melon Inc. - Manta Mini Receiver */ ++ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8008) }, ++ /* Twisted Melon Inc. - Manta Pico Receiver */ ++ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) }, ++ /* Twisted Melon Inc. - Manta Transceiver */ ++ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) }, ++ /* Hauppauge WINTV-HVR-HVR 930C-HD - based on cx231xx */ ++ { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb130), ++ .driver_info = HAUPPAUGE_CX_HYBRID_TV }, ++ /* Adaptec / HP eHome Receiver */ ++ { USB_DEVICE(VENDOR_ADAPTEC, 0x0094) }, ++ /* Terminating entry */ ++ { } ++}; ++ ++/* data structure for each usb transceiver */ ++struct mceusb_dev { ++ /* ir-core bits */ ++ struct rc_dev *rc; ++ ++ /* optional features we can enable */ ++ bool carrier_report_enabled; ++ bool learning_enabled; ++ ++ /* core device bits */ ++ struct device *dev; ++ ++ /* usb */ ++ struct usb_device *usbdev; ++ struct urb *urb_in; ++ struct usb_endpoint_descriptor *usb_ep_out; ++ ++ /* buffers and dma */ ++ unsigned char *buf_in; ++ unsigned int len_in; ++ dma_addr_t dma_in; ++ ++ enum { ++ CMD_HEADER = 0, ++ SUBCMD, ++ CMD_DATA, ++ PARSE_IRDATA, ++ } parser_state; ++ ++ u8 cmd, rem; /* Remaining IR data bytes in packet */ ++ ++ struct { ++ u32 connected:1; ++ u32 tx_mask_normal:1; ++ u32 microsoft_gen1:1; ++ u32 no_tx:1; ++ } flags; ++ ++ /* transmit support */ ++ int send_flags; ++ u32 carrier; ++ unsigned char tx_mask; ++ ++ char name[128]; ++ char phys[64]; ++ enum mceusb_model_type model; ++ ++ bool need_reset; /* flag to issue a device resume cmd */ ++ u8 emver; /* emulator interface version */ ++ u8 num_txports; /* number of transmit ports */ ++ u8 num_rxports; /* number of receive sensors */ ++ u8 txports_cabled; /* bitmask of transmitters with cable */ ++ u8 rxports_active; /* bitmask of active receive sensors */ ++}; ++ ++/* MCE Device Command Strings, generally a port and command pair */ ++static char DEVICE_RESUME[] = {MCE_CMD_NULL, MCE_CMD_PORT_SYS, ++ MCE_CMD_RESUME}; ++static char GET_REVISION[] = {MCE_CMD_PORT_SYS, MCE_CMD_G_REVISION}; ++static char GET_EMVER[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETEMVER}; ++static char GET_WAKEVERSION[] = {MCE_CMD_PORT_SYS, MCE_CMD_GETWAKEVERSION}; ++static char FLASH_LED[] = {MCE_CMD_PORT_SYS, MCE_CMD_FLASHLED}; ++static char GET_UNKNOWN2[] = {MCE_CMD_PORT_IR, MCE_CMD_UNKNOWN2}; ++static char GET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRCFS}; ++static char GET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTIMEOUT}; ++static char GET_NUM_PORTS[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRNUMPORTS}; ++static char GET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRTXPORTS}; ++static char GET_RX_SENSOR[] = {MCE_CMD_PORT_IR, MCE_CMD_GETIRRXPORTEN}; ++/* sub in desired values in lower byte or bytes for full command */ ++/* FIXME: make use of these for transmit. ++static char SET_CARRIER_FREQ[] = {MCE_CMD_PORT_IR, ++ MCE_CMD_SETIRCFS, 0x00, 0x00}; ++static char SET_TX_BITMASK[] = {MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00}; ++static char SET_RX_TIMEOUT[] = {MCE_CMD_PORT_IR, ++ MCE_CMD_SETIRTIMEOUT, 0x00, 0x00}; ++static char SET_RX_SENSOR[] = {MCE_CMD_PORT_IR, ++ MCE_RSP_EQIRRXPORTEN, 0x00}; ++*/ ++ ++static int mceusb_cmd_datasize(u8 cmd, u8 subcmd) ++{ ++ int datasize = 0; ++ ++ switch (cmd) { ++ case MCE_CMD_NULL: ++ if (subcmd == MCE_CMD_PORT_SYS) ++ datasize = 1; ++ break; ++ case MCE_CMD_PORT_SYS: ++ switch (subcmd) { ++ case MCE_RSP_GETPORTSTATUS: ++ datasize = 5; ++ break; ++ case MCE_RSP_EQWAKEVERSION: ++ datasize = 4; ++ break; ++ case MCE_CMD_G_REVISION: ++ datasize = 2; ++ break; ++ case MCE_RSP_EQWAKESUPPORT: ++ case MCE_RSP_GETWAKESOURCE: ++ case MCE_RSP_EQDEVDETAILS: ++ case MCE_RSP_EQEMVER: ++ datasize = 1; ++ break; ++ } ++ case MCE_CMD_PORT_IR: ++ switch (subcmd) { ++ case MCE_CMD_UNKNOWN: ++ case MCE_RSP_EQIRCFS: ++ case MCE_RSP_EQIRTIMEOUT: ++ case MCE_RSP_EQIRRXCFCNT: ++ case MCE_RSP_EQIRNUMPORTS: ++ datasize = 2; ++ break; ++ case MCE_CMD_SIG_END: ++ case MCE_RSP_EQIRTXPORTS: ++ case MCE_RSP_EQIRRXPORTEN: ++ datasize = 1; ++ break; ++ } ++ } ++ return datasize; ++} ++ ++static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, ++ int offset, int len, bool out) ++{ ++ char codes[USB_BUFLEN * 3 + 1]; ++ char inout[9]; ++ u8 cmd, subcmd, data1, data2, data3, data4; ++ struct device *dev = ir->dev; ++ int i, start, skip = 0; ++ u32 carrier, period; ++ ++ if (!debug) ++ return; ++ ++ /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ ++ if (ir->flags.microsoft_gen1 && !out && !offset) ++ skip = 2; ++ ++ if (len <= skip) ++ return; ++ ++ for (i = 0; i < len && i < USB_BUFLEN; i++) ++ snprintf(codes + i * 3, 4, "%02x ", buf[i + offset] & 0xff); ++ ++ dev_info(dev, "%sx data: %s(length=%d)\n", ++ (out ? "t" : "r"), codes, len); ++ ++ if (out) ++ strcpy(inout, "Request\0"); ++ else ++ strcpy(inout, "Got\0"); ++ ++ start = offset + skip; ++ cmd = buf[start] & 0xff; ++ subcmd = buf[start + 1] & 0xff; ++ data1 = buf[start + 2] & 0xff; ++ data2 = buf[start + 3] & 0xff; ++ data3 = buf[start + 4] & 0xff; ++ data4 = buf[start + 5] & 0xff; ++ ++ switch (cmd) { ++ case MCE_CMD_NULL: ++ if (subcmd == MCE_CMD_NULL) ++ break; ++ if ((subcmd == MCE_CMD_PORT_SYS) && ++ (data1 == MCE_CMD_RESUME)) ++ dev_info(dev, "Device resume requested\n"); ++ else ++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n", ++ cmd, subcmd); ++ break; ++ case MCE_CMD_PORT_SYS: ++ switch (subcmd) { ++ case MCE_RSP_EQEMVER: ++ if (!out) ++ dev_info(dev, "Emulator interface version %x\n", ++ data1); ++ break; ++ case MCE_CMD_G_REVISION: ++ if (len == 2) ++ dev_info(dev, "Get hw/sw rev?\n"); ++ else ++ dev_info(dev, "hw/sw rev 0x%02x 0x%02x " ++ "0x%02x 0x%02x\n", data1, data2, ++ buf[start + 4], buf[start + 5]); ++ break; ++ case MCE_CMD_RESUME: ++ dev_info(dev, "Device resume requested\n"); ++ break; ++ case MCE_RSP_CMD_ILLEGAL: ++ dev_info(dev, "Illegal PORT_SYS command\n"); ++ break; ++ case MCE_RSP_EQWAKEVERSION: ++ if (!out) ++ dev_info(dev, "Wake version, proto: 0x%02x, " ++ "payload: 0x%02x, address: 0x%02x, " ++ "version: 0x%02x\n", ++ data1, data2, data3, data4); ++ break; ++ case MCE_RSP_GETPORTSTATUS: ++ if (!out) ++ /* We use data1 + 1 here, to match hw labels */ ++ dev_info(dev, "TX port %d: blaster is%s connected\n", ++ data1 + 1, data4 ? " not" : ""); ++ break; ++ case MCE_CMD_FLASHLED: ++ dev_info(dev, "Attempting to flash LED\n"); ++ break; ++ default: ++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n", ++ cmd, subcmd); ++ break; ++ } ++ break; ++ case MCE_CMD_PORT_IR: ++ switch (subcmd) { ++ case MCE_CMD_SIG_END: ++ dev_info(dev, "End of signal\n"); ++ break; ++ case MCE_CMD_PING: ++ dev_info(dev, "Ping\n"); ++ break; ++ case MCE_CMD_UNKNOWN: ++ dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n", ++ data1, data2); ++ break; ++ case MCE_RSP_EQIRCFS: ++ period = DIV_ROUND_CLOSEST( ++ (1U << data1 * 2) * (data2 + 1), 10); ++ if (!period) ++ break; ++ carrier = (1000 * 1000) / period; ++ dev_info(dev, "%s carrier of %u Hz (period %uus)\n", ++ inout, carrier, period); ++ break; ++ case MCE_CMD_GETIRCFS: ++ dev_info(dev, "Get carrier mode and freq\n"); ++ break; ++ case MCE_RSP_EQIRTXPORTS: ++ dev_info(dev, "%s transmit blaster mask of 0x%02x\n", ++ inout, data1); ++ break; ++ case MCE_RSP_EQIRTIMEOUT: ++ /* value is in units of 50us, so x*50/1000 ms */ ++ period = ((data1 << 8) | data2) * MCE_TIME_UNIT / 1000; ++ dev_info(dev, "%s receive timeout of %d ms\n", ++ inout, period); ++ break; ++ case MCE_CMD_GETIRTIMEOUT: ++ dev_info(dev, "Get receive timeout\n"); ++ break; ++ case MCE_CMD_GETIRTXPORTS: ++ dev_info(dev, "Get transmit blaster mask\n"); ++ break; ++ case MCE_RSP_EQIRRXPORTEN: ++ dev_info(dev, "%s %s-range receive sensor in use\n", ++ inout, data1 == 0x02 ? "short" : "long"); ++ break; ++ case MCE_CMD_GETIRRXPORTEN: ++ /* aka MCE_RSP_EQIRRXCFCNT */ ++ if (out) ++ dev_info(dev, "Get receive sensor\n"); ++ else if (ir->learning_enabled) ++ dev_info(dev, "RX pulse count: %d\n", ++ ((data1 << 8) | data2)); ++ break; ++ case MCE_RSP_EQIRNUMPORTS: ++ if (out) ++ break; ++ dev_info(dev, "Num TX ports: %x, num RX ports: %x\n", ++ data1, data2); ++ break; ++ case MCE_RSP_CMD_ILLEGAL: ++ dev_info(dev, "Illegal PORT_IR command\n"); ++ break; ++ default: ++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n", ++ cmd, subcmd); ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (cmd == MCE_IRDATA_TRAILER) ++ dev_info(dev, "End of raw IR data\n"); ++ else if ((cmd != MCE_CMD_PORT_IR) && ++ ((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA)) ++ dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem); ++} ++ ++static void mce_async_callback(struct urb *urb) ++{ ++ struct mceusb_dev *ir; ++ int len; ++ ++ if (!urb) ++ return; ++ ++ ir = urb->context; ++ if (ir) { ++ len = urb->actual_length; ++ ++ mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true); ++ } ++ ++ /* the transfer buffer and urb were allocated in mce_request_packet */ ++ kfree(urb->transfer_buffer); ++ usb_free_urb(urb); ++} ++ ++/* request incoming or send outgoing usb packet - used to initialize remote */ ++static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, ++ int size, int urb_type) ++{ ++ int res, pipe; ++ struct urb *async_urb; ++ struct device *dev = ir->dev; ++ unsigned char *async_buf; ++ ++ if (urb_type == MCEUSB_TX) { ++ async_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (unlikely(!async_urb)) { ++ dev_err(dev, "Error, couldn't allocate urb!\n"); ++ return; ++ } ++ ++ async_buf = kzalloc(size, GFP_KERNEL); ++ if (!async_buf) { ++ dev_err(dev, "Error, couldn't allocate buf!\n"); ++ usb_free_urb(async_urb); ++ return; ++ } ++ ++ /* outbound data */ ++ pipe = usb_sndintpipe(ir->usbdev, ++ ir->usb_ep_out->bEndpointAddress); ++ usb_fill_int_urb(async_urb, ir->usbdev, pipe, ++ async_buf, size, mce_async_callback, ++ ir, ir->usb_ep_out->bInterval); ++ memcpy(async_buf, data, size); ++ ++ } else if (urb_type == MCEUSB_RX) { ++ /* standard request */ ++ async_urb = ir->urb_in; ++ ir->send_flags = RECV_FLAG_IN_PROGRESS; ++ ++ } else { ++ dev_err(dev, "Error! Unknown urb type %d\n", urb_type); ++ return; ++ } ++ ++ mce_dbg(dev, "receive request called (size=%#x)\n", size); ++ ++ async_urb->transfer_buffer_length = size; ++ async_urb->dev = ir->usbdev; ++ ++ res = usb_submit_urb(async_urb, GFP_ATOMIC); ++ if (res) { ++ mce_dbg(dev, "receive request FAILED! (res=%d)\n", res); ++ return; ++ } ++ mce_dbg(dev, "receive request complete (res=%d)\n", res); ++} ++ ++static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size) ++{ ++ int rsize = sizeof(DEVICE_RESUME); ++ ++ if (ir->need_reset) { ++ ir->need_reset = false; ++ mce_request_packet(ir, DEVICE_RESUME, rsize, MCEUSB_TX); ++ msleep(10); ++ } ++ ++ mce_request_packet(ir, data, size, MCEUSB_TX); ++ msleep(10); ++} ++ ++static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size) ++{ ++ mce_request_packet(ir, NULL, size, MCEUSB_RX); ++} ++ ++/* Send data out the IR blaster port(s) */ ++static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) ++{ ++ struct mceusb_dev *ir = dev->priv; ++ int i, length, ret = 0; ++ int cmdcount = 0; ++ unsigned char cmdbuf[MCE_CMDBUF_SIZE]; ++ ++ /* MCE tx init header */ ++ cmdbuf[cmdcount++] = MCE_CMD_PORT_IR; ++ cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS; ++ cmdbuf[cmdcount++] = ir->tx_mask; ++ ++ /* Send the set TX ports command */ ++ mce_async_out(ir, cmdbuf, cmdcount); ++ cmdcount = 0; ++ ++ /* Generate mce packet data */ ++ for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) { ++ txbuf[i] = txbuf[i] / MCE_TIME_UNIT; ++ ++ do { /* loop to support long pulses/spaces > 127*50us=6.35ms */ ++ ++ /* Insert mce packet header every 4th entry */ ++ if ((cmdcount < MCE_CMDBUF_SIZE) && ++ (cmdcount % MCE_CODE_LENGTH) == 0) ++ cmdbuf[cmdcount++] = MCE_IRDATA_HEADER; ++ ++ /* Insert mce packet data */ ++ if (cmdcount < MCE_CMDBUF_SIZE) ++ cmdbuf[cmdcount++] = ++ (txbuf[i] < MCE_PULSE_BIT ? ++ txbuf[i] : MCE_MAX_PULSE_LENGTH) | ++ (i & 1 ? 0x00 : MCE_PULSE_BIT); ++ else { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ } while ((txbuf[i] > MCE_MAX_PULSE_LENGTH) && ++ (txbuf[i] -= MCE_MAX_PULSE_LENGTH)); ++ } ++ ++ /* Check if we have room for the empty packet at the end */ ++ if (cmdcount >= MCE_CMDBUF_SIZE) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Fix packet length in last header */ ++ length = cmdcount % MCE_CODE_LENGTH; ++ cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; ++ ++ /* All mce commands end with an empty packet (0x80) */ ++ cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER; ++ ++ /* Transmit the command to the mce device */ ++ mce_async_out(ir, cmdbuf, cmdcount); ++ ++out: ++ return ret ? ret : count; ++} ++ ++/* Sets active IR outputs -- mce devices typically have two */ ++static int mceusb_set_tx_mask(struct rc_dev *dev, u32 mask) ++{ ++ struct mceusb_dev *ir = dev->priv; ++ ++ if (ir->flags.tx_mask_normal) ++ ir->tx_mask = mask; ++ else ++ ir->tx_mask = (mask != MCE_DEFAULT_TX_MASK ? ++ mask ^ MCE_DEFAULT_TX_MASK : mask) << 1; ++ ++ return 0; ++} ++ ++/* Sets the send carrier frequency and mode */ ++static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier) ++{ ++ struct mceusb_dev *ir = dev->priv; ++ int clk = 10000000; ++ int prescaler = 0, divisor = 0; ++ unsigned char cmdbuf[4] = { MCE_CMD_PORT_IR, ++ MCE_CMD_SETIRCFS, 0x00, 0x00 }; ++ ++ /* Carrier has changed */ ++ if (ir->carrier != carrier) { ++ ++ if (carrier == 0) { ++ ir->carrier = carrier; ++ cmdbuf[2] = MCE_CMD_SIG_END; ++ cmdbuf[3] = MCE_IRDATA_TRAILER; ++ mce_dbg(ir->dev, "%s: disabling carrier " ++ "modulation\n", __func__); ++ mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); ++ return carrier; ++ } ++ ++ for (prescaler = 0; prescaler < 4; ++prescaler) { ++ divisor = (clk >> (2 * prescaler)) / carrier; ++ if (divisor <= 0xff) { ++ ir->carrier = carrier; ++ cmdbuf[2] = prescaler; ++ cmdbuf[3] = divisor; ++ mce_dbg(ir->dev, "%s: requesting %u HZ " ++ "carrier\n", __func__, carrier); ++ ++ /* Transmit new carrier to mce device */ ++ mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); ++ return carrier; ++ } ++ } ++ ++ return -EINVAL; ++ ++ } ++ ++ return carrier; ++} ++ ++/* ++ * We don't do anything but print debug spew for many of the command bits ++ * we receive from the hardware, but some of them are useful information ++ * we want to store so that we can use them. ++ */ ++static void mceusb_handle_command(struct mceusb_dev *ir, int index) ++{ ++ u8 hi = ir->buf_in[index + 1] & 0xff; ++ u8 lo = ir->buf_in[index + 2] & 0xff; ++ ++ switch (ir->buf_in[index]) { ++ /* the one and only 5-byte return value command */ ++ case MCE_RSP_GETPORTSTATUS: ++ if ((ir->buf_in[index + 4] & 0xff) == 0x00) ++ ir->txports_cabled |= 1 << hi; ++ break; ++ ++ /* 2-byte return value commands */ ++ case MCE_RSP_EQIRTIMEOUT: ++ ir->rc->timeout = US_TO_NS((hi << 8 | lo) * MCE_TIME_UNIT); ++ break; ++ case MCE_RSP_EQIRNUMPORTS: ++ ir->num_txports = hi; ++ ir->num_rxports = lo; ++ break; ++ ++ /* 1-byte return value commands */ ++ case MCE_RSP_EQEMVER: ++ ir->emver = hi; ++ break; ++ case MCE_RSP_EQIRTXPORTS: ++ ir->tx_mask = hi; ++ break; ++ case MCE_RSP_EQIRRXPORTEN: ++ ir->learning_enabled = ((hi & 0x02) == 0x02); ++ ir->rxports_active = hi; ++ break; ++ case MCE_RSP_CMD_ILLEGAL: ++ ir->need_reset = true; ++ break; ++ default: ++ break; ++ } ++} ++ ++static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) ++{ ++ DEFINE_IR_RAW_EVENT(rawir); ++ bool event = false; ++ int i = 0; ++ ++ /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ ++ if (ir->flags.microsoft_gen1) ++ i = 2; ++ ++ /* if there's no data, just return now */ ++ if (buf_len <= i) ++ return; ++ ++ for (; i < buf_len; i++) { ++ switch (ir->parser_state) { ++ case SUBCMD: ++ ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]); ++ mceusb_dev_printdata(ir, ir->buf_in, i - 1, ++ ir->rem + 2, false); ++ mceusb_handle_command(ir, i); ++ ir->parser_state = CMD_DATA; ++ break; ++ case PARSE_IRDATA: ++ ir->rem--; ++ init_ir_raw_event(&rawir); ++ rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); ++ rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK) ++ * US_TO_NS(MCE_TIME_UNIT); ++ ++ mce_dbg(ir->dev, "Storing %s with duration %d\n", ++ rawir.pulse ? "pulse" : "space", ++ rawir.duration); ++ ++ if (ir_raw_event_store_with_filter(ir->rc, &rawir)) ++ event = true; ++ break; ++ case CMD_DATA: ++ ir->rem--; ++ break; ++ case CMD_HEADER: ++ /* decode mce packets of the form (84),AA,BB,CC,DD */ ++ /* IR data packets can span USB messages - rem */ ++ ir->cmd = ir->buf_in[i]; ++ if ((ir->cmd == MCE_CMD_PORT_IR) || ++ ((ir->cmd & MCE_PORT_MASK) != ++ MCE_COMMAND_IRDATA)) { ++ ir->parser_state = SUBCMD; ++ continue; ++ } ++ ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK); ++ mceusb_dev_printdata(ir, ir->buf_in, ++ i, ir->rem + 1, false); ++ if (ir->rem) ++ ir->parser_state = PARSE_IRDATA; ++ else ++ ir_raw_event_reset(ir->rc); ++ break; ++ } ++ ++ if (ir->parser_state != CMD_HEADER && !ir->rem) ++ ir->parser_state = CMD_HEADER; ++ } ++ if (event) { ++ mce_dbg(ir->dev, "processed IR data, calling ir_raw_event_handle\n"); ++ ir_raw_event_handle(ir->rc); ++ } ++} ++ ++static void mceusb_dev_recv(struct urb *urb) ++{ ++ struct mceusb_dev *ir; ++ int buf_len; ++ ++ if (!urb) ++ return; ++ ++ ir = urb->context; ++ if (!ir) { ++ usb_unlink_urb(urb); ++ return; ++ } ++ ++ buf_len = urb->actual_length; ++ ++ if (ir->send_flags == RECV_FLAG_IN_PROGRESS) { ++ ir->send_flags = SEND_FLAG_COMPLETE; ++ mce_dbg(ir->dev, "setup answer received %d bytes\n", ++ buf_len); ++ } ++ ++ switch (urb->status) { ++ /* success */ ++ case 0: ++ mceusb_process_ir_data(ir, buf_len); ++ break; ++ ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ usb_unlink_urb(urb); ++ return; ++ ++ case -EPIPE: ++ default: ++ mce_dbg(ir->dev, "Error: urb status = %d\n", urb->status); ++ break; ++ } ++ ++ usb_submit_urb(urb, GFP_ATOMIC); ++} ++ ++static void mceusb_get_emulator_version(struct mceusb_dev *ir) ++{ ++ /* If we get no reply or an illegal command reply, its ver 1, says MS */ ++ ir->emver = 1; ++ mce_async_out(ir, GET_EMVER, sizeof(GET_EMVER)); ++} ++ ++static void mceusb_gen1_init(struct mceusb_dev *ir) ++{ ++ int ret; ++ struct device *dev = ir->dev; ++ char *data; ++ ++ data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL); ++ if (!data) { ++ dev_err(dev, "%s: memory allocation failed!\n", __func__); ++ return; ++ } ++ ++ /* ++ * This is a strange one. Windows issues a set address to the device ++ * on the receive control pipe and expect a certain value pair back ++ */ ++ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), ++ USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0, ++ data, USB_CTRL_MSG_SZ, HZ * 3); ++ mce_dbg(dev, "%s - ret = %d\n", __func__, ret); ++ mce_dbg(dev, "%s - data[0] = %d, data[1] = %d\n", ++ __func__, data[0], data[1]); ++ ++ /* set feature: bit rate 38400 bps */ ++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), ++ USB_REQ_SET_FEATURE, USB_TYPE_VENDOR, ++ 0xc04e, 0x0000, NULL, 0, HZ * 3); ++ ++ mce_dbg(dev, "%s - ret = %d\n", __func__, ret); ++ ++ /* bRequest 4: set char length to 8 bits */ ++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), ++ 4, USB_TYPE_VENDOR, ++ 0x0808, 0x0000, NULL, 0, HZ * 3); ++ mce_dbg(dev, "%s - retB = %d\n", __func__, ret); ++ ++ /* bRequest 2: set handshaking to use DTR/DSR */ ++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), ++ 2, USB_TYPE_VENDOR, ++ 0x0000, 0x0100, NULL, 0, HZ * 3); ++ mce_dbg(dev, "%s - retC = %d\n", __func__, ret); ++ ++ /* device resume */ ++ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); ++ ++ /* get hw/sw revision? */ ++ mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); ++ ++ kfree(data); ++} ++ ++static void mceusb_gen2_init(struct mceusb_dev *ir) ++{ ++ /* device resume */ ++ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); ++ ++ /* get wake version (protocol, key, address) */ ++ mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION)); ++ ++ /* unknown what this one actually returns... */ ++ mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2)); ++} ++ ++static void mceusb_get_parameters(struct mceusb_dev *ir) ++{ ++ int i; ++ unsigned char cmdbuf[3] = { MCE_CMD_PORT_SYS, ++ MCE_CMD_GETPORTSTATUS, 0x00 }; ++ ++ /* defaults, if the hardware doesn't support querying */ ++ ir->num_txports = 2; ++ ir->num_rxports = 2; ++ ++ /* get number of tx and rx ports */ ++ mce_async_out(ir, GET_NUM_PORTS, sizeof(GET_NUM_PORTS)); ++ ++ /* get the carrier and frequency */ ++ mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ)); ++ ++ if (ir->num_txports && !ir->flags.no_tx) ++ /* get the transmitter bitmask */ ++ mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK)); ++ ++ /* get receiver timeout value */ ++ mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); ++ ++ /* get receiver sensor setting */ ++ mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR)); ++ ++ for (i = 0; i < ir->num_txports; i++) { ++ cmdbuf[2] = i; ++ mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); ++ } ++} ++ ++static void mceusb_flash_led(struct mceusb_dev *ir) ++{ ++ if (ir->emver < 2) ++ return; ++ ++ mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED)); ++} ++ ++static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) ++{ ++ struct device *dev = ir->dev; ++ struct rc_dev *rc; ++ int ret; ++ ++ rc = rc_allocate_device(); ++ if (!rc) { ++ dev_err(dev, "remote dev allocation failed\n"); ++ goto out; ++ } ++ ++ snprintf(ir->name, sizeof(ir->name), "%s (%04x:%04x)", ++ mceusb_model[ir->model].name ? ++ mceusb_model[ir->model].name : ++ "Media Center Ed. eHome Infrared Remote Transceiver", ++ le16_to_cpu(ir->usbdev->descriptor.idVendor), ++ le16_to_cpu(ir->usbdev->descriptor.idProduct)); ++ ++ usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys)); ++ ++ rc->input_name = ir->name; ++ rc->input_phys = ir->phys; ++ usb_to_input_id(ir->usbdev, &rc->input_id); ++ rc->dev.parent = dev; ++ rc->priv = ir; ++ rc->driver_type = RC_DRIVER_IR_RAW; ++ rc->allowed_protos = RC_BIT_ALL; ++ rc->timeout = MS_TO_NS(100); ++ if (!ir->flags.no_tx) { ++ rc->s_tx_mask = mceusb_set_tx_mask; ++ rc->s_tx_carrier = mceusb_set_tx_carrier; ++ rc->tx_ir = mceusb_tx_ir; ++ } ++ rc->driver_name = DRIVER_NAME; ++ rc->map_name = mceusb_model[ir->model].rc_map ? ++ mceusb_model[ir->model].rc_map : RC_MAP_RC6_MCE; ++ ++ ret = rc_register_device(rc); ++ if (ret < 0) { ++ dev_err(dev, "remote dev registration failed\n"); ++ goto out; ++ } ++ ++ return rc; ++ ++out: ++ rc_free_device(rc); ++ return NULL; ++} ++ ++static int mceusb_dev_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *dev = interface_to_usbdev(intf); ++ struct usb_host_interface *idesc; ++ struct usb_endpoint_descriptor *ep = NULL; ++ struct usb_endpoint_descriptor *ep_in = NULL; ++ struct usb_endpoint_descriptor *ep_out = NULL; ++ struct mceusb_dev *ir = NULL; ++ int pipe, maxp, i; ++ char buf[63], name[128] = ""; ++ enum mceusb_model_type model = id->driver_info; ++ bool is_gen3; ++ bool is_microsoft_gen1; ++ bool tx_mask_normal; ++ int ir_intfnum; ++ ++ mce_dbg(&intf->dev, "%s called\n", __func__); ++ ++ idesc = intf->cur_altsetting; ++ ++ is_gen3 = mceusb_model[model].mce_gen3; ++ is_microsoft_gen1 = mceusb_model[model].mce_gen1; ++ tx_mask_normal = mceusb_model[model].tx_mask_normal; ++ ir_intfnum = mceusb_model[model].ir_intfnum; ++ ++ /* There are multi-function devices with non-IR interfaces */ ++ if (idesc->desc.bInterfaceNumber != ir_intfnum) ++ return -ENODEV; ++ ++ /* step through the endpoints to find first bulk in and out endpoint */ ++ for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { ++ ep = &idesc->endpoint[i].desc; ++ ++ if ((ep_in == NULL) ++ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ++ == USB_DIR_IN) ++ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ == USB_ENDPOINT_XFER_BULK) ++ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ == USB_ENDPOINT_XFER_INT))) { ++ ++ ep_in = ep; ++ ep_in->bmAttributes = USB_ENDPOINT_XFER_INT; ++ ep_in->bInterval = 1; ++ mce_dbg(&intf->dev, "acceptable inbound endpoint " ++ "found\n"); ++ } ++ ++ if ((ep_out == NULL) ++ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ++ == USB_DIR_OUT) ++ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ == USB_ENDPOINT_XFER_BULK) ++ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ == USB_ENDPOINT_XFER_INT))) { ++ ++ ep_out = ep; ++ ep_out->bmAttributes = USB_ENDPOINT_XFER_INT; ++ ep_out->bInterval = 1; ++ mce_dbg(&intf->dev, "acceptable outbound endpoint " ++ "found\n"); ++ } ++ } ++ if (ep_in == NULL) { ++ mce_dbg(&intf->dev, "inbound and/or endpoint not found\n"); ++ return -ENODEV; ++ } ++ ++ pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); ++ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); ++ ++ ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL); ++ if (!ir) ++ goto mem_alloc_fail; ++ ++ ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in); ++ if (!ir->buf_in) ++ goto buf_in_alloc_fail; ++ ++ ir->urb_in = usb_alloc_urb(0, GFP_KERNEL); ++ if (!ir->urb_in) ++ goto urb_in_alloc_fail; ++ ++ ir->usbdev = dev; ++ ir->dev = &intf->dev; ++ ir->len_in = maxp; ++ ir->flags.microsoft_gen1 = is_microsoft_gen1; ++ ir->flags.tx_mask_normal = tx_mask_normal; ++ ir->flags.no_tx = mceusb_model[model].no_tx; ++ ir->model = model; ++ ++ /* Saving usb interface data for use by the transmitter routine */ ++ ir->usb_ep_out = ep_out; ++ ++ if (dev->descriptor.iManufacturer ++ && usb_string(dev, dev->descriptor.iManufacturer, ++ buf, sizeof(buf)) > 0) ++ strlcpy(name, buf, sizeof(name)); ++ if (dev->descriptor.iProduct ++ && usb_string(dev, dev->descriptor.iProduct, ++ buf, sizeof(buf)) > 0) ++ snprintf(name + strlen(name), sizeof(name) - strlen(name), ++ " %s", buf); ++ ++ ir->rc = mceusb_init_rc_dev(ir); ++ if (!ir->rc) ++ goto rc_dev_fail; ++ ++ /* wire up inbound data handler */ ++ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp, ++ mceusb_dev_recv, ir, ep_in->bInterval); ++ ir->urb_in->transfer_dma = ir->dma_in; ++ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ /* flush buffers on the device */ ++ mce_dbg(&intf->dev, "Flushing receive buffers\n"); ++ mce_flush_rx_buffer(ir, maxp); ++ ++ /* figure out which firmware/emulator version this hardware has */ ++ mceusb_get_emulator_version(ir); ++ ++ /* initialize device */ ++ if (ir->flags.microsoft_gen1) ++ mceusb_gen1_init(ir); ++ else if (!is_gen3) ++ mceusb_gen2_init(ir); ++ ++ mceusb_get_parameters(ir); ++ ++ mceusb_flash_led(ir); ++ ++ if (!ir->flags.no_tx) ++ mceusb_set_tx_mask(ir->rc, MCE_DEFAULT_TX_MASK); ++ ++ usb_set_intfdata(intf, ir); ++ ++ /* enable wake via this device */ ++ device_set_wakeup_capable(ir->dev, true); ++ device_set_wakeup_enable(ir->dev, true); ++ ++ dev_info(&intf->dev, "Registered %s with mce emulator interface " ++ "version %x\n", name, ir->emver); ++ dev_info(&intf->dev, "%x tx ports (0x%x cabled) and " ++ "%x rx sensors (0x%x active)\n", ++ ir->num_txports, ir->txports_cabled, ++ ir->num_rxports, ir->rxports_active); ++ ++ return 0; ++ ++ /* Error-handling path */ ++rc_dev_fail: ++ usb_free_urb(ir->urb_in); ++urb_in_alloc_fail: ++ usb_free_coherent(dev, maxp, ir->buf_in, ir->dma_in); ++buf_in_alloc_fail: ++ kfree(ir); ++mem_alloc_fail: ++ dev_err(&intf->dev, "%s: device setup failed!\n", __func__); ++ ++ return -ENOMEM; ++} ++ ++ ++static void mceusb_dev_disconnect(struct usb_interface *intf) ++{ ++ struct usb_device *dev = interface_to_usbdev(intf); ++ struct mceusb_dev *ir = usb_get_intfdata(intf); ++ ++ usb_set_intfdata(intf, NULL); ++ ++ if (!ir) ++ return; ++ ++ ir->usbdev = NULL; ++ rc_unregister_device(ir->rc); ++ usb_kill_urb(ir->urb_in); ++ usb_free_urb(ir->urb_in); ++ usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); ++ ++ kfree(ir); ++} ++ ++static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct mceusb_dev *ir = usb_get_intfdata(intf); ++ dev_info(ir->dev, "suspend\n"); ++ usb_kill_urb(ir->urb_in); ++ return 0; ++} ++ ++static int mceusb_dev_resume(struct usb_interface *intf) ++{ ++ struct mceusb_dev *ir = usb_get_intfdata(intf); ++ dev_info(ir->dev, "resume\n"); ++ if (usb_submit_urb(ir->urb_in, GFP_ATOMIC)) ++ return -EIO; ++ return 0; ++} ++ ++static struct usb_driver mceusb_dev_driver = { ++ .name = DRIVER_NAME, ++ .probe = mceusb_dev_probe, ++ .disconnect = mceusb_dev_disconnect, ++ .suspend = mceusb_dev_suspend, ++ .resume = mceusb_dev_resume, ++ .reset_resume = mceusb_dev_resume, ++ .id_table = mceusb_dev_table ++}; ++ ++module_usb_driver(mceusb_dev_driver); ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(usb, mceusb_dev_table); ++ ++module_param(debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Debug enabled or not"); +diff -Nur linux-3.14.36/drivers/media/tuners/Kconfig linux-openelec/drivers/media/tuners/Kconfig +--- linux-3.14.36/drivers/media/tuners/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/tuners/Kconfig 2015-07-24 18:03:30.148842002 -0500 +@@ -242,4 +242,11 @@ + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Rafael Micro R820T silicon tuner driver. ++ ++config MEDIA_TUNER_SI2157 ++ tristate "Silicon Labs Si2157 silicon tuner" ++ depends on MEDIA_SUPPORT && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Si2157 silicon tuner driver. + endmenu +diff -Nur linux-3.14.36/drivers/media/tuners/Makefile linux-openelec/drivers/media/tuners/Makefile +--- linux-3.14.36/drivers/media/tuners/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/tuners/Makefile 2015-07-24 18:03:30.148842002 -0500 +@@ -37,6 +37,7 @@ + obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o + obj-$(CONFIG_MEDIA_TUNER_IT913X) += tuner_it913x.o + obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o ++obj-$(CONFIG_MEDIA_TUNER_SI2157) += si2157.o + + ccflags-y += -I$(srctree)/drivers/media/dvb-core + ccflags-y += -I$(srctree)/drivers/media/dvb-frontends +diff -Nur linux-3.14.36/drivers/media/tuners/si2157.c linux-openelec/drivers/media/tuners/si2157.c +--- linux-3.14.36/drivers/media/tuners/si2157.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/tuners/si2157.c 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,417 @@ ++/* ++ * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver ++ * ++ * Copyright (C) 2014 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include "si2157_priv.h" ++ ++static const struct dvb_tuner_ops si2157_ops; ++ ++/* execute firmware command */ ++static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) ++{ ++ int ret; ++ unsigned long timeout; ++ ++ mutex_lock(&s->i2c_mutex); ++ ++ if (cmd->wlen) { ++ /* write cmd and args for firmware */ ++ ret = i2c_master_send(s->client, cmd->args, cmd->wlen); ++ if (ret < 0) { ++ goto err_mutex_unlock; ++ } else if (ret != cmd->wlen) { ++ ret = -EREMOTEIO; ++ goto err_mutex_unlock; ++ } ++ } ++ ++ if (cmd->rlen) { ++ /* wait cmd execution terminate */ ++ #define TIMEOUT 80 ++ timeout = jiffies + msecs_to_jiffies(TIMEOUT); ++ while (!time_after(jiffies, timeout)) { ++ ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); ++ if (ret < 0) { ++ goto err_mutex_unlock; ++ } else if (ret != cmd->rlen) { ++ ret = -EREMOTEIO; ++ goto err_mutex_unlock; ++ } ++ ++ /* firmware ready? */ ++ if ((cmd->args[0] >> 7) & 0x01) ++ break; ++ } ++ ++ dev_dbg(&s->client->dev, "cmd execution took %d ms\n", ++ jiffies_to_msecs(jiffies) - ++ (jiffies_to_msecs(timeout) - TIMEOUT)); ++ ++ if (!((cmd->args[0] >> 7) & 0x01)) { ++ ret = -ETIMEDOUT; ++ goto err_mutex_unlock; ++ } ++ } ++ ++ ret = 0; ++ ++err_mutex_unlock: ++ mutex_unlock(&s->i2c_mutex); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2157_init(struct dvb_frontend *fe) ++{ ++ struct si2157 *s = fe->tuner_priv; ++ int ret, len, remaining; ++ struct si2157_cmd cmd; ++ const struct firmware *fw = NULL; ++ u8 *fw_file; ++ unsigned int chip_id; ++ ++ dev_dbg(&s->client->dev, "\n"); ++ ++ if (s->fw_loaded) ++ goto warm; ++ ++ /* power up */ ++ if (s->chiptype == SI2157_CHIPTYPE_SI2146) { ++ memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); ++ cmd.wlen = 9; ++ } else { ++ memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); ++ cmd.wlen = 15; ++ } ++ cmd.rlen = 1; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ /* query chip revision */ ++ memcpy(cmd.args, "\x02", 1); ++ cmd.wlen = 1; ++ cmd.rlen = 13; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | ++ cmd.args[4] << 0; ++ ++ #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) ++ #define SI2148_A20 ('A' << 24 | 48 << 16 | '2' << 8 | '0' << 0) ++ #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) ++ #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0) ++ #define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0) ++ ++ switch (chip_id) { ++ case SI2158_A20: ++ case SI2148_A20: ++ fw_file = SI2158_A20_FIRMWARE; ++ break; ++ case SI2157_A30: ++ case SI2147_A30: ++ case SI2146_A10: ++ goto skip_fw_download; ++ default: ++ dev_err(&s->client->dev, ++ "unknown chip version Si21%d-%c%c%c\n", ++ cmd.args[2], cmd.args[1], ++ cmd.args[3], cmd.args[4]); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ /* cold state - try to download firmware */ ++ dev_info(&s->client->dev, "found a '%s' in cold state\n", ++ si2157_ops.info.name); ++ ++ /* request the firmware, this will block and timeout */ ++ ret = request_firmware(&fw, fw_file, &s->client->dev); ++ if (ret) { ++ dev_err(&s->client->dev, "firmware file '%s' not found\n", ++ fw_file); ++ goto err; ++ } ++ ++ /* firmware should be n chunks of 17 bytes */ ++ if (fw->size % 17 != 0) { ++ dev_err(&s->client->dev, "firmware file '%s' is invalid\n", ++ fw_file); ++ ret = -EINVAL; ++ goto fw_release_exit; ++ } ++ ++ dev_info(&s->client->dev, "downloading firmware from file '%s'\n", ++ fw_file); ++ ++ for (remaining = fw->size; remaining > 0; remaining -= 17) { ++ len = fw->data[fw->size - remaining]; ++ memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); ++ cmd.wlen = len; ++ cmd.rlen = 1; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) { ++ dev_err(&s->client->dev, ++ "firmware download failed=%d\n", ++ ret); ++ goto fw_release_exit; ++ } ++ } ++ ++ release_firmware(fw); ++ fw = NULL; ++ ++skip_fw_download: ++ /* reboot the tuner with new firmware? */ ++ memcpy(cmd.args, "\x01\x01", 2); ++ cmd.wlen = 2; ++ cmd.rlen = 1; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ s->fw_loaded = true; ++ ++warm: ++ s->active = true; ++ return 0; ++ ++fw_release_exit: ++ release_firmware(fw); ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2157_sleep(struct dvb_frontend *fe) ++{ ++ struct si2157 *s = fe->tuner_priv; ++ int ret; ++ struct si2157_cmd cmd; ++ ++ dev_dbg(&s->client->dev, "\n"); ++ ++ s->active = false; ++ ++ /* standby */ ++ memcpy(cmd.args, "\x16\x00", 2); ++ cmd.wlen = 2; ++ cmd.rlen = 1; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2157_set_params(struct dvb_frontend *fe) ++{ ++ struct si2157 *s = fe->tuner_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ struct si2157_cmd cmd; ++ u8 bandwidth, delivery_system; ++ ++ dev_dbg(&s->client->dev, ++ "delivery_system=%d frequency=%u bandwidth_hz=%u\n", ++ c->delivery_system, c->frequency, ++ c->bandwidth_hz); ++ ++ if (!s->active) { ++ ret = -EAGAIN; ++ goto err; ++ } ++ ++ if (c->bandwidth_hz <= 6000000) ++ bandwidth = 0x06; ++ else if (c->bandwidth_hz <= 7000000) ++ bandwidth = 0x07; ++ else if (c->bandwidth_hz <= 8000000) ++ bandwidth = 0x08; ++ else ++ bandwidth = 0x0f; ++ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ delivery_system = 0x00; ++ break; ++ case SYS_DVBC_ANNEX_B: ++ delivery_system = 0x10; ++ break; ++ case SYS_DVBT: ++ case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ ++ delivery_system = 0x20; ++ break; ++ case SYS_DVBC_ANNEX_A: ++ delivery_system = 0x30; ++ break; ++ default: ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); ++ cmd.args[4] = delivery_system | bandwidth; ++ if (s->inversion) ++ cmd.args[5] = 0x01; ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ if (s->chiptype == SI2157_CHIPTYPE_SI2146) ++ memcpy(cmd.args, "\x14\x00\x02\x07\x00\x01", 6); ++ else ++ memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6); ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ /* set frequency */ ++ memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); ++ cmd.args[4] = (c->frequency >> 0) & 0xff; ++ cmd.args[5] = (c->frequency >> 8) & 0xff; ++ cmd.args[6] = (c->frequency >> 16) & 0xff; ++ cmd.args[7] = (c->frequency >> 24) & 0xff; ++ cmd.wlen = 8; ++ cmd.rlen = 1; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&s->client->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ *frequency = 5000000; /* default value of property 0x0706 */ ++ return 0; ++} ++ ++static const struct dvb_tuner_ops si2157_ops = { ++ .info = { ++ .name = "Silicon Labs Si2146/2147/2148/2157/2158", ++ .frequency_min = 110000000, ++ .frequency_max = 862000000, ++ }, ++ ++ .init = si2157_init, ++ .sleep = si2157_sleep, ++ .set_params = si2157_set_params, ++ .get_if_frequency = si2157_get_if_frequency, ++}; ++ ++static int si2157_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct si2157_config *cfg = client->dev.platform_data; ++ struct dvb_frontend *fe = cfg->fe; ++ struct si2157 *s; ++ struct si2157_cmd cmd; ++ int ret; ++ ++ s = kzalloc(sizeof(struct si2157), GFP_KERNEL); ++ if (!s) { ++ ret = -ENOMEM; ++ dev_err(&client->dev, "kzalloc() failed\n"); ++ goto err; ++ } ++ ++ s->client = client; ++ s->fe = cfg->fe; ++ s->inversion = cfg->inversion; ++ s->fw_loaded = false; ++ s->chiptype = (u8)id->driver_data; ++ mutex_init(&s->i2c_mutex); ++ ++ /* check if the tuner is there */ ++ cmd.wlen = 0; ++ cmd.rlen = 1; ++ ret = si2157_cmd_execute(s, &cmd); ++ if (ret) ++ goto err; ++ ++ fe->tuner_priv = s; ++ memcpy(&fe->ops.tuner_ops, &si2157_ops, ++ sizeof(struct dvb_tuner_ops)); ++ ++ i2c_set_clientdata(client, s); ++ ++ dev_info(&s->client->dev, ++ "Silicon Labs %s successfully attached\n", ++ s->chiptype == SI2157_CHIPTYPE_SI2146 ? ++ "Si2146" : "Si2147/2148/2157/2158"); ++ ++ return 0; ++err: ++ dev_dbg(&client->dev, "failed=%d\n", ret); ++ kfree(s); ++ ++ return ret; ++} ++ ++static int si2157_remove(struct i2c_client *client) ++{ ++ struct si2157 *s = i2c_get_clientdata(client); ++ struct dvb_frontend *fe = s->fe; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); ++ fe->tuner_priv = NULL; ++ kfree(s); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id si2157_id[] = { ++ {"si2157", 0}, ++ {"si2146", 1}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, si2157_id); ++ ++static struct i2c_driver si2157_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "si2157", ++ }, ++ .probe = si2157_probe, ++ .remove = si2157_remove, ++ .id_table = si2157_id, ++}; ++ ++module_i2c_driver(si2157_driver); ++ ++MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver"); ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE(SI2158_A20_FIRMWARE); +diff -Nur linux-3.14.36/drivers/media/tuners/si2157.h linux-openelec/drivers/media/tuners/si2157.h +--- linux-3.14.36/drivers/media/tuners/si2157.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/tuners/si2157.h 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,39 @@ ++/* ++ * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver ++ * ++ * Copyright (C) 2014 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef SI2157_H ++#define SI2157_H ++ ++#include ++#include "dvb_frontend.h" ++ ++/* ++ * I2C address ++ * 0x60 ++ */ ++struct si2157_config { ++ /* ++ * frontend ++ */ ++ struct dvb_frontend *fe; ++ ++ /* ++ * Spectral Inversion ++ */ ++ bool inversion; ++}; ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/tuners/si2157_priv.h linux-openelec/drivers/media/tuners/si2157_priv.h +--- linux-3.14.36/drivers/media/tuners/si2157_priv.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/tuners/si2157_priv.h 2015-07-24 18:03:30.148842002 -0500 +@@ -0,0 +1,47 @@ ++/* ++ * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver ++ * ++ * Copyright (C) 2014 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef SI2157_PRIV_H ++#define SI2157_PRIV_H ++ ++#include ++#include "si2157.h" ++ ++/* state struct */ ++struct si2157 { ++ struct mutex i2c_mutex; ++ struct i2c_client *client; ++ struct dvb_frontend *fe; ++ bool active; ++ bool fw_loaded; ++ bool inversion; ++ u8 chiptype; ++}; ++ ++#define SI2157_CHIPTYPE_SI2157 0 ++#define SI2157_CHIPTYPE_SI2146 1 ++ ++/* firmware command struct */ ++#define SI2157_ARGLEN 30 ++struct si2157_cmd { ++ u8 args[SI2157_ARGLEN]; ++ unsigned wlen; ++ unsigned rlen; ++}; ++ ++#define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" ++ ++#endif +diff -Nur linux-3.14.36/drivers/media/usb/dvb-usb/dw2102.c linux-openelec/drivers/media/usb/dvb-usb/dw2102.c +--- linux-3.14.36/drivers/media/usb/dvb-usb/dw2102.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/dvb-usb/dw2102.c 2015-07-24 18:03:30.140842002 -0500 +@@ -1109,6 +1109,7 @@ + static struct cxd2820r_config cxd2820r_config = { + .i2c_address = 0x6c, /* (0xd8 >> 1) */ + .ts_mode = 0x38, ++ .ts_clock_inv = 1, + }; + + static struct tda18271_config tda18271_config = { +@@ -1387,20 +1388,27 @@ + + static int t220_frontend_attach(struct dvb_usb_adapter *d) + { +- u8 obuf[3] = { 0xe, 0x80, 0 }; ++ u8 obuf[3] = { 0xe, 0x87, 0 }; + u8 ibuf[] = { 0 }; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + + obuf[0] = 0xe; +- obuf[1] = 0x83; ++ obuf[1] = 0x86; ++ obuf[2] = 1; ++ ++ if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) ++ err("command 0x0e transfer failed."); ++ ++ obuf[0] = 0xe; ++ obuf[1] = 0x80; + obuf[2] = 0; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + +- msleep(100); ++ msleep(50); + + obuf[0] = 0xe; + obuf[1] = 0x80; +diff -Nur linux-3.14.36/drivers/media/usb/dvb-usb/pctv452e.c linux-openelec/drivers/media/usb/dvb-usb/pctv452e.c +--- linux-3.14.36/drivers/media/usb/dvb-usb/pctv452e.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/dvb-usb/pctv452e.c 2015-07-24 18:03:30.140842002 -0500 +@@ -995,11 +995,11 @@ + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, +- .count = 7, ++ .count = 4, + .endpoint = 0x02, + .u = { + .isoc = { +- .framesperurb = 4, ++ .framesperurb = 64, + .framesize = 940, + .interval = 1 + } +diff -Nur linux-3.14.36/drivers/media/usb/dvb-usb-v2/dvbsky.c linux-openelec/drivers/media/usb/dvb-usb-v2/dvbsky.c +--- linux-3.14.36/drivers/media/usb/dvb-usb-v2/dvbsky.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/usb/dvb-usb-v2/dvbsky.c 2015-07-24 18:03:30.196842002 -0500 +@@ -0,0 +1,845 @@ ++/* ++ * Driver for DVBSky USB2.0 receiver ++ * ++ * Copyright (C) 2013 Max nibble ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "dvb_usb.h" ++#include "m88ds3103.h" ++#include "m88ts2022.h" ++#include "sp2.h" ++#include "si2168.h" ++#include "si2157.h" ++ ++#define DVBSKY_MSG_DELAY 0/*2000*/ ++#define DVBSKY_BUF_LEN 64 ++ ++static int dvb_usb_dvbsky_disable_rc; ++module_param_named(disable_rc, dvb_usb_dvbsky_disable_rc, int, 0644); ++MODULE_PARM_DESC(disable_rc, "Disable inbuilt IR receiver."); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++struct dvbsky_state { ++ struct mutex stream_mutex; ++ u8 ibuf[DVBSKY_BUF_LEN]; ++ u8 obuf[DVBSKY_BUF_LEN]; ++ u8 last_lock; ++ struct i2c_client *i2c_client_demod; ++ struct i2c_client *i2c_client_tuner; ++ struct i2c_client *i2c_client_ci; ++ ++ /* fe hook functions*/ ++ int (*fe_set_voltage)(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage); ++ int (*fe_read_status)(struct dvb_frontend *fe, ++ fe_status_t *status); ++}; ++ ++static int dvbsky_usb_generic_rw(struct dvb_usb_device *d, ++ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) ++{ ++ int ret; ++ struct dvbsky_state *state = d_to_priv(d); ++ ++ mutex_lock(&d->usb_mutex); ++ if (wlen != 0) ++ memcpy(state->obuf, wbuf, wlen); ++ ++ ret = dvb_usbv2_generic_rw_locked(d, state->obuf, wlen, ++ state->ibuf, rlen); ++ ++ if (!ret && (rlen != 0)) ++ memcpy(rbuf, state->ibuf, rlen); ++ ++ mutex_unlock(&d->usb_mutex); ++ return ret; ++} ++ ++static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff) ++{ ++ struct dvbsky_state *state = d_to_priv(d); ++ int ret; ++ u8 obuf_pre[3] = { 0x37, 0, 0 }; ++ u8 obuf_post[3] = { 0x36, 3, 0 }; ++ ++ mutex_lock(&state->stream_mutex); ++ ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0); ++ if (!ret && onoff) { ++ msleep(20); ++ ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0); ++ } ++ mutex_unlock(&state->stream_mutex); ++ return ret; ++} ++ ++static int dvbsky_streaming_ctrl(struct dvb_frontend *fe, int onoff) ++{ ++ struct dvb_usb_device *d = fe_to_d(fe); ++ ++ return dvbsky_stream_ctrl(d, (onoff == 0) ? 0 : 1); ++} ++ ++/* GPIO */ ++static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value) ++{ ++ int ret; ++ u8 obuf[3], ibuf[2]; ++ ++ obuf[0] = 0x0e; ++ obuf[1] = gport; ++ obuf[2] = value; ++ ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1); ++ if (ret) ++ dev_err(&d->udev->dev, "failed=%d\n", ret); ++ return ret; ++} ++ ++/* I2C */ ++static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ++ int num) ++{ ++ struct dvb_usb_device *d = i2c_get_adapdata(adap); ++ int ret = 0; ++ u8 ibuf[64], obuf[64]; ++ ++ if (mutex_lock_interruptible(&d->i2c_mutex) < 0) ++ return -EAGAIN; ++ ++ if (num > 2) { ++ dev_err(&d->udev->dev, ++ "too many i2c messages[%d], max 2.", num); ++ ret = -EOPNOTSUPP; ++ goto i2c_error; ++ } ++ ++ if (num == 1) { ++ if (msg[0].len > 60) { ++ dev_err(&d->udev->dev, ++ "too many i2c bytes[%d], max 60.", ++ msg[0].len); ++ ret = -EOPNOTSUPP; ++ goto i2c_error; ++ } ++ if (msg[0].flags & I2C_M_RD) { ++ /* single read */ ++ obuf[0] = 0x09; ++ obuf[1] = 0; ++ obuf[2] = msg[0].len; ++ obuf[3] = msg[0].addr; ++ ret = dvbsky_usb_generic_rw(d, obuf, 4, ++ ibuf, msg[0].len + 1); ++ if (ret) ++ dev_err(&d->udev->dev, "failed=%d\n", ret); ++ if (!ret) ++ memcpy(msg[0].buf, &ibuf[1], msg[0].len); ++ } else { ++ /* write */ ++ obuf[0] = 0x08; ++ obuf[1] = msg[0].addr; ++ obuf[2] = msg[0].len; ++ memcpy(&obuf[3], msg[0].buf, msg[0].len); ++ ret = dvbsky_usb_generic_rw(d, obuf, ++ msg[0].len + 3, ibuf, 1); ++ if (ret) ++ dev_err(&d->udev->dev, "failed=%d\n", ret); ++ } ++ } else { ++ if ((msg[0].len > 60) || (msg[1].len > 60)) { ++ dev_err(&d->udev->dev, ++ "too many i2c bytes[w-%d][r-%d], max 60.", ++ msg[0].len, msg[1].len); ++ ret = -EOPNOTSUPP; ++ goto i2c_error; ++ } ++ /* write then read */ ++ obuf[0] = 0x09; ++ obuf[1] = msg[0].len; ++ obuf[2] = msg[1].len; ++ obuf[3] = msg[0].addr; ++ memcpy(&obuf[4], msg[0].buf, msg[0].len); ++ ret = dvbsky_usb_generic_rw(d, obuf, ++ msg[0].len + 4, ibuf, msg[1].len + 1); ++ if (ret) ++ dev_err(&d->udev->dev, "failed=%d\n", ret); ++ ++ if (!ret) ++ memcpy(msg[1].buf, &ibuf[1], msg[1].len); ++ } ++i2c_error: ++ mutex_unlock(&d->i2c_mutex); ++ return (ret) ? ret : num; ++} ++ ++static u32 dvbsky_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm dvbsky_i2c_algo = { ++ .master_xfer = dvbsky_i2c_xfer, ++ .functionality = dvbsky_i2c_func, ++}; ++ ++#if IS_ENABLED(CONFIG_RC_CORE) ++static int dvbsky_rc_query(struct dvb_usb_device *d) ++{ ++ return 0; ++} ++ ++static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) ++{ ++ if (dvb_usb_dvbsky_disable_rc) { ++ rc->map_name = NULL; ++ return 0; ++ } ++ ++ rc->allowed_protos = RC_BIT_RC5; ++ rc->query = dvbsky_rc_query; ++ rc->interval = 300; ++ return 0; ++} ++#else ++ #define dvbsky_get_rc_config NULL ++#endif ++ ++static int dvbsky_usb_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct dvb_usb_device *d = fe_to_d(fe); ++ struct dvbsky_state *state = d_to_priv(d); ++ u8 value; ++ ++ if (voltage == SEC_VOLTAGE_OFF) ++ value = 0; ++ else ++ value = 1; ++ dvbsky_gpio_ctrl(d, 0x80, value); ++ ++ return state->fe_set_voltage(fe, voltage); ++} ++ ++static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6]) ++{ ++ struct dvb_usb_device *d = adap_to_d(adap); ++ u8 obuf[] = { 0x1e, 0x00 }; ++ u8 ibuf[6] = { 0 }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = 0x51, ++ .flags = 0, ++ .buf = obuf, ++ .len = 2, ++ }, { ++ .addr = 0x51, ++ .flags = I2C_M_RD, ++ .buf = ibuf, ++ .len = 6, ++ } ++ }; ++ ++ if (i2c_transfer(&d->i2c_adap, msg, 2) == 2) ++ memcpy(mac, ibuf, 6); ++ ++ return 0; ++} ++ ++static int dvbsky_usb_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct dvb_usb_device *d = fe_to_d(fe); ++ struct dvbsky_state *state = d_to_priv(d); ++ int ret; ++ ++ ret = state->fe_read_status(fe, status); ++ ++ /* it need resync slave fifo when signal change from unlock to lock.*/ ++ if ((*status & FE_HAS_LOCK) && (!state->last_lock)) ++ dvbsky_stream_ctrl(d, 1); ++ ++ state->last_lock = (*status & FE_HAS_LOCK) ? 1 : 0; ++ return ret; ++} ++ ++static const struct m88ds3103_config dvbsky_s960_m88ds3103_config = { ++ .i2c_addr = 0x68, ++ .clock = 27000000, ++ .i2c_wr_max = 33, ++ .clock_out = 0, ++ .ts_mode = M88DS3103_TS_CI, ++ .ts_clk = 16000, ++ .ts_clk_pol = 0, ++ .agc = 0x99, ++ .lnb_hv_pol = 1, ++ .lnb_en_pol = 1, ++}; ++ ++static int dvbsky_s960_attach(struct dvb_usb_adapter *adap) ++{ ++ struct dvbsky_state *state = adap_to_priv(adap); ++ struct dvb_usb_device *d = adap_to_d(adap); ++ int ret = 0; ++ /* demod I2C adapter */ ++ struct i2c_adapter *i2c_adapter; ++ struct i2c_client *client; ++ struct i2c_board_info info; ++ struct m88ts2022_config m88ts2022_config = { ++ .clock = 27000000, ++ }; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ ++ /* attach demod */ ++ adap->fe[0] = dvb_attach(m88ds3103_attach, ++ &dvbsky_s960_m88ds3103_config, ++ &d->i2c_adap, ++ &i2c_adapter); ++ if (!adap->fe[0]) { ++ dev_err(&d->udev->dev, "dvbsky_s960_attach fail.\n"); ++ ret = -ENODEV; ++ goto fail_attach; ++ } ++ ++ /* attach tuner */ ++ m88ts2022_config.fe = adap->fe[0]; ++ strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &m88ts2022_config; ++ request_module("m88ts2022"); ++ client = i2c_new_device(i2c_adapter, &info); ++ if (client == NULL || client->dev.driver == NULL) { ++ dvb_frontend_detach(adap->fe[0]); ++ ret = -ENODEV; ++ goto fail_attach; ++ } ++ ++ if (!try_module_get(client->dev.driver->owner)) { ++ i2c_unregister_device(client); ++ dvb_frontend_detach(adap->fe[0]); ++ ret = -ENODEV; ++ goto fail_attach; ++ } ++ ++ /* delegate signal strength measurement to tuner */ ++ adap->fe[0]->ops.read_signal_strength = ++ adap->fe[0]->ops.tuner_ops.get_rf_strength; ++ ++ /* hook fe: need to resync the slave fifo when signal locks. */ ++ state->fe_read_status = adap->fe[0]->ops.read_status; ++ adap->fe[0]->ops.read_status = dvbsky_usb_read_status; ++ ++ /* hook fe: LNB off/on is control by Cypress usb chip. */ ++ state->fe_set_voltage = adap->fe[0]->ops.set_voltage; ++ adap->fe[0]->ops.set_voltage = dvbsky_usb_set_voltage; ++ ++ state->i2c_client_tuner = client; ++ ++fail_attach: ++ return ret; ++} ++ ++static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct dvb_usb_device *d = fe_to_d(fe); ++ struct dvbsky_state *state = d_to_priv(d); ++ u8 value; ++ ++ if (voltage == SEC_VOLTAGE_OFF) ++ value = 0; ++ else ++ value = 1; ++ dvbsky_gpio_ctrl(d, 0x00, value); ++ ++ return state->fe_set_voltage(fe, voltage); ++} ++ ++static int dvbsky_ci_ctrl(void *priv, u8 read, int addr, ++ u8 data, int *mem) ++{ ++ struct dvb_usb_device *d = priv; ++ int ret = 0; ++ u8 command[4], respond[2], command_size, respond_size; ++ ++ command[1] = (u8)((addr >> 8) & 0xff); /*high part of address*/ ++ command[2] = (u8)(addr & 0xff); /*low part of address*/ ++ if (read) { ++ command[0] = 0x71; ++ command_size = 3; ++ respond_size = 2; ++ } else { ++ command[0] = 0x70; ++ command[3] = data; ++ command_size = 4; ++ respond_size = 1; ++ } ++ ret = dvbsky_usb_generic_rw(d, command, command_size, ++ respond, respond_size); ++ if (ret) ++ goto err; ++ if (read) ++ *mem = respond[1]; ++ return ret; ++err: ++ dev_err(&d->udev->dev, "ci control failed=%d\n", ret); ++ return ret; ++} ++ ++static const struct m88ds3103_config dvbsky_s960c_m88ds3103_config = { ++ .i2c_addr = 0x68, ++ .clock = 27000000, ++ .i2c_wr_max = 33, ++ .clock_out = 0, ++ .ts_mode = M88DS3103_TS_CI, ++ .agc = 0x99, ++}; ++ ++static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap) ++{ ++ struct dvbsky_state *state = adap_to_priv(adap); ++ struct dvb_usb_device *d = adap_to_d(adap); ++ int ret = 0; ++ /* demod I2C adapter */ ++ struct i2c_adapter *i2c_adapter; ++ struct i2c_client *client_tuner, *client_ci; ++ struct i2c_board_info info; ++ struct sp2_config sp2_config; ++ struct m88ts2022_config m88ts2022_config = { ++ .clock = 27000000, ++ }; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ ++ /* attach demod */ ++ adap->fe[0] = dvb_attach(m88ds3103_attach, ++ &dvbsky_s960c_m88ds3103_config, ++ &d->i2c_adap, ++ &i2c_adapter); ++ if (!adap->fe[0]) { ++ dev_err(&d->udev->dev, "dvbsky_s960ci_attach fail.\n"); ++ ret = -ENODEV; ++ goto fail_attach; ++ } ++ ++ /* attach tuner */ ++ m88ts2022_config.fe = adap->fe[0]; ++ strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &m88ts2022_config; ++ request_module("m88ts2022"); ++ client_tuner = i2c_new_device(i2c_adapter, &info); ++ if (client_tuner == NULL || client_tuner->dev.driver == NULL) { ++ ret = -ENODEV; ++ goto fail_tuner_device; ++ } ++ ++ if (!try_module_get(client_tuner->dev.driver->owner)) { ++ ret = -ENODEV; ++ goto fail_tuner_module; ++ } ++ ++ /* attach ci controller */ ++ memset(&sp2_config, 0, sizeof(sp2_config)); ++ sp2_config.dvb_adap = &adap->dvb_adap; ++ sp2_config.priv = d; ++ sp2_config.ci_control = dvbsky_ci_ctrl; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "sp2", I2C_NAME_SIZE); ++ info.addr = 0x40; ++ info.platform_data = &sp2_config; ++ request_module("sp2"); ++ client_ci = i2c_new_device(&d->i2c_adap, &info); ++ if (client_ci == NULL || client_ci->dev.driver == NULL) { ++ ret = -ENODEV; ++ goto fail_ci_device; ++ } ++ ++ if (!try_module_get(client_ci->dev.driver->owner)) { ++ ret = -ENODEV; ++ goto fail_ci_module; ++ } ++ ++ /* delegate signal strength measurement to tuner */ ++ adap->fe[0]->ops.read_signal_strength = ++ adap->fe[0]->ops.tuner_ops.get_rf_strength; ++ ++ /* hook fe: need to resync the slave fifo when signal locks. */ ++ state->fe_read_status = adap->fe[0]->ops.read_status; ++ adap->fe[0]->ops.read_status = dvbsky_usb_read_status; ++ ++ /* hook fe: LNB off/on is control by Cypress usb chip. */ ++ state->fe_set_voltage = adap->fe[0]->ops.set_voltage; ++ adap->fe[0]->ops.set_voltage = dvbsky_usb_ci_set_voltage; ++ ++ state->i2c_client_tuner = client_tuner; ++ state->i2c_client_ci = client_ci; ++ return ret; ++fail_ci_module: ++ i2c_unregister_device(client_ci); ++fail_ci_device: ++ module_put(client_tuner->dev.driver->owner); ++fail_tuner_module: ++ i2c_unregister_device(client_tuner); ++fail_tuner_device: ++ dvb_frontend_detach(adap->fe[0]); ++fail_attach: ++ return ret; ++} ++ ++static int dvbsky_t680c_attach(struct dvb_usb_adapter *adap) ++{ ++ struct dvbsky_state *state = adap_to_priv(adap); ++ struct dvb_usb_device *d = adap_to_d(adap); ++ int ret = 0; ++ struct i2c_adapter *i2c_adapter; ++ struct i2c_client *client_demod, *client_tuner, *client_ci; ++ struct i2c_board_info info; ++ struct si2168_config si2168_config; ++ struct si2157_config si2157_config; ++ struct sp2_config sp2_config; ++ ++ /* attach demod */ ++ memset(&si2168_config, 0, sizeof(si2168_config)); ++ si2168_config.i2c_adapter = &i2c_adapter; ++ si2168_config.fe = &adap->fe[0]; ++ si2168_config.ts_mode = SI2168_TS_PARALLEL; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2168", I2C_NAME_SIZE); ++ info.addr = 0x64; ++ info.platform_data = &si2168_config; ++ ++ request_module(info.type); ++ client_demod = i2c_new_device(&d->i2c_adap, &info); ++ if (client_demod == NULL || ++ client_demod->dev.driver == NULL) ++ goto fail_demod_device; ++ if (!try_module_get(client_demod->dev.driver->owner)) ++ goto fail_demod_module; ++ ++ /* attach tuner */ ++ memset(&si2157_config, 0, sizeof(si2157_config)); ++ si2157_config.fe = adap->fe[0]; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2157", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &si2157_config; ++ ++ request_module(info.type); ++ client_tuner = i2c_new_device(i2c_adapter, &info); ++ if (client_tuner == NULL || ++ client_tuner->dev.driver == NULL) ++ goto fail_tuner_device; ++ if (!try_module_get(client_tuner->dev.driver->owner)) ++ goto fail_tuner_module; ++ ++ /* attach ci controller */ ++ memset(&sp2_config, 0, sizeof(sp2_config)); ++ sp2_config.dvb_adap = &adap->dvb_adap; ++ sp2_config.priv = d; ++ sp2_config.ci_control = dvbsky_ci_ctrl; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "sp2", I2C_NAME_SIZE); ++ info.addr = 0x40; ++ info.platform_data = &sp2_config; ++ ++ request_module(info.type); ++ client_ci = i2c_new_device(&d->i2c_adap, &info); ++ ++ if (client_ci == NULL || client_ci->dev.driver == NULL) ++ goto fail_ci_device; ++ ++ if (!try_module_get(client_ci->dev.driver->owner)) ++ goto fail_ci_module; ++ ++ state->i2c_client_demod = client_demod; ++ state->i2c_client_tuner = client_tuner; ++ state->i2c_client_ci = client_ci; ++ return ret; ++fail_ci_module: ++ i2c_unregister_device(client_ci); ++fail_ci_device: ++ module_put(client_tuner->dev.driver->owner); ++fail_tuner_module: ++ i2c_unregister_device(client_tuner); ++fail_tuner_device: ++ module_put(client_demod->dev.driver->owner); ++fail_demod_module: ++ i2c_unregister_device(client_demod); ++fail_demod_device: ++ ret = -ENODEV; ++ return ret; ++} ++ ++static int dvbsky_t330_attach(struct dvb_usb_adapter *adap) ++{ ++ struct dvbsky_state *state = adap_to_priv(adap); ++ struct dvb_usb_device *d = adap_to_d(adap); ++ int ret = 0; ++ struct i2c_adapter *i2c_adapter; ++ struct i2c_client *client_demod, *client_tuner; ++ struct i2c_board_info info; ++ struct si2168_config si2168_config; ++ struct si2157_config si2157_config; ++ ++ /* attach demod */ ++ memset(&si2168_config, 0, sizeof(si2168_config)); ++ si2168_config.i2c_adapter = &i2c_adapter; ++ si2168_config.fe = &adap->fe[0]; ++ si2168_config.ts_mode = SI2168_TS_PARALLEL | 0x40; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2168", I2C_NAME_SIZE); ++ info.addr = 0x64; ++ info.platform_data = &si2168_config; ++ ++ request_module(info.type); ++ client_demod = i2c_new_device(&d->i2c_adap, &info); ++ if (client_demod == NULL || ++ client_demod->dev.driver == NULL) ++ goto fail_demod_device; ++ if (!try_module_get(client_demod->dev.driver->owner)) ++ goto fail_demod_module; ++ ++ /* attach tuner */ ++ memset(&si2157_config, 0, sizeof(si2157_config)); ++ si2157_config.fe = adap->fe[0]; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2157", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &si2157_config; ++ ++ request_module(info.type); ++ client_tuner = i2c_new_device(i2c_adapter, &info); ++ if (client_tuner == NULL || ++ client_tuner->dev.driver == NULL) ++ goto fail_tuner_device; ++ if (!try_module_get(client_tuner->dev.driver->owner)) ++ goto fail_tuner_module; ++ ++ state->i2c_client_demod = client_demod; ++ state->i2c_client_tuner = client_tuner; ++ return ret; ++fail_tuner_module: ++ i2c_unregister_device(client_tuner); ++fail_tuner_device: ++ module_put(client_demod->dev.driver->owner); ++fail_demod_module: ++ i2c_unregister_device(client_demod); ++fail_demod_device: ++ ret = -ENODEV; ++ return ret; ++} ++ ++static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name) ++{ ++ dvbsky_gpio_ctrl(d, 0x04, 1); ++ msleep(20); ++ dvbsky_gpio_ctrl(d, 0x83, 0); ++ dvbsky_gpio_ctrl(d, 0xc0, 1); ++ msleep(100); ++ dvbsky_gpio_ctrl(d, 0x83, 1); ++ dvbsky_gpio_ctrl(d, 0xc0, 0); ++ msleep(50); ++ ++ return WARM; ++} ++ ++static int dvbsky_init(struct dvb_usb_device *d) ++{ ++ struct dvbsky_state *state = d_to_priv(d); ++ ++ /* use default interface */ ++ /* ++ ret = usb_set_interface(d->udev, 0, 0); ++ if (ret) ++ return ret; ++ */ ++ mutex_init(&state->stream_mutex); ++ ++ state->last_lock = 0; ++ ++ return 0; ++} ++ ++static void dvbsky_exit(struct dvb_usb_device *d) ++{ ++ struct dvbsky_state *state = d_to_priv(d); ++ struct i2c_client *client; ++ ++ client = state->i2c_client_tuner; ++ /* remove I2C tuner */ ++ if (client) { ++ module_put(client->dev.driver->owner); ++ i2c_unregister_device(client); ++ } ++ client = state->i2c_client_demod; ++ /* remove I2C demod */ ++ if (client) { ++ module_put(client->dev.driver->owner); ++ i2c_unregister_device(client); ++ } ++ client = state->i2c_client_ci; ++ /* remove I2C ci */ ++ if (client) { ++ module_put(client->dev.driver->owner); ++ i2c_unregister_device(client); ++ } ++} ++ ++/* DVB USB Driver stuff */ ++static struct dvb_usb_device_properties dvbsky_s960_props = { ++ .driver_name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .adapter_nr = adapter_nr, ++ .size_of_priv = sizeof(struct dvbsky_state), ++ ++ .generic_bulk_ctrl_endpoint = 0x01, ++ .generic_bulk_ctrl_endpoint_response = 0x81, ++ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY, ++ ++ .i2c_algo = &dvbsky_i2c_algo, ++ .frontend_attach = dvbsky_s960_attach, ++ .init = dvbsky_init, ++ .get_rc_config = dvbsky_get_rc_config, ++ .streaming_ctrl = dvbsky_streaming_ctrl, ++ .identify_state = dvbsky_identify_state, ++ .exit = dvbsky_exit, ++ .read_mac_address = dvbsky_read_mac_addr, ++ ++ .num_adapters = 1, ++ .adapter = { ++ { ++ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096), ++ } ++ } ++}; ++ ++static struct dvb_usb_device_properties dvbsky_s960c_props = { ++ .driver_name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .adapter_nr = adapter_nr, ++ .size_of_priv = sizeof(struct dvbsky_state), ++ ++ .generic_bulk_ctrl_endpoint = 0x01, ++ .generic_bulk_ctrl_endpoint_response = 0x81, ++ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY, ++ ++ .i2c_algo = &dvbsky_i2c_algo, ++ .frontend_attach = dvbsky_s960c_attach, ++ .init = dvbsky_init, ++ .get_rc_config = dvbsky_get_rc_config, ++ .streaming_ctrl = dvbsky_streaming_ctrl, ++ .identify_state = dvbsky_identify_state, ++ .exit = dvbsky_exit, ++ .read_mac_address = dvbsky_read_mac_addr, ++ ++ .num_adapters = 1, ++ .adapter = { ++ { ++ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096), ++ } ++ } ++}; ++ ++static struct dvb_usb_device_properties dvbsky_t680c_props = { ++ .driver_name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .adapter_nr = adapter_nr, ++ .size_of_priv = sizeof(struct dvbsky_state), ++ ++ .generic_bulk_ctrl_endpoint = 0x01, ++ .generic_bulk_ctrl_endpoint_response = 0x81, ++ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY, ++ ++ .i2c_algo = &dvbsky_i2c_algo, ++ .frontend_attach = dvbsky_t680c_attach, ++ .init = dvbsky_init, ++ .get_rc_config = dvbsky_get_rc_config, ++ .streaming_ctrl = dvbsky_streaming_ctrl, ++ .identify_state = dvbsky_identify_state, ++ .exit = dvbsky_exit, ++ .read_mac_address = dvbsky_read_mac_addr, ++ ++ .num_adapters = 1, ++ .adapter = { ++ { ++ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096), ++ } ++ } ++}; ++ ++static struct dvb_usb_device_properties dvbsky_t330_props = { ++ .driver_name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .adapter_nr = adapter_nr, ++ .size_of_priv = sizeof(struct dvbsky_state), ++ ++ .generic_bulk_ctrl_endpoint = 0x01, ++ .generic_bulk_ctrl_endpoint_response = 0x81, ++ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY, ++ ++ .i2c_algo = &dvbsky_i2c_algo, ++ .frontend_attach = dvbsky_t330_attach, ++ .init = dvbsky_init, ++ .get_rc_config = dvbsky_get_rc_config, ++ .streaming_ctrl = dvbsky_streaming_ctrl, ++ .identify_state = dvbsky_identify_state, ++ .exit = dvbsky_exit, ++ .read_mac_address = dvbsky_read_mac_addr, ++ ++ .num_adapters = 1, ++ .adapter = { ++ { ++ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096), ++ } ++ } ++}; ++ ++static const struct usb_device_id dvbsky_id_table[] = { ++ { DVB_USB_DEVICE(0x0572, 0x6831, ++ &dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) }, ++ { DVB_USB_DEVICE(0x0572, 0x960c, ++ &dvbsky_s960c_props, "DVBSky S960CI", RC_MAP_DVBSKY) }, ++ { DVB_USB_DEVICE(0x0572, 0x680c, ++ &dvbsky_t680c_props, "DVBSky T680CI", RC_MAP_DVBSKY) }, ++ { DVB_USB_DEVICE(0x0572, 0x0320, ++ &dvbsky_t330_props, "DVBSky T330", RC_MAP_DVBSKY) }, ++ { DVB_USB_DEVICE(USB_VID_TECHNOTREND, ++ USB_PID_TECHNOTREND_TVSTICK_CT2_4400, ++ &dvbsky_t330_props, "TechnoTrend TVStick CT2-4400", ++ RC_MAP_TT_1500) }, ++ { DVB_USB_DEVICE(USB_VID_TECHNOTREND, ++ USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI, ++ &dvbsky_t680c_props, "TechnoTrend TT-connect CT2-4650 CI", ++ RC_MAP_TT_1500) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(usb, dvbsky_id_table); ++ ++static struct usb_driver dvbsky_usb_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = dvbsky_id_table, ++ .probe = dvb_usbv2_probe, ++ .disconnect = dvb_usbv2_disconnect, ++ .suspend = dvb_usbv2_suspend, ++ .resume = dvb_usbv2_resume, ++ .reset_resume = dvb_usbv2_reset_resume, ++ .no_dynamic_id = 1, ++ .soft_unbind = 1, ++}; ++ ++module_usb_driver(dvbsky_usb_driver); ++ ++MODULE_AUTHOR("Max nibble "); ++MODULE_DESCRIPTION("Driver for DVBSky USB"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.36/drivers/media/usb/dvb-usb-v2/Kconfig linux-openelec/drivers/media/usb/dvb-usb-v2/Kconfig +--- linux-3.14.36/drivers/media/usb/dvb-usb-v2/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/dvb-usb-v2/Kconfig 2015-07-24 18:03:30.152842002 -0500 +@@ -147,3 +147,12 @@ + help + Say Y here to support the Realtek RTL28xxU DVB USB receiver. + ++config DVB_USB_DVBSKY ++ tristate "DVBSky USB2.0 support" ++ depends on DVB_USB_V2 ++ select DVB_DVBSKY_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y here to support the USB receivers from DVBSky. +diff -Nur linux-3.14.36/drivers/media/usb/dvb-usb-v2/Makefile linux-openelec/drivers/media/usb/dvb-usb-v2/Makefile +--- linux-3.14.36/drivers/media/usb/dvb-usb-v2/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/dvb-usb-v2/Makefile 2015-07-24 18:03:30.128842002 -0500 +@@ -40,6 +40,9 @@ + dvb-usb-rtl28xxu-objs := rtl28xxu.o + obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o + ++dvb-usb-dvbsky-objs := dvbsky.o ++obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o ++ + ccflags-y += -I$(srctree)/drivers/media/dvb-core + ccflags-y += -I$(srctree)/drivers/media/dvb-frontends + ccflags-y += -I$(srctree)/drivers/media/tuners +diff -Nur linux-3.14.36/drivers/media/usb/dvb-usb-v2/rtl28xxu.c linux-openelec/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +--- linux-3.14.36/drivers/media/usb/dvb-usb-v2/rtl28xxu.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/dvb-usb-v2/rtl28xxu.c 2015-07-24 18:03:30.060842002 -0500 +@@ -1441,6 +1441,8 @@ + &rtl2832u_props, "Sveon STV20", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV27, + &rtl2832u_props, "Sveon STV27", NULL) }, ++ { DVB_USB_DEVICE(USB_VID_GTEK, 0xa803, ++ &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, + + /* RTL2832P devices: */ + { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131, +diff -Nur linux-3.14.36/drivers/media/usb/em28xx/em28xx-cards.c linux-openelec/drivers/media/usb/em28xx/em28xx-cards.c +--- linux-3.14.36/drivers/media/usb/em28xx/em28xx-cards.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/em28xx/em28xx-cards.c 2015-07-24 18:03:30.156842002 -0500 +@@ -448,6 +448,18 @@ + { -1, -1, -1, -1}, + }; + ++static struct em28xx_reg_seq pctv_292e[] = { ++ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, ++ {0x0d, 0xff, 0xff, 950}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xbd, 0xff, 100}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 410}, ++ {EM2874_R80_GPIO_P0_CTRL, 0x7d, 0xff, 300}, ++ {EM2874_R80_GPIO_P0_CTRL, 0x7c, 0xff, 60}, ++ {0x0d, 0x42, 0xff, 50}, ++ {EM2874_R5F_TS_ENABLE, 0x85, 0xff, 0}, ++ {-1, -1, -1, -1}, ++}; ++ + /* + * Button definitions + */ +@@ -2157,6 +2169,17 @@ + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, ++ /* 2013:025f PCTV tripleStick (292e). ++ * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 */ ++ [EM28178_BOARD_PCTV_292E] = { ++ .name = "PCTV tripleStick (292e)", ++ .def_i2c_bus = 1, ++ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_gpio = pctv_292e, ++ .has_dvb = 1, ++ .ir_codes = RC_MAP_PINNACLE_PCTV_HD, ++ }, + }; + EXPORT_SYMBOL_GPL(em28xx_boards); + +@@ -2330,6 +2353,8 @@ + .driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE }, + { USB_DEVICE(0x2013, 0x0258), + .driver_info = EM28178_BOARD_PCTV_461E }, ++ { USB_DEVICE(0x2013, 0x025f), ++ .driver_info = EM28178_BOARD_PCTV_292E }, + { }, + }; + MODULE_DEVICE_TABLE(usb, em28xx_id_table); +diff -Nur linux-3.14.36/drivers/media/usb/em28xx/em28xx-dvb.c linux-openelec/drivers/media/usb/em28xx/em28xx-dvb.c +--- linux-3.14.36/drivers/media/usb/em28xx/em28xx-dvb.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/em28xx/em28xx-dvb.c 2015-07-24 18:03:30.156842002 -0500 +@@ -53,6 +53,8 @@ + #include "mb86a20s.h" + #include "m88ds3103.h" + #include "m88ts2022.h" ++#include "si2168.h" ++#include "si2157.h" + + MODULE_AUTHOR("Mauro Carvalho Chehab "); + MODULE_LICENSE("GPL"); +@@ -91,6 +93,7 @@ + struct semaphore pll_mutex; + bool dont_attach_fe1; + int lna_gpio; ++ struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; + }; + +@@ -719,6 +722,21 @@ + #endif + } + ++static int em28xx_pctv_292e_set_lna(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv; ++ struct em28xx *dev = i2c_bus->dev; ++ u8 lna; ++ ++ if (c->lna == 1) ++ lna = 0x01; ++ else ++ lna = 0x00; ++ ++ return em28xx_write_reg_bits(dev, EM2874_R80_GPIO_P0_CTRL, lna, 0x01); ++} ++ + static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) + { + /* Values extracted from a USB trace of the Terratec Windows driver */ +@@ -814,7 +832,9 @@ + .clock = 27000000, + .i2c_wr_max = 33, + .clock_out = 0, +- .ts_mode = M88DS3103_TS_PARALLEL_16, ++ .ts_mode = M88DS3103_TS_PARALLEL, ++ .ts_clk = 16000, ++ .ts_clk_pol = 1, + .agc = 0x99, + }; + +@@ -1413,6 +1433,66 @@ + } + } + break; ++ case EM28178_BOARD_PCTV_292E: ++ { ++ struct i2c_adapter *adapter; ++ struct i2c_client *client; ++ struct i2c_board_info info; ++ struct si2168_config si2168_config; ++ struct si2157_config si2157_config; ++ ++ /* attach demod */ ++ memset(&si2168_config, 0, sizeof(si2168_config)); ++ si2168_config.i2c_adapter = &adapter; ++ si2168_config.fe = &dvb->fe[0]; ++ si2168_config.ts_mode = SI2168_TS_PARALLEL; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2168", I2C_NAME_SIZE); ++ info.addr = 0x64; ++ info.platform_data = &si2168_config; ++ request_module(info.type); ++ client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); ++ if (client == NULL || client->dev.driver == NULL) { ++ result = -ENODEV; ++ goto out_free; ++ } ++ ++ if (!try_module_get(client->dev.driver->owner)) { ++ i2c_unregister_device(client); ++ result = -ENODEV; ++ goto out_free; ++ } ++ ++ dvb->i2c_client_demod = client; ++ ++ /* attach tuner */ ++ memset(&si2157_config, 0, sizeof(si2157_config)); ++ si2157_config.fe = dvb->fe[0]; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2157", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &si2157_config; ++ request_module(info.type); ++ client = i2c_new_device(adapter, &info); ++ if (client == NULL || client->dev.driver == NULL) { ++ module_put(dvb->i2c_client_demod->dev.driver->owner); ++ i2c_unregister_device(dvb->i2c_client_demod); ++ result = -ENODEV; ++ goto out_free; ++ } ++ ++ if (!try_module_get(client->dev.driver->owner)) { ++ i2c_unregister_device(client); ++ module_put(dvb->i2c_client_demod->dev.driver->owner); ++ i2c_unregister_device(dvb->i2c_client_demod); ++ result = -ENODEV; ++ goto out_free; ++ } ++ ++ dvb->i2c_client_tuner = client; ++ dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna; ++ } ++ break; + default: + em28xx_errdev("/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n"); +@@ -1485,6 +1565,10 @@ + } + + i2c_release_client(dvb->i2c_client_tuner); ++ /* remove I2C demod */ ++ if (dvb->i2c_client_demod) { ++ i2c_unregister_device(dvb->i2c_client_demod); ++ } + em28xx_unregister_dvb(dvb); + kfree(dvb); + dev->dvb = NULL; +diff -Nur linux-3.14.36/drivers/media/usb/em28xx/em28xx-dvb.c.orig linux-openelec/drivers/media/usb/em28xx/em28xx-dvb.c.orig +--- linux-3.14.36/drivers/media/usb/em28xx/em28xx-dvb.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/media/usb/em28xx/em28xx-dvb.c.orig 2015-07-24 18:03:30.152842002 -0500 +@@ -0,0 +1,1516 @@ ++/* ++ DVB device driver for em28xx ++ ++ (c) 2008-2011 Mauro Carvalho Chehab ++ ++ (c) 2008 Devin Heitmueller ++ - Fixes for the driver to properly work with HVR-950 ++ - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick ++ - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600 ++ ++ (c) 2008 Aidan Thornton ++ ++ (c) 2012 Frank Schäfer ++ ++ Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: ++ (c) 2004, 2005 Chris Pascoe ++ (c) 2004 Gerd Knorr [SuSE Labs] ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License. ++ */ ++ ++#include ++#include ++#include ++ ++#include "em28xx.h" ++#include ++#include ++#include ++#include ++#include ++#include "tuner-simple.h" ++#include ++ ++#include "lgdt330x.h" ++#include "lgdt3305.h" ++#include "zl10353.h" ++#include "s5h1409.h" ++#include "mt352.h" ++#include "mt352_priv.h" /* FIXME */ ++#include "tda1002x.h" ++#include "tda18271.h" ++#include "s921.h" ++#include "drxd.h" ++#include "cxd2820r.h" ++#include "tda18271c2dd.h" ++#include "drxk.h" ++#include "tda10071.h" ++#include "a8293.h" ++#include "qt1010.h" ++#include "mb86a20s.h" ++#include "m88ds3103.h" ++#include "m88ts2022.h" ++ ++MODULE_AUTHOR("Mauro Carvalho Chehab "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION(DRIVER_DESC " - digital TV interface"); ++MODULE_VERSION(EM28XX_VERSION); ++ ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define dprintk(level, fmt, arg...) do { \ ++if (debug >= level) \ ++ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ ++} while (0) ++ ++struct em28xx_dvb { ++ struct dvb_frontend *fe[2]; ++ ++ /* feed count management */ ++ struct mutex lock; ++ int nfeeds; ++ ++ /* general boilerplate stuff */ ++ struct dvb_adapter adapter; ++ struct dvb_demux demux; ++ struct dmxdev dmxdev; ++ struct dmx_frontend fe_hw; ++ struct dmx_frontend fe_mem; ++ struct dvb_net net; ++ ++ /* Due to DRX-K - probably need changes */ ++ int (*gate_ctrl)(struct dvb_frontend *, int); ++ struct semaphore pll_mutex; ++ bool dont_attach_fe1; ++ int lna_gpio; ++ struct i2c_client *i2c_client_tuner; ++}; ++ ++ ++static inline void print_err_status(struct em28xx *dev, ++ int packet, int status) ++{ ++ char *errmsg = "Unknown"; ++ ++ switch (status) { ++ case -ENOENT: ++ errmsg = "unlinked synchronuously"; ++ break; ++ case -ECONNRESET: ++ errmsg = "unlinked asynchronuously"; ++ break; ++ case -ENOSR: ++ errmsg = "Buffer error (overrun)"; ++ break; ++ case -EPIPE: ++ errmsg = "Stalled (device not responding)"; ++ break; ++ case -EOVERFLOW: ++ errmsg = "Babble (bad cable?)"; ++ break; ++ case -EPROTO: ++ errmsg = "Bit-stuff error (bad cable?)"; ++ break; ++ case -EILSEQ: ++ errmsg = "CRC/Timeout (could be anything)"; ++ break; ++ case -ETIME: ++ errmsg = "Device does not respond"; ++ break; ++ } ++ if (packet < 0) { ++ dprintk(1, "URB status %d [%s].\n", status, errmsg); ++ } else { ++ dprintk(1, "URB packet %d, status %d [%s].\n", ++ packet, status, errmsg); ++ } ++} ++ ++static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) ++{ ++ int xfer_bulk, num_packets, i; ++ ++ if (!dev) ++ return 0; ++ ++ if (dev->disconnected) ++ return 0; ++ ++ if (urb->status < 0) ++ print_err_status(dev, -1, urb->status); ++ ++ xfer_bulk = usb_pipebulk(urb->pipe); ++ ++ if (xfer_bulk) /* bulk */ ++ num_packets = 1; ++ else /* isoc */ ++ num_packets = urb->number_of_packets; ++ ++ for (i = 0; i < num_packets; i++) { ++ if (xfer_bulk) { ++ if (urb->status < 0) { ++ print_err_status(dev, i, urb->status); ++ if (urb->status != -EPROTO) ++ continue; ++ } ++ dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, ++ urb->actual_length); ++ } else { ++ if (urb->iso_frame_desc[i].status < 0) { ++ print_err_status(dev, i, ++ urb->iso_frame_desc[i].status); ++ if (urb->iso_frame_desc[i].status != -EPROTO) ++ continue; ++ } ++ dvb_dmx_swfilter(&dev->dvb->demux, ++ urb->transfer_buffer + ++ urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].actual_length); ++ } ++ } ++ ++ return 0; ++} ++ ++static int em28xx_start_streaming(struct em28xx_dvb *dvb) ++{ ++ int rc; ++ struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; ++ struct em28xx *dev = i2c_bus->dev; ++ int dvb_max_packet_size, packet_multiplier, dvb_alt; ++ ++ if (dev->dvb_xfer_bulk) { ++ if (!dev->dvb_ep_bulk) ++ return -ENODEV; ++ dvb_max_packet_size = 512; /* USB 2.0 spec */ ++ packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER; ++ dvb_alt = 0; ++ } else { /* isoc */ ++ if (!dev->dvb_ep_isoc) ++ return -ENODEV; ++ dvb_max_packet_size = dev->dvb_max_pkt_size_isoc; ++ if (dvb_max_packet_size < 0) ++ return dvb_max_packet_size; ++ packet_multiplier = EM28XX_DVB_NUM_ISOC_PACKETS; ++ dvb_alt = dev->dvb_alt_isoc; ++ } ++ ++ usb_set_interface(dev->udev, dev->ifnum, dvb_alt); ++ rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); ++ if (rc < 0) ++ return rc; ++ ++ dprintk(1, "Using %d buffers each with %d x %d bytes\n", ++ EM28XX_DVB_NUM_BUFS, ++ packet_multiplier, ++ dvb_max_packet_size); ++ ++ return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, ++ dev->dvb_xfer_bulk, ++ EM28XX_DVB_NUM_BUFS, ++ dvb_max_packet_size, ++ packet_multiplier, ++ em28xx_dvb_urb_data_copy); ++} ++ ++static int em28xx_stop_streaming(struct em28xx_dvb *dvb) ++{ ++ struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; ++ struct em28xx *dev = i2c_bus->dev; ++ ++ em28xx_stop_urbs(dev); ++ ++ return 0; ++} ++ ++static int em28xx_start_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct em28xx_dvb *dvb = demux->priv; ++ int rc, ret; ++ ++ if (!demux->dmx.frontend) ++ return -EINVAL; ++ ++ mutex_lock(&dvb->lock); ++ dvb->nfeeds++; ++ rc = dvb->nfeeds; ++ ++ if (dvb->nfeeds == 1) { ++ ret = em28xx_start_streaming(dvb); ++ if (ret < 0) ++ rc = ret; ++ } ++ ++ mutex_unlock(&dvb->lock); ++ return rc; ++} ++ ++static int em28xx_stop_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct em28xx_dvb *dvb = demux->priv; ++ int err = 0; ++ ++ mutex_lock(&dvb->lock); ++ dvb->nfeeds--; ++ ++ if (0 == dvb->nfeeds) ++ err = em28xx_stop_streaming(dvb); ++ ++ mutex_unlock(&dvb->lock); ++ return err; ++} ++ ++ ++ ++/* ------------------------------------------------------------------ */ ++static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) ++{ ++ struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv; ++ struct em28xx *dev = i2c_bus->dev; ++ ++ if (acquire) ++ return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); ++ else ++ return em28xx_set_mode(dev, EM28XX_SUSPEND); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static struct lgdt330x_config em2880_lgdt3303_dev = { ++ .demod_address = 0x0e, ++ .demod_chip = LGDT3303, ++}; ++ ++static struct lgdt3305_config em2870_lgdt3304_dev = { ++ .i2c_addr = 0x0e, ++ .demod_chip = LGDT3304, ++ .spectral_inversion = 1, ++ .deny_i2c_rptr = 1, ++ .mpeg_mode = LGDT3305_MPEG_PARALLEL, ++ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, ++ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, ++ .vsb_if_khz = 3250, ++ .qam_if_khz = 4000, ++}; ++ ++static struct lgdt3305_config em2874_lgdt3305_dev = { ++ .i2c_addr = 0x0e, ++ .demod_chip = LGDT3305, ++ .spectral_inversion = 1, ++ .deny_i2c_rptr = 0, ++ .mpeg_mode = LGDT3305_MPEG_SERIAL, ++ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, ++ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, ++ .vsb_if_khz = 3250, ++ .qam_if_khz = 4000, ++}; ++ ++static struct s921_config sharp_isdbt = { ++ .demod_address = 0x30 >> 1 ++}; ++ ++static struct zl10353_config em28xx_zl10353_with_xc3028 = { ++ .demod_address = (0x1e >> 1), ++ .no_tuner = 1, ++ .parallel_ts = 1, ++ .if2 = 45600, ++}; ++ ++static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_PARALLEL_OUTPUT, ++ .gpio = S5H1409_GPIO_OFF, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK ++}; ++ ++static struct tda18271_std_map kworld_a340_std_map = { ++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++}; ++ ++static struct tda18271_config kworld_a340_config = { ++ .std_map = &kworld_a340_std_map, ++}; ++ ++static struct tda18271_config kworld_ub435q_v2_config = { ++ .std_map = &kworld_a340_std_map, ++ .gate = TDA18271_GATE_DIGITAL, ++}; ++ ++static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { ++ .demod_address = (0x1e >> 1), ++ .no_tuner = 1, ++ .disable_i2c_gate_ctrl = 1, ++ .parallel_ts = 1, ++ .if2 = 45600, ++}; ++ ++static struct drxd_config em28xx_drxd = { ++ .demod_address = 0x70, ++ .demod_revision = 0xa2, ++ .pll_type = DRXD_PLL_NONE, ++ .clock = 12000, ++ .insert_rs_byte = 1, ++ .IF = 42800000, ++ .disable_i2c_gate_ctrl = 1, ++}; ++ ++static struct drxk_config terratec_h5_drxk = { ++ .adr = 0x29, ++ .single_master = 1, ++ .no_i2c_bridge = 1, ++ .microcode_name = "dvb-usb-terratec-h5-drxk.fw", ++ .qam_demod_parameter_count = 2, ++}; ++ ++static struct drxk_config hauppauge_930c_drxk = { ++ .adr = 0x29, ++ .single_master = 1, ++ .no_i2c_bridge = 1, ++ .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw", ++ .chunk_size = 56, ++ .qam_demod_parameter_count = 2, ++}; ++ ++static struct drxk_config terratec_htc_stick_drxk = { ++ .adr = 0x29, ++ .single_master = 1, ++ .no_i2c_bridge = 1, ++ .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw", ++ .chunk_size = 54, ++ .qam_demod_parameter_count = 2, ++ /* Required for the antenna_gpio to disable LNA. */ ++ .antenna_dvbt = true, ++ /* The windows driver uses the same. This will disable LNA. */ ++ .antenna_gpio = 0x6, ++}; ++ ++static struct drxk_config maxmedia_ub425_tc_drxk = { ++ .adr = 0x29, ++ .single_master = 1, ++ .no_i2c_bridge = 1, ++ .microcode_name = "dvb-demod-drxk-01.fw", ++ .chunk_size = 62, ++ .qam_demod_parameter_count = 2, ++}; ++ ++static struct drxk_config pctv_520e_drxk = { ++ .adr = 0x29, ++ .single_master = 1, ++ .microcode_name = "dvb-demod-drxk-pctv.fw", ++ .qam_demod_parameter_count = 2, ++ .chunk_size = 58, ++ .antenna_dvbt = true, /* disable LNA */ ++ .antenna_gpio = (1 << 2), /* disable LNA */ ++}; ++ ++static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct em28xx_dvb *dvb = fe->sec_priv; ++ int status; ++ ++ if (!dvb) ++ return -EINVAL; ++ ++ if (enable) { ++ down(&dvb->pll_mutex); ++ status = dvb->gate_ctrl(fe, 1); ++ } else { ++ status = dvb->gate_ctrl(fe, 0); ++ up(&dvb->pll_mutex); ++ } ++ return status; ++} ++ ++static void hauppauge_hvr930c_init(struct em28xx *dev) ++{ ++ int i; ++ ++ struct em28xx_reg_seq hauppauge_hvr930c_init[] = { ++ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0x65}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xfb, 0xff, 0x32}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0xb8}, ++ { -1, -1, -1, -1}, ++ }; ++ struct em28xx_reg_seq hauppauge_hvr930c_end[] = { ++ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x01}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xaf, 0xff, 0x65}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x76}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x01}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xcf, 0xff, 0x0b}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x40}, ++ ++ {EM2874_R80_GPIO_P0_CTRL, 0xcf, 0xff, 0x65}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x65}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xcf, 0xff, 0x0b}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x65}, ++ ++ { -1, -1, -1, -1}, ++ }; ++ ++ struct { ++ unsigned char r[4]; ++ int len; ++ } regs[] = { ++ {{ 0x06, 0x02, 0x00, 0x31 }, 4}, ++ {{ 0x01, 0x02 }, 2}, ++ {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0xff, 0xaf }, 4}, ++ {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0x73, 0xaf }, 4}, ++ {{ 0x04, 0x00 }, 2}, ++ {{ 0x00, 0x04 }, 2}, ++ {{ 0x00, 0x04, 0x00, 0x0a }, 4}, ++ {{ 0x04, 0x14 }, 2}, ++ {{ 0x04, 0x14, 0x00, 0x00 }, 4}, ++ }; ++ ++ em28xx_gpio_set(dev, hauppauge_hvr930c_init); ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); ++ msleep(10); ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); ++ msleep(10); ++ ++ dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; ++ ++ for (i = 0; i < ARRAY_SIZE(regs); i++) ++ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); ++ em28xx_gpio_set(dev, hauppauge_hvr930c_end); ++ ++ msleep(100); ++ ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); ++ msleep(30); ++ ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); ++ msleep(10); ++ ++} ++ ++static void terratec_h5_init(struct em28xx *dev) ++{ ++ int i; ++ struct em28xx_reg_seq terratec_h5_init[] = { ++ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xf2, 0xff, 50}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, ++ { -1, -1, -1, -1}, ++ }; ++ struct em28xx_reg_seq terratec_h5_end[] = { ++ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 50}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, ++ { -1, -1, -1, -1}, ++ }; ++ struct { ++ unsigned char r[4]; ++ int len; ++ } regs[] = { ++ {{ 0x06, 0x02, 0x00, 0x31 }, 4}, ++ {{ 0x01, 0x02 }, 2}, ++ {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0xff, 0xaf }, 4}, ++ {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0x73, 0xaf }, 4}, ++ {{ 0x04, 0x00 }, 2}, ++ {{ 0x00, 0x04 }, 2}, ++ {{ 0x00, 0x04, 0x00, 0x0a }, 4}, ++ {{ 0x04, 0x14 }, 2}, ++ {{ 0x04, 0x14, 0x00, 0x00 }, 4}, ++ }; ++ ++ em28xx_gpio_set(dev, terratec_h5_init); ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); ++ msleep(10); ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45); ++ msleep(10); ++ ++ dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; ++ ++ for (i = 0; i < ARRAY_SIZE(regs); i++) ++ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); ++ em28xx_gpio_set(dev, terratec_h5_end); ++}; ++ ++static void terratec_htc_stick_init(struct em28xx *dev) ++{ ++ int i; ++ ++ /* ++ * GPIO configuration: ++ * 0xff: unknown (does not affect DVB-T). ++ * 0xf6: DRX-K (demodulator). ++ * 0xe6: unknown (does not affect DVB-T). ++ * 0xb6: unknown (does not affect DVB-T). ++ */ ++ struct em28xx_reg_seq terratec_htc_stick_init[] = { ++ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 50}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, ++ { -1, -1, -1, -1}, ++ }; ++ struct em28xx_reg_seq terratec_htc_stick_end[] = { ++ {EM2874_R80_GPIO_P0_CTRL, 0xb6, 0xff, 100}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 50}, ++ { -1, -1, -1, -1}, ++ }; ++ ++ /* ++ * Init the analog decoder (not yet supported), but ++ * it's probably still a good idea. ++ */ ++ struct { ++ unsigned char r[4]; ++ int len; ++ } regs[] = { ++ {{ 0x06, 0x02, 0x00, 0x31 }, 4}, ++ {{ 0x01, 0x02 }, 2}, ++ {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0xff, 0xaf }, 4}, ++ }; ++ ++ em28xx_gpio_set(dev, terratec_htc_stick_init); ++ ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); ++ msleep(10); ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); ++ msleep(10); ++ ++ dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; ++ ++ for (i = 0; i < ARRAY_SIZE(regs); i++) ++ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); ++ ++ em28xx_gpio_set(dev, terratec_htc_stick_end); ++}; ++ ++static void terratec_htc_usb_xs_init(struct em28xx *dev) ++{ ++ int i; ++ ++ struct em28xx_reg_seq terratec_htc_usb_xs_init[] = { ++ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xb2, 0xff, 100}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xb2, 0xff, 50}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xb6, 0xff, 100}, ++ { -1, -1, -1, -1}, ++ }; ++ struct em28xx_reg_seq terratec_htc_usb_xs_end[] = { ++ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 100}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 50}, ++ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, ++ { -1, -1, -1, -1}, ++ }; ++ ++ /* ++ * Init the analog decoder (not yet supported), but ++ * it's probably still a good idea. ++ */ ++ struct { ++ unsigned char r[4]; ++ int len; ++ } regs[] = { ++ {{ 0x06, 0x02, 0x00, 0x31 }, 4}, ++ {{ 0x01, 0x02 }, 2}, ++ {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0xff, 0xaf }, 4}, ++ {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0x73, 0xaf }, 4}, ++ {{ 0x04, 0x00 }, 2}, ++ {{ 0x00, 0x04 }, 2}, ++ {{ 0x00, 0x04, 0x00, 0x0a }, 4}, ++ {{ 0x04, 0x14 }, 2}, ++ {{ 0x04, 0x14, 0x00, 0x00 }, 4}, ++ }; ++ ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); ++ ++ em28xx_gpio_set(dev, terratec_htc_usb_xs_init); ++ ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); ++ msleep(10); ++ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44); ++ msleep(10); ++ ++ dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; ++ ++ for (i = 0; i < ARRAY_SIZE(regs); i++) ++ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); ++ ++ em28xx_gpio_set(dev, terratec_htc_usb_xs_end); ++}; ++ ++static void pctv_520e_init(struct em28xx *dev) ++{ ++ /* ++ * Init AVF4910B analog decoder. Looks like I2C traffic to ++ * digital demodulator and tuner are routed via AVF4910B. ++ */ ++ int i; ++ struct { ++ unsigned char r[4]; ++ int len; ++ } regs[] = { ++ {{ 0x06, 0x02, 0x00, 0x31 }, 4}, ++ {{ 0x01, 0x02 }, 2}, ++ {{ 0x01, 0x02, 0x00, 0xc6 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0xff, 0xaf }, 4}, ++ {{ 0x01, 0x00, 0x03, 0xa0 }, 4}, ++ {{ 0x01, 0x00 }, 2}, ++ {{ 0x01, 0x00, 0x73, 0xaf }, 4}, ++ }; ++ ++ dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; /* 0x41 */ ++ ++ for (i = 0; i < ARRAY_SIZE(regs); i++) ++ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len); ++}; ++ ++static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv; ++ struct em28xx *dev = i2c_bus->dev; ++#ifdef CONFIG_GPIOLIB ++ struct em28xx_dvb *dvb = dev->dvb; ++ int ret; ++ unsigned long flags; ++ ++ if (c->lna == 1) ++ flags = GPIOF_OUT_INIT_HIGH; /* enable LNA */ ++ else ++ flags = GPIOF_OUT_INIT_LOW; /* disable LNA */ ++ ++ ret = gpio_request_one(dvb->lna_gpio, flags, NULL); ++ if (ret) ++ em28xx_errdev("gpio request failed %d\n", ret); ++ else ++ gpio_free(dvb->lna_gpio); ++ ++ return ret; ++#else ++ dev_warn(&dev->udev->dev, "%s: LNA control is disabled (lna=%u)\n", ++ KBUILD_MODNAME, c->lna); ++ return 0; ++#endif ++} ++ ++static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) ++{ ++ /* Values extracted from a USB trace of the Terratec Windows driver */ ++ static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c }; ++ static u8 reset[] = { RESET, 0x80 }; ++ static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; ++ static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 }; ++ static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 }; ++ static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d }; ++ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; ++ static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 }; ++ static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 }; ++ static u8 tuner_go[] = { TUNER_GO, 0x01}; ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(200); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg)); ++ mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg)); ++ mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg)); ++ mt352_write(fe, tuner_go, sizeof(tuner_go)); ++ return 0; ++} ++ ++static struct mt352_config terratec_xs_mt352_cfg = { ++ .demod_address = (0x1e >> 1), ++ .no_tuner = 1, ++ .if2 = 45600, ++ .demod_init = em28xx_mt352_terratec_xs_init, ++}; ++ ++static struct tda10023_config em28xx_tda10023_config = { ++ .demod_address = 0x0c, ++ .invert = 1, ++}; ++ ++static struct cxd2820r_config em28xx_cxd2820r_config = { ++ .i2c_address = (0xd8 >> 1), ++ .ts_mode = CXD2820R_TS_SERIAL, ++}; ++ ++static struct tda18271_config em28xx_cxd2820r_tda18271_config = { ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++ .gate = TDA18271_GATE_DIGITAL, ++}; ++ ++static const struct tda10071_config em28xx_tda10071_config = { ++ .demod_i2c_addr = 0x55, /* (0xaa >> 1) */ ++ .tuner_i2c_addr = 0x14, ++ .i2c_wr_max = 64, ++ .ts_mode = TDA10071_TS_SERIAL, ++ .spec_inv = 0, ++ .xtal = 40444000, /* 40.444 MHz */ ++ .pll_multiplier = 20, ++}; ++ ++static const struct a8293_config em28xx_a8293_config = { ++ .i2c_addr = 0x08, /* (0x10 >> 1) */ ++}; ++ ++static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = { ++ .demod_address = (0x1e >> 1), ++ .disable_i2c_gate_ctrl = 1, ++ .no_tuner = 1, ++ .parallel_ts = 1, ++}; ++static struct qt1010_config em28xx_qt1010_config = { ++ .i2c_address = 0x62 ++}; ++ ++static const struct mb86a20s_config c3tech_duo_mb86a20s_config = { ++ .demod_address = 0x10, ++ .is_serial = true, ++}; ++ ++static struct tda18271_std_map mb86a20s_tda18271_config = { ++ .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++}; ++ ++static struct tda18271_config c3tech_duo_tda18271_config = { ++ .std_map = &mb86a20s_tda18271_config, ++ .gate = TDA18271_GATE_DIGITAL, ++ .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, ++}; ++ ++static const struct m88ds3103_config pctv_461e_m88ds3103_config = { ++ .i2c_addr = 0x68, ++ .clock = 27000000, ++ .i2c_wr_max = 33, ++ .clock_out = 0, ++ .ts_mode = M88DS3103_TS_PARALLEL, ++ .ts_clk = 16000, ++ .ts_clk_pol = 1, ++ .agc = 0x99, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) ++{ ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg; ++ struct xc2028_ctrl ctl; ++ ++ memset(&cfg, 0, sizeof(cfg)); ++ cfg.i2c_adap = &dev->i2c_adap[dev->def_i2c_bus]; ++ cfg.i2c_addr = addr; ++ ++ memset(&ctl, 0, sizeof(ctl)); ++ em28xx_setup_xc3028(dev, &ctl); ++ cfg.ctrl = &ctl; ++ ++ if (!dev->dvb->fe[0]) { ++ em28xx_errdev("/2: dvb frontend not attached. " ++ "Can't attach xc3028\n"); ++ return -EINVAL; ++ } ++ ++ fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg); ++ if (!fe) { ++ em28xx_errdev("/2: xc3028 attach failed\n"); ++ dvb_frontend_detach(dev->dvb->fe[0]); ++ dev->dvb->fe[0] = NULL; ++ return -EINVAL; ++ } ++ ++ em28xx_info("%s/2: xc3028 attached\n", dev->name); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module, ++ struct em28xx *dev, struct device *device) ++{ ++ int result; ++ ++ mutex_init(&dvb->lock); ++ ++ /* register adapter */ ++ result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, ++ adapter_nr); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", ++ dev->name, result); ++ goto fail_adapter; ++ } ++ ++ /* Ensure all frontends negotiate bus access */ ++ dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; ++ if (dvb->fe[1]) ++ dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; ++ ++ dvb->adapter.priv = &dev->i2c_bus[dev->def_i2c_bus]; ++ ++ /* register frontend */ ++ result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", ++ dev->name, result); ++ goto fail_frontend0; ++ } ++ ++ /* register 2nd frontend */ ++ if (dvb->fe[1]) { ++ result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n", ++ dev->name, result); ++ goto fail_frontend1; ++ } ++ } ++ ++ /* register demux stuff */ ++ dvb->demux.dmx.capabilities = ++ DMX_TS_FILTERING | DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING; ++ dvb->demux.priv = dvb; ++ dvb->demux.filternum = 256; ++ dvb->demux.feednum = 256; ++ dvb->demux.start_feed = em28xx_start_feed; ++ dvb->demux.stop_feed = em28xx_stop_feed; ++ ++ result = dvb_dmx_init(&dvb->demux); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", ++ dev->name, result); ++ goto fail_dmx; ++ } ++ ++ dvb->dmxdev.filternum = 256; ++ dvb->dmxdev.demux = &dvb->demux.dmx; ++ dvb->dmxdev.capabilities = 0; ++ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", ++ dev->name, result); ++ goto fail_dmxdev; ++ } ++ ++ dvb->fe_hw.source = DMX_FRONTEND_0; ++ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", ++ dev->name, result); ++ goto fail_fe_hw; ++ } ++ ++ dvb->fe_mem.source = DMX_MEMORY_FE; ++ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", ++ dev->name, result); ++ goto fail_fe_mem; ++ } ++ ++ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++ if (result < 0) { ++ printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", ++ dev->name, result); ++ goto fail_fe_conn; ++ } ++ ++ /* register network adapter */ ++ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); ++ return 0; ++ ++fail_fe_conn: ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); ++fail_fe_mem: ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++fail_fe_hw: ++ dvb_dmxdev_release(&dvb->dmxdev); ++fail_dmxdev: ++ dvb_dmx_release(&dvb->demux); ++fail_dmx: ++ if (dvb->fe[1]) ++ dvb_unregister_frontend(dvb->fe[1]); ++ dvb_unregister_frontend(dvb->fe[0]); ++fail_frontend1: ++ if (dvb->fe[1]) ++ dvb_frontend_detach(dvb->fe[1]); ++fail_frontend0: ++ dvb_frontend_detach(dvb->fe[0]); ++ dvb_unregister_adapter(&dvb->adapter); ++fail_adapter: ++ return result; ++} ++ ++static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) ++{ ++ dvb_net_release(&dvb->net); ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++ dvb_dmxdev_release(&dvb->dmxdev); ++ dvb_dmx_release(&dvb->demux); ++ if (dvb->fe[1]) ++ dvb_unregister_frontend(dvb->fe[1]); ++ dvb_unregister_frontend(dvb->fe[0]); ++ if (dvb->fe[1] && !dvb->dont_attach_fe1) ++ dvb_frontend_detach(dvb->fe[1]); ++ dvb_frontend_detach(dvb->fe[0]); ++ dvb_unregister_adapter(&dvb->adapter); ++} ++ ++static int em28xx_dvb_init(struct em28xx *dev) ++{ ++ int result = 0, mfe_shared = 0; ++ struct em28xx_dvb *dvb; ++ ++ if (dev->is_audio_only) { ++ /* Shouldn't initialize IR for this interface */ ++ return 0; ++ } ++ ++ if (!dev->board.has_dvb) { ++ /* This device does not support the extension */ ++ return 0; ++ } ++ ++ em28xx_info("Binding DVB extension\n"); ++ ++ dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); ++ ++ if (dvb == NULL) { ++ em28xx_info("em28xx_dvb: memory allocation failed\n"); ++ return -ENOMEM; ++ } ++ dev->dvb = dvb; ++ dvb->fe[0] = dvb->fe[1] = NULL; ++ ++ /* pre-allocate DVB usb transfer buffers */ ++ if (dev->dvb_xfer_bulk) { ++ result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, ++ dev->dvb_xfer_bulk, ++ EM28XX_DVB_NUM_BUFS, ++ 512, ++ EM28XX_DVB_BULK_PACKET_MULTIPLIER); ++ } else { ++ result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, ++ dev->dvb_xfer_bulk, ++ EM28XX_DVB_NUM_BUFS, ++ dev->dvb_max_pkt_size_isoc, ++ EM28XX_DVB_NUM_ISOC_PACKETS); ++ } ++ if (result) { ++ em28xx_errdev("em28xx_dvb: failed to pre-allocate USB transfer buffers for DVB.\n"); ++ kfree(dvb); ++ dev->dvb = NULL; ++ return result; ++ } ++ ++ mutex_lock(&dev->lock); ++ em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); ++ /* init frontend */ ++ switch (dev->model) { ++ case EM2874_BOARD_LEADERSHIP_ISDBT: ++ dvb->fe[0] = dvb_attach(s921_attach, ++ &sharp_isdbt, &dev->i2c_adap[dev->def_i2c_bus]); ++ ++ if (!dvb->fe[0]) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ ++ break; ++ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: ++ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: ++ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: ++ case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: ++ dvb->fe[0] = dvb_attach(lgdt330x_attach, ++ &em2880_lgdt3303_dev, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (em28xx_attach_xc3028(0x61, dev) < 0) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2880_BOARD_KWORLD_DVB_310U: ++ dvb->fe[0] = dvb_attach(zl10353_attach, ++ &em28xx_zl10353_with_xc3028, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (em28xx_attach_xc3028(0x61, dev) < 0) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: ++ case EM2882_BOARD_TERRATEC_HYBRID_XS: ++ case EM2880_BOARD_EMPIRE_DUAL_TV: ++ dvb->fe[0] = dvb_attach(zl10353_attach, ++ &em28xx_zl10353_xc3028_no_i2c_gate, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (em28xx_attach_xc3028(0x61, dev) < 0) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2880_BOARD_TERRATEC_HYBRID_XS: ++ case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: ++ case EM2881_BOARD_PINNACLE_HYBRID_PRO: ++ case EM2882_BOARD_DIKOM_DK300: ++ case EM2882_BOARD_KWORLD_VS_DVBT: ++ dvb->fe[0] = dvb_attach(zl10353_attach, ++ &em28xx_zl10353_xc3028_no_i2c_gate, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (dvb->fe[0] == NULL) { ++ /* This board could have either a zl10353 or a mt352. ++ If the chip id isn't for zl10353, try mt352 */ ++ dvb->fe[0] = dvb_attach(mt352_attach, ++ &terratec_xs_mt352_cfg, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ } ++ ++ if (em28xx_attach_xc3028(0x61, dev) < 0) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2870_BOARD_KWORLD_355U: ++ dvb->fe[0] = dvb_attach(zl10353_attach, ++ &em28xx_zl10353_no_i2c_gate_dev, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (dvb->fe[0] != NULL) ++ dvb_attach(qt1010_attach, dvb->fe[0], ++ &dev->i2c_adap[dev->def_i2c_bus], &em28xx_qt1010_config); ++ break; ++ case EM2883_BOARD_KWORLD_HYBRID_330U: ++ case EM2882_BOARD_EVGA_INDTUBE: ++ dvb->fe[0] = dvb_attach(s5h1409_attach, ++ &em28xx_s5h1409_with_xc3028, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (em28xx_attach_xc3028(0x61, dev) < 0) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2882_BOARD_KWORLD_ATSC_315U: ++ dvb->fe[0] = dvb_attach(lgdt330x_attach, ++ &em2880_lgdt3303_dev, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (dvb->fe[0] != NULL) { ++ if (!dvb_attach(simple_tuner_attach, dvb->fe[0], ++ &dev->i2c_adap[dev->def_i2c_bus], 0x61, TUNER_THOMSON_DTT761X)) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ } ++ break; ++ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: ++ case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E: ++ dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL, ++ &dev->i2c_adap[dev->def_i2c_bus], &dev->udev->dev); ++ if (em28xx_attach_xc3028(0x61, dev) < 0) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2870_BOARD_REDDO_DVB_C_USB_BOX: ++ /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ ++ dvb->fe[0] = dvb_attach(tda10023_attach, ++ &em28xx_tda10023_config, ++ &dev->i2c_adap[dev->def_i2c_bus], 0x48); ++ if (dvb->fe[0]) { ++ if (!dvb_attach(simple_tuner_attach, dvb->fe[0], ++ &dev->i2c_adap[dev->def_i2c_bus], 0x60, TUNER_PHILIPS_CU1216L)) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ } ++ break; ++ case EM2870_BOARD_KWORLD_A340: ++ dvb->fe[0] = dvb_attach(lgdt3305_attach, ++ &em2870_lgdt3304_dev, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (dvb->fe[0] != NULL) ++ dvb_attach(tda18271_attach, dvb->fe[0], 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], &kworld_a340_config); ++ break; ++ case EM28174_BOARD_PCTV_290E: ++ /* set default GPIO0 for LNA, used if GPIOLIB is undefined */ ++ dvb->lna_gpio = CXD2820R_GPIO_E | CXD2820R_GPIO_O | ++ CXD2820R_GPIO_L; ++ dvb->fe[0] = dvb_attach(cxd2820r_attach, ++ &em28xx_cxd2820r_config, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &dvb->lna_gpio); ++ if (dvb->fe[0]) { ++ /* FE 0 attach tuner */ ++ if (!dvb_attach(tda18271_attach, ++ dvb->fe[0], ++ 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &em28xx_cxd2820r_tda18271_config)) { ++ ++ dvb_frontend_detach(dvb->fe[0]); ++ result = -EINVAL; ++ goto out_free; ++ } ++ ++#ifdef CONFIG_GPIOLIB ++ /* enable LNA for DVB-T, DVB-T2 and DVB-C */ ++ result = gpio_request_one(dvb->lna_gpio, ++ GPIOF_OUT_INIT_LOW, NULL); ++ if (result) ++ em28xx_errdev("gpio request failed %d\n", ++ result); ++ else ++ gpio_free(dvb->lna_gpio); ++ ++ result = 0; /* continue even set LNA fails */ ++#endif ++ dvb->fe[0]->ops.set_lna = em28xx_pctv_290e_set_lna; ++ } ++ ++ break; ++ case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: ++ { ++ struct xc5000_config cfg; ++ hauppauge_hvr930c_init(dev); ++ ++ dvb->fe[0] = dvb_attach(drxk_attach, ++ &hauppauge_930c_drxk, &dev->i2c_adap[dev->def_i2c_bus]); ++ if (!dvb->fe[0]) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ /* FIXME: do we need a pll semaphore? */ ++ dvb->fe[0]->sec_priv = dvb; ++ sema_init(&dvb->pll_mutex, 1); ++ dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl; ++ dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; ++ ++ /* Attach xc5000 */ ++ memset(&cfg, 0, sizeof(cfg)); ++ cfg.i2c_address = 0x61; ++ cfg.if_khz = 4000; ++ ++ if (dvb->fe[0]->ops.i2c_gate_ctrl) ++ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); ++ if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], ++ &cfg)) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ if (dvb->fe[0]->ops.i2c_gate_ctrl) ++ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); ++ ++ break; ++ } ++ case EM2884_BOARD_TERRATEC_H5: ++ terratec_h5_init(dev); ++ ++ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap[dev->def_i2c_bus]); ++ if (!dvb->fe[0]) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ /* FIXME: do we need a pll semaphore? */ ++ dvb->fe[0]->sec_priv = dvb; ++ sema_init(&dvb->pll_mutex, 1); ++ dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl; ++ dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; ++ ++ /* Attach tda18271 to DVB-C frontend */ ++ if (dvb->fe[0]->ops.i2c_gate_ctrl) ++ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1); ++ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], 0x60)) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ if (dvb->fe[0]->ops.i2c_gate_ctrl) ++ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); ++ ++ break; ++ case EM2884_BOARD_C3TECH_DIGITAL_DUO: ++ dvb->fe[0] = dvb_attach(mb86a20s_attach, ++ &c3tech_duo_mb86a20s_config, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (dvb->fe[0] != NULL) ++ dvb_attach(tda18271_attach, dvb->fe[0], 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &c3tech_duo_tda18271_config); ++ break; ++ case EM28174_BOARD_PCTV_460E: ++ /* attach demod */ ++ dvb->fe[0] = dvb_attach(tda10071_attach, ++ &em28xx_tda10071_config, &dev->i2c_adap[dev->def_i2c_bus]); ++ ++ /* attach SEC */ ++ if (dvb->fe[0]) ++ dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], ++ &em28xx_a8293_config); ++ break; ++ case EM2874_BOARD_DELOCK_61959: ++ case EM2874_BOARD_MAXMEDIA_UB425_TC: ++ /* attach demodulator */ ++ dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ ++ if (dvb->fe[0]) { ++ /* disable I2C-gate */ ++ dvb->fe[0]->ops.i2c_gate_ctrl = NULL; ++ ++ /* attach tuner */ ++ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &em28xx_cxd2820r_tda18271_config)) { ++ dvb_frontend_detach(dvb->fe[0]); ++ result = -EINVAL; ++ goto out_free; ++ } ++ } ++ break; ++ case EM2884_BOARD_PCTV_510E: ++ case EM2884_BOARD_PCTV_520E: ++ pctv_520e_init(dev); ++ ++ /* attach demodulator */ ++ dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ ++ if (dvb->fe[0]) { ++ /* attach tuner */ ++ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &em28xx_cxd2820r_tda18271_config)) { ++ dvb_frontend_detach(dvb->fe[0]); ++ result = -EINVAL; ++ goto out_free; ++ } ++ } ++ break; ++ case EM2884_BOARD_CINERGY_HTC_STICK: ++ terratec_htc_stick_init(dev); ++ ++ /* attach demodulator */ ++ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (!dvb->fe[0]) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ ++ /* Attach the demodulator. */ ++ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &em28xx_cxd2820r_tda18271_config)) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2884_BOARD_TERRATEC_HTC_USB_XS: ++ terratec_htc_usb_xs_init(dev); ++ ++ /* attach demodulator */ ++ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (!dvb->fe[0]) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ ++ /* Attach the demodulator. */ ++ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &em28xx_cxd2820r_tda18271_config)) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM2874_BOARD_KWORLD_UB435Q_V2: ++ dvb->fe[0] = dvb_attach(lgdt3305_attach, ++ &em2874_lgdt3305_dev, ++ &dev->i2c_adap[dev->def_i2c_bus]); ++ if (!dvb->fe[0]) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ ++ /* Attach the demodulator. */ ++ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &kworld_ub435q_v2_config)) { ++ result = -EINVAL; ++ goto out_free; ++ } ++ break; ++ case EM28178_BOARD_PCTV_461E: ++ { ++ /* demod I2C adapter */ ++ struct i2c_adapter *i2c_adapter; ++ struct i2c_board_info info; ++ struct m88ts2022_config m88ts2022_config = { ++ .clock = 27000000, ++ }; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ ++ /* attach demod */ ++ dvb->fe[0] = dvb_attach(m88ds3103_attach, ++ &pctv_461e_m88ds3103_config, ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &i2c_adapter); ++ if (dvb->fe[0] == NULL) { ++ result = -ENODEV; ++ goto out_free; ++ } ++ ++ /* attach tuner */ ++ m88ts2022_config.fe = dvb->fe[0]; ++ strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &m88ts2022_config; ++ request_module("m88ts2022"); ++ dvb->i2c_client_tuner = i2c_new_device(i2c_adapter, &info); ++ ++ /* delegate signal strength measurement to tuner */ ++ dvb->fe[0]->ops.read_signal_strength = ++ dvb->fe[0]->ops.tuner_ops.get_rf_strength; ++ ++ /* attach SEC */ ++ if (!dvb_attach(a8293_attach, dvb->fe[0], ++ &dev->i2c_adap[dev->def_i2c_bus], ++ &em28xx_a8293_config)) { ++ dvb_frontend_detach(dvb->fe[0]); ++ result = -ENODEV; ++ goto out_free; ++ } ++ } ++ break; ++ default: ++ em28xx_errdev("/2: The frontend of your DVB/ATSC card" ++ " isn't supported yet\n"); ++ break; ++ } ++ if (NULL == dvb->fe[0]) { ++ em28xx_errdev("/2: frontend initialization failed\n"); ++ result = -EINVAL; ++ goto out_free; ++ } ++ /* define general-purpose callback pointer */ ++ dvb->fe[0]->callback = em28xx_tuner_callback; ++ if (dvb->fe[1]) ++ dvb->fe[1]->callback = em28xx_tuner_callback; ++ ++ /* register everything */ ++ result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); ++ ++ if (result < 0) ++ goto out_free; ++ ++ /* MFE lock */ ++ dvb->adapter.mfe_shared = mfe_shared; ++ ++ em28xx_info("DVB extension successfully initialized\n"); ++ret: ++ em28xx_set_mode(dev, EM28XX_SUSPEND); ++ mutex_unlock(&dev->lock); ++ return result; ++ ++out_free: ++ kfree(dvb); ++ dev->dvb = NULL; ++ goto ret; ++} ++ ++static inline void prevent_sleep(struct dvb_frontend_ops *ops) ++{ ++ ops->set_voltage = NULL; ++ ops->sleep = NULL; ++ ops->tuner_ops.sleep = NULL; ++} ++ ++static int em28xx_dvb_fini(struct em28xx *dev) ++{ ++ if (dev->is_audio_only) { ++ /* Shouldn't initialize IR for this interface */ ++ return 0; ++ } ++ ++ if (!dev->board.has_dvb) { ++ /* This device does not support the extension */ ++ return 0; ++ } ++ ++ em28xx_info("Closing DVB extension\n"); ++ ++ if (dev->dvb) { ++ struct em28xx_dvb *dvb = dev->dvb; ++ ++ em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); ++ ++ if (dev->disconnected) { ++ /* We cannot tell the device to sleep ++ * once it has been unplugged. */ ++ if (dvb->fe[0]) ++ prevent_sleep(&dvb->fe[0]->ops); ++ if (dvb->fe[1]) ++ prevent_sleep(&dvb->fe[1]->ops); ++ } ++ ++ i2c_release_client(dvb->i2c_client_tuner); ++ em28xx_unregister_dvb(dvb); ++ kfree(dvb); ++ dev->dvb = NULL; ++ } ++ ++ return 0; ++} ++ ++static struct em28xx_ops dvb_ops = { ++ .id = EM28XX_DVB, ++ .name = "Em28xx dvb Extension", ++ .init = em28xx_dvb_init, ++ .fini = em28xx_dvb_fini, ++}; ++ ++static int __init em28xx_dvb_register(void) ++{ ++ return em28xx_register_extension(&dvb_ops); ++} ++ ++static void __exit em28xx_dvb_unregister(void) ++{ ++ em28xx_unregister_extension(&dvb_ops); ++} ++ ++module_init(em28xx_dvb_register); ++module_exit(em28xx_dvb_unregister); +diff -Nur linux-3.14.36/drivers/media/usb/em28xx/em28xx.h linux-openelec/drivers/media/usb/em28xx/em28xx.h +--- linux-3.14.36/drivers/media/usb/em28xx/em28xx.h 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/em28xx/em28xx.h 2015-07-24 18:03:30.156842002 -0500 +@@ -137,6 +137,7 @@ + #define EM2874_BOARD_KWORLD_UB435Q_V2 90 + #define EM2765_BOARD_SPEEDLINK_VAD_LAPLACE 91 + #define EM28178_BOARD_PCTV_461E 92 ++#define EM28178_BOARD_PCTV_292E 94 + + /* Limits minimum and default number of buffers */ + #define EM28XX_MIN_BUF 4 +diff -Nur linux-3.14.36/drivers/media/usb/em28xx/Kconfig linux-openelec/drivers/media/usb/em28xx/Kconfig +--- linux-3.14.36/drivers/media/usb/em28xx/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/usb/em28xx/Kconfig 2015-07-24 18:03:30.156842002 -0500 +@@ -55,6 +55,8 @@ + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + ---help--- + This adds support for DVB cards based on the + Empiatech em28xx chips. +diff -Nur linux-3.14.36/drivers/media/v4l2-core/videobuf2-dma-contig.c linux-openelec/drivers/media/v4l2-core/videobuf2-dma-contig.c +--- linux-3.14.36/drivers/media/v4l2-core/videobuf2-dma-contig.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/v4l2-core/videobuf2-dma-contig.c 2015-05-06 12:05:42.000000000 -0500 +@@ -719,7 +719,7 @@ + + /* get the associated scatterlist for this buffer */ + sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir); +- if (IS_ERR_OR_NULL(sgt)) { ++ if (IS_ERR(sgt)) { + pr_err("Error getting dmabuf scatterlist\n"); + return -EINVAL; + } +diff -Nur linux-3.14.36/drivers/media/v4l2-core/videobuf-dma-contig.c linux-openelec/drivers/media/v4l2-core/videobuf-dma-contig.c +--- linux-3.14.36/drivers/media/v4l2-core/videobuf-dma-contig.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/media/v4l2-core/videobuf-dma-contig.c 2015-05-06 12:05:42.000000000 -0500 +@@ -304,7 +304,7 @@ + + /* Try to remap memory */ + size = vma->vm_end - vma->vm_start; +- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + retval = vm_iomap_memory(vma, vma->vm_start, size); + if (retval) { + dev_err(q->dev, "mmap: remap failed with error %d. ", +diff -Nur linux-3.14.36/drivers/mfd/ab8500-core.c linux-openelec/drivers/mfd/ab8500-core.c +--- linux-3.14.36/drivers/mfd/ab8500-core.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/mfd/ab8500-core.c 2015-05-06 12:05:42.000000000 -0500 +@@ -592,7 +592,7 @@ + + /* If ->irq_base is zero this will give a linear mapping */ + ab8500->domain = irq_domain_add_simple(NULL, +- num_irqs, ab8500->irq_base, ++ num_irqs, 0, + &ab8500_irq_ops, ab8500); + + if (!ab8500->domain) { +@@ -1583,14 +1583,13 @@ + if (!ab8500) + return -ENOMEM; + +- if (plat) +- ab8500->irq_base = plat->irq_base; +- + ab8500->dev = &pdev->dev; + + resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +- if (!resource) ++ if (!resource) { ++ dev_err(&pdev->dev, "no IRQ resource\n"); + return -ENODEV; ++ } + + ab8500->irq = resource->start; + +@@ -1612,8 +1611,10 @@ + else { + ret = get_register_interruptible(ab8500, AB8500_MISC, + AB8500_IC_NAME_REG, &value); +- if (ret < 0) ++ if (ret < 0) { ++ dev_err(&pdev->dev, "could not probe HW\n"); + return ret; ++ } + + ab8500->version = value; + } +@@ -1759,30 +1760,30 @@ + if (is_ab9540(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, + ARRAY_SIZE(ab9540_devs), NULL, +- ab8500->irq_base, ab8500->domain); ++ 0, ab8500->domain); + else if (is_ab8540(ab8500)) { + ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs, + ARRAY_SIZE(ab8540_devs), NULL, +- ab8500->irq_base, NULL); ++ 0, ab8500->domain); + if (ret) + return ret; + + if (is_ab8540_1p2_or_earlier(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut1_devs, + ARRAY_SIZE(ab8540_cut1_devs), NULL, +- ab8500->irq_base, NULL); ++ 0, ab8500->domain); + else /* ab8540 >= cut2 */ + ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut2_devs, + ARRAY_SIZE(ab8540_cut2_devs), NULL, +- ab8500->irq_base, NULL); ++ 0, ab8500->domain); + } else if (is_ab8505(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs, + ARRAY_SIZE(ab8505_devs), NULL, +- ab8500->irq_base, ab8500->domain); ++ 0, ab8500->domain); + else + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, + ARRAY_SIZE(ab8500_devs), NULL, +- ab8500->irq_base, ab8500->domain); ++ 0, ab8500->domain); + if (ret) + return ret; + +@@ -1790,7 +1791,7 @@ + /* Add battery management devices */ + ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs, + ARRAY_SIZE(ab8500_bm_devs), NULL, +- ab8500->irq_base, ab8500->domain); ++ 0, ab8500->domain); + if (ret) + dev_err(ab8500->dev, "error adding bm devices\n"); + } +diff -Nur linux-3.14.36/drivers/mfd/db8500-prcmu.c linux-openelec/drivers/mfd/db8500-prcmu.c +--- linux-3.14.36/drivers/mfd/db8500-prcmu.c 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/mfd/db8500-prcmu.c 2015-05-06 12:05:42.000000000 -0500 +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2678,16 +2679,12 @@ + .xlate = irq_domain_xlate_twocell, + }; + +-static int db8500_irq_init(struct device_node *np, int irq_base) ++static int db8500_irq_init(struct device_node *np) + { + int i; + +- /* In the device tree case, just take some IRQs */ +- if (np) +- irq_base = 0; +- + db8500_irq_domain = irq_domain_add_simple( +- np, NUM_PRCMU_WAKEUPS, irq_base, ++ np, NUM_PRCMU_WAKEUPS, 0, + &db8500_irq_ops, NULL); + + if (!db8500_irq_domain) { +@@ -3114,10 +3111,10 @@ + } + + static int db8500_prcmu_register_ab8500(struct device *parent, +- struct ab8500_platform_data *pdata, +- int irq) ++ struct ab8500_platform_data *pdata) + { +- struct resource ab8500_resource = DEFINE_RES_IRQ(irq); ++ struct device_node *np; ++ struct resource ab8500_resource; + struct mfd_cell ab8500_cell = { + .name = "ab8500-core", + .of_compatible = "stericsson,ab8500", +@@ -3128,6 +3125,20 @@ + .num_resources = 1, + }; + ++ if (!parent->of_node) ++ return -ENODEV; ++ ++ /* Look up the device node, sneak the IRQ out of it */ ++ for_each_child_of_node(parent->of_node, np) { ++ if (of_device_is_compatible(np, ab8500_cell.of_compatible)) ++ break; ++ } ++ if (!np) { ++ dev_info(parent, "could not find AB8500 node in the device tree\n"); ++ return -ENODEV; ++ } ++ of_irq_to_resource_table(np, &ab8500_resource, 1); ++ + return mfd_add_devices(parent, 0, &ab8500_cell, 1, NULL, 0, NULL); + } + +@@ -3180,7 +3191,7 @@ + goto no_irq_return; + } + +- db8500_irq_init(np, pdata->irq_base); ++ db8500_irq_init(np); + + prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); + +@@ -3205,8 +3216,7 @@ + } + } + +- err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata, +- pdata->ab_irq); ++ err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata); + if (err) { + mfd_remove_devices(&pdev->dev); + pr_err("prcmu: Failed to add ab8500 subdevice\n"); +diff -Nur linux-3.14.36/drivers/mfd/Kconfig linux-openelec/drivers/mfd/Kconfig +--- linux-3.14.36/drivers/mfd/Kconfig 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/mfd/Kconfig 2015-05-06 12:05:42.000000000 -0500 +@@ -163,6 +163,14 @@ + Additional drivers must be enabled in order to use the functionality + of the device. + ++config MFD_MXC_HDMI ++ tristate "Freescale HDMI Core" ++ select MFD_CORE ++ help ++ This is the core driver for the Freescale i.MX6 on-chip HDMI. ++ This MFD driver connects with the video and audio drivers for HDMI. ++ ++ + config MFD_MC13XXX + tristate + depends on (SPI_MASTER || I2C) +@@ -1226,3 +1234,4 @@ + help + Platform configuration infrastructure for the ARM Ltd. + Versatile Express. ++ +diff -Nur linux-3.14.36/drivers/mfd/Makefile linux-openelec/drivers/mfd/Makefile +--- linux-3.14.36/drivers/mfd/Makefile 2015-03-18 07:31:43.000000000 -0500 ++++ linux-openelec/drivers/mfd/Makefile 2015-05-06 12:05:42.000000000 -0500 +@@ -166,3 +166,4 @@ + obj-$(CONFIG_MFD_AS3711) += as3711.o + obj-$(CONFIG_MFD_AS3722) += as3722.o + obj-$(CONFIG_MFD_STW481X) += stw481x.o ++obj-$(CONFIG_MFD_MXC_HDMI) += mxc-hdmi-core.o +diff -Nur linux-3.14.36/drivers/mfd/mxc-hdmi-core.c linux-openelec/drivers/mfd/mxc-hdmi-core.c +--- linux-3.14.36/drivers/mfd/mxc-hdmi-core.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-openelec/drivers/mfd/mxc-hdmi-core.c 2015-05-06 12:05:42.000000000 -0500 +@@ -0,0 +1,798 @@ ++/* ++ * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include