diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2014-06-10 20:59:03 +0200 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2014-06-10 20:59:22 +0200 |
commit | 0b9f665205c36306f5433de78df3aebf08b53082 (patch) | |
tree | 63351b914665dbfedabf8379e952e80c4d59cf1c /target/arm/cubox-i/patches/3.15 | |
parent | 8c22e3f5cd0880af766fc88fa79d718caebd1d7c (diff) |
update to 3.15, rb532 pci patch no longer required
Diffstat (limited to 'target/arm/cubox-i/patches/3.15')
-rw-r--r-- | target/arm/cubox-i/patches/3.15/rmk.patch | 6640 | ||||
-rw-r--r-- | target/arm/cubox-i/patches/3.15/sdma-firmware-cubox-i.patch | 131 |
2 files changed, 6771 insertions, 0 deletions
diff --git a/target/arm/cubox-i/patches/3.15/rmk.patch b/target/arm/cubox-i/patches/3.15/rmk.patch new file mode 100644 index 000000000..7ccbf3b4a --- /dev/null +++ b/target/arm/cubox-i/patches/3.15/rmk.patch @@ -0,0 +1,6640 @@ + .../DocBook/media/v4l/pixfmt-packed-rgb.xml | 39 ++ + .../devicetree/bindings/leds/leds-pwm.txt | 2 + + Documentation/devicetree/bindings/mmc/mmc.txt | 11 + + .../bindings/staging/imx-drm/fsl-imx-drm.txt | 3 +- + arch/arm/boot/dts/imx6dl-hummingboard.dts | 27 + + arch/arm/boot/dts/imx6q-cubox-i.dts | 4 + + arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 78 ++- + arch/arm/boot/dts/imx6qdl-microsom.dtsi | 98 +++ + arch/arm/configs/imx_v6_v7_defconfig | 1 + + arch/arm/mach-imx/clk-imx6q.c | 12 + + arch/arm/mach-imx/clk-pllv3.c | 27 +- + drivers/Kconfig | 2 + + drivers/Makefile | 1 + + drivers/ata/ahci_imx.c | 184 +++++- + drivers/cec/Kconfig | 14 + + drivers/cec/Makefile | 1 + + drivers/cec/cec-dev.c | 384 +++++++++++ + drivers/gpu/drm/drm_crtc_helper.c | 6 - + drivers/leds/leds-pwm.c | 144 ++-- + drivers/mmc/core/core.c | 42 ++ + drivers/mmc/core/host.c | 68 ++ + drivers/mmc/core/sdio_irq.c | 41 +- + drivers/mmc/host/Kconfig | 63 +- + drivers/mmc/host/dw_mmc.c | 2 + + drivers/mmc/host/sdhci-acpi.c | 8 + + drivers/mmc/host/sdhci-bcm-kona.c | 4 + + drivers/mmc/host/sdhci-bcm2835.c | 4 + + drivers/mmc/host/sdhci-cns3xxx.c | 13 +- + drivers/mmc/host/sdhci-dove.c | 4 + + drivers/mmc/host/sdhci-esdhc-imx.c | 82 +-- + drivers/mmc/host/sdhci-esdhc.h | 4 +- + drivers/mmc/host/sdhci-of-arasan.c | 4 + + drivers/mmc/host/sdhci-of-esdhc.c | 70 +- + drivers/mmc/host/sdhci-of-hlwd.c | 4 + + drivers/mmc/host/sdhci-pci.c | 9 +- + drivers/mmc/host/sdhci-pltfm.c | 4 + + drivers/mmc/host/sdhci-pxav2.c | 14 +- + drivers/mmc/host/sdhci-pxav3.c | 13 +- + drivers/mmc/host/sdhci-s3c.c | 36 +- + drivers/mmc/host/sdhci-sirf.c | 4 + + drivers/mmc/host/sdhci-spear.c | 5 +- + drivers/mmc/host/sdhci-tegra.c | 27 +- + drivers/mmc/host/sdhci.c | 722 ++++++++++----------- + drivers/mmc/host/sdhci.h | 20 +- + drivers/regulator/anatop-regulator.c | 1 + + drivers/regulator/core.c | 2 +- + drivers/regulator/dummy.c | 1 + + drivers/regulator/fixed.c | 4 +- + drivers/staging/imx-drm/Kconfig | 18 + + drivers/staging/imx-drm/Makefile | 3 + + drivers/staging/imx-drm/drm-ddc-connector.c | 92 +++ + drivers/staging/imx-drm/drm-ddc-connector.h | 26 + + drivers/staging/imx-drm/dw-hdmi-audio.c | 652 +++++++++++++++++++ + drivers/staging/imx-drm/dw-hdmi-audio.h | 15 + + drivers/staging/imx-drm/dw-hdmi-cec.c | 205 ++++++ + drivers/staging/imx-drm/dw-hdmi-cec.h | 16 + + drivers/staging/imx-drm/imx-hdmi.c | 178 ++--- + drivers/staging/imx-drm/imx-ldb.c | 21 + + drivers/staging/imx-drm/imx-tve.c | 63 +- + drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h | 5 + + drivers/staging/imx-drm/ipu-v3/ipu-common.c | 41 +- + drivers/staging/imx-drm/ipu-v3/ipu-dc.c | 94 ++- + drivers/staging/imx-drm/ipu-v3/ipu-di.c | 2 +- + drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 25 +- + drivers/staging/imx-drm/ipu-v3/ipu-dp.c | 71 +- + drivers/staging/imx-drm/ipu-v3/ipu-prv.h | 3 + + drivers/staging/imx-drm/ipuv3-crtc.c | 16 +- + drivers/staging/imx-drm/ipuv3-plane.c | 4 + + drivers/staging/imx-drm/parallel-display.c | 2 + + include/linux/cec-dev.h | 69 ++ + include/linux/mmc/host.h | 8 + + include/linux/mmc/sdhci.h | 15 +- + include/uapi/linux/cec-dev.h | 34 + + include/uapi/linux/videodev2.h | 1 + + sound/soc/fsl/imx-pcm-dma.c | 2 +- + 75 files changed, 3162 insertions(+), 832 deletions(-) + +diff --git a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml +index e1c4f8b4c0b3..88a7fe1ecaf1 100644 +--- a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml ++++ b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml +@@ -279,6 +279,45 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para> + <entry></entry> + <entry></entry> + </row> ++ <row id="V4L2-PIX-FMT-RGB666"> ++ <entry><constant>V4L2_PIX_FMT_RGB666</constant></entry> ++ <entry>'RGBH'</entry> ++ <entry></entry> ++ <entry>r<subscript>5</subscript></entry> ++ <entry>r<subscript>4</subscript></entry> ++ <entry>r<subscript>3</subscript></entry> ++ <entry>r<subscript>2</subscript></entry> ++ <entry>r<subscript>1</subscript></entry> ++ <entry>r<subscript>0</subscript></entry> ++ <entry>g<subscript>5</subscript></entry> ++ <entry>g<subscript>4</subscript></entry> ++ <entry></entry> ++ <entry>g<subscript>3</subscript></entry> ++ <entry>g<subscript>2</subscript></entry> ++ <entry>g<subscript>1</subscript></entry> ++ <entry>g<subscript>0</subscript></entry> ++ <entry>b<subscript>5</subscript></entry> ++ <entry>b<subscript>4</subscript></entry> ++ <entry>b<subscript>3</subscript></entry> ++ <entry>b<subscript>2</subscript></entry> ++ <entry></entry> ++ <entry>b<subscript>1</subscript></entry> ++ <entry>b<subscript>0</subscript></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ <entry></entry> ++ </row> + <row id="V4L2-PIX-FMT-BGR24"> + <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry> + <entry>'BGR3'</entry> +diff --git a/Documentation/devicetree/bindings/leds/leds-pwm.txt b/Documentation/devicetree/bindings/leds/leds-pwm.txt +index 7297107cf832..6c6583c35f2f 100644 +--- a/Documentation/devicetree/bindings/leds/leds-pwm.txt ++++ b/Documentation/devicetree/bindings/leds/leds-pwm.txt +@@ -13,6 +13,8 @@ node's name represents the name of the corresponding LED. + 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 --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt +index 9dce540771fb..b9b534ebc0c5 100644 +--- a/Documentation/devicetree/bindings/mmc/mmc.txt ++++ b/Documentation/devicetree/bindings/mmc/mmc.txt +@@ -5,6 +5,8 @@ these definitions. + Interpreted by the OF core: + - reg: Registers location and length. + - interrupts: Interrupts used by the MMC controller. ++- clocks: Clocks needed for the host controller, if any. ++- clock-names: Goes with clocks above. + + Card detection: + If no property below is supplied, host native card detect is used. +@@ -39,6 +41,15 @@ If no property below is supplied, host native card detect is used. + - mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported + - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported + ++Card power and reset control: ++The following properties can be specified for cases where the MMC ++peripheral needs additional reset, regulator and clock lines. It is for ++example common for WiFi/BT adapters to have these separate from the main ++MMC bus: ++ - card-reset-gpios: Specify GPIOs for card reset (reset active low) ++ - card-external-vcc-supply: Regulator to drive (independent) card VCC ++ - clock with name "card_ext_clock": External clock provided to the card ++ + *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line + polarity properties, we have to fix the meaning of the "normal" and "inverted" + line levels. We choose to follow the SDHCI standard, which specifies both those +diff --git a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt +index 3be5ce7a9654..83137ef5a1ba 100644 +--- a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt ++++ b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt +@@ -60,7 +60,8 @@ Parallel display support + - compatible: Should be "fsl,imx-parallel-display" + Optional properties: + - interface_pix_fmt: How this display is connected to the +- display interface. Currently supported types: "rgb24", "rgb565", "bgr666" ++ display interface. Currently supported types: "rgb24", "rgb565", "bgr666", ++ "rgb666" + - edid: verbatim EDID data block describing attached display. + - ddc: phandle describing the i2c bus handling the display data + channel +diff --git a/arch/arm/boot/dts/imx6dl-hummingboard.dts b/arch/arm/boot/dts/imx6dl-hummingboard.dts +index 5bfae54fb780..5cfab68fe43d 100644 +--- a/arch/arm/boot/dts/imx6dl-hummingboard.dts ++++ b/arch/arm/boot/dts/imx6dl-hummingboard.dts +@@ -67,6 +67,13 @@ + status = "okay"; + }; + ++&hdmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_hdmi>; ++ ddc-i2c-bus = <&i2c2>; ++ status = "okay"; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_i2c1>; +@@ -82,6 +89,13 @@ + */ + }; + ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_i2c2>; ++ status = "okay"; ++}; ++ + &iomuxc { + hummingboard { + pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 { +@@ -97,6 +111,12 @@ + >; + }; + ++ 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 +@@ -104,6 +124,13 @@ + >; + }; + ++ pinctrl_hummingboard_i2c2: hummingboard-i2c2 { ++ fsl,pins = < ++ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 ++ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 ++ >; ++ }; ++ + pinctrl_hummingboard_spdif: hummingboard-spdif { + fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>; + }; +diff --git a/arch/arm/boot/dts/imx6q-cubox-i.dts b/arch/arm/boot/dts/imx6q-cubox-i.dts +index bc5f31e3e892..9efd8b0c8011 100644 +--- a/arch/arm/boot/dts/imx6q-cubox-i.dts ++++ b/arch/arm/boot/dts/imx6q-cubox-i.dts +@@ -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 --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +index c2a24888a276..f45380073973 100644 +--- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi ++++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +@@ -12,6 +12,19 @@ + pinctrl-0 = <&pinctrl_cubox_i_ir>; + }; + ++ pwmleds { ++ compatible = "pwm-leds"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_pwm1>; ++ ++ front { ++ active-low; ++ label = "imx6:red:front"; ++ max-brightness = <248>; ++ pwms = <&pwm1 0 50000>; ++ }; ++ }; ++ + regulators { + compatible = "simple-bus"; + +@@ -55,6 +68,20 @@ + }; + }; + ++&hdmi { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_hdmi>; ++ ddc-i2c-bus = <&i2c2>; ++ status = "okay"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_i2c2>; ++ status = "okay"; ++}; ++ + &i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_i2c3>; +@@ -69,6 +96,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,6 +122,10 @@ + >; + }; + ++ pinctrl_cubox_i_pwm1: cubox-i-pwm1-front-led { ++ fsl,pins = <MX6QDL_PAD_DISP0_DAT8__PWM1_OUT 0x1b0b0>; ++ }; ++ + pinctrl_cubox_i_spdif: cubox-i-spdif { + fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>; + }; +@@ -111,6 +155,28 @@ + 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 ++ >; ++ }; + }; + }; + +@@ -130,9 +196,19 @@ + status = "okay"; + }; + ++&uart4 { ++ status = "okay"; ++}; ++ ++&usdhc1 { ++ 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>; + status = "okay"; +diff --git a/arch/arm/boot/dts/imx6qdl-microsom.dtsi b/arch/arm/boot/dts/imx6qdl-microsom.dtsi +index d729d0b15f25..a5d72895d9ce 100644 +--- a/arch/arm/boot/dts/imx6qdl-microsom.dtsi ++++ b/arch/arm/boot/dts/imx6qdl-microsom.dtsi +@@ -1,9 +1,69 @@ + /* + * Copyright (C) 2013,2014 Russell King + */ ++#include <dt-bindings/gpio/gpio.h> ++/ { ++ 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_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_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_uart1: microsom-uart1 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 +@@ -11,6 +71,15 @@ + >; + }; + ++ 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_usbotg: microsom-usbotg { + /* + * Similar to pinctrl_usbotg_2, but we want it +@@ -18,6 +87,17 @@ + */ + fsl,pins = <MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059>; + }; ++ ++ 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 +107,25 @@ + status = "okay"; + }; + ++/* 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; ++}; ++ + &usbotg { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_usbotg>; + }; ++ ++/* 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_brcm_wifi &pinctrl_microsom_usdhc1>; ++ vmmc-supply = <®_brcm>; ++}; +diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig +index 09e974392fa1..ea50d34f7944 100644 +--- a/arch/arm/configs/imx_v6_v7_defconfig ++++ b/arch/arm/configs/imx_v6_v7_defconfig +@@ -245,6 +245,7 @@ CONFIG_DRM_IMX_TVE=y + CONFIG_DRM_IMX_LDB=y + CONFIG_DRM_IMX_IPUV3_CORE=y + CONFIG_DRM_IMX_IPUV3=y ++CONFIG_DRM_IMX_HDMI=y + CONFIG_COMMON_CLK_DEBUG=y + # CONFIG_IOMMU_SUPPORT is not set + CONFIG_PWM=y +diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c +index 2b4d6acfa34a..fefa6c3d4c86 100644 +--- a/arch/arm/mach-imx/clk-imx6q.c ++++ b/arch/arm/mach-imx/clk-imx6q.c +@@ -454,6 +454,18 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) + 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_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. +diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c +index 61364050fccd..3776f974d1dc 100644 +--- a/arch/arm/mach-imx/clk-pllv3.c ++++ b/arch/arm/mach-imx/clk-pllv3.c +@@ -273,9 +273,10 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, + 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) + return -EINVAL; +@@ -287,13 +288,27 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, + 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 = { +diff --git a/drivers/Kconfig b/drivers/Kconfig +index 0a0a90f52d26..05a21b857996 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -174,4 +174,6 @@ source "drivers/powercap/Kconfig" + + source "drivers/mcb/Kconfig" + ++source "drivers/cec/Kconfig" ++ + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index 7183b6af5dac..470eec2238ca 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -157,3 +157,4 @@ obj-$(CONFIG_NTB) += ntb/ + obj-$(CONFIG_FMC) += fmc/ + obj-$(CONFIG_POWERCAP) += powercap/ + obj-$(CONFIG_MCB) += mcb/ ++obj-$(CONFIG_CEC) += cec/ +diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c +index 8befeb69eeb1..54e2972fc6da 100644 +--- a/drivers/ata/ahci_imx.c ++++ b/drivers/ata/ahci_imx.c +@@ -62,6 +62,7 @@ struct imx_ahci_priv { + struct regmap *gpr; + bool no_device; + bool first_time; ++ u32 phy_params; + }; + + static int ahci_imx_hotplug; +@@ -246,14 +247,7 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv) + 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 | +- 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); ++ imxpriv->phy_params); + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + IMX6Q_GPR13_SATA_MPLL_CLK_EN); +@@ -324,6 +318,10 @@ static void ahci_imx_error_handler(struct ata_port *ap) + 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, +@@ -364,6 +362,165 @@ static const struct of_device_id imx_ahci_of_match[] = { + }; + MODULE_DEVICE_TABLE(of, imx_ahci_of_match); + ++struct reg_value { ++ u32 of_value; ++ u32 reg_value; ++}; ++ ++struct reg_property { ++ const char *name; ++ const struct reg_value *values; ++ size_t num_values; ++ u32 def_value; ++ u32 set_value; ++}; ++ ++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 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 }, ++ { 111, IMX6Q_GPR13_SATA_TX_BOOST_1_11_DB }, ++ { 148, IMX6Q_GPR13_SATA_TX_BOOST_1_48_DB }, ++ { 185, IMX6Q_GPR13_SATA_TX_BOOST_1_85_DB }, ++ { 222, IMX6Q_GPR13_SATA_TX_BOOST_2_22_DB }, ++ { 259, IMX6Q_GPR13_SATA_TX_BOOST_2_59_DB }, ++ { 296, IMX6Q_GPR13_SATA_TX_BOOST_2_96_DB }, ++ { 333, IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB }, ++ { 370, IMX6Q_GPR13_SATA_TX_BOOST_3_70_DB }, ++ { 407, IMX6Q_GPR13_SATA_TX_BOOST_4_07_DB }, ++ { 444, IMX6Q_GPR13_SATA_TX_BOOST_4_44_DB }, ++ { 481, IMX6Q_GPR13_SATA_TX_BOOST_4_81_DB }, ++ { 528, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB }, ++ { 575, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB } ++}; ++ ++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 }, ++}; ++ ++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 }, ++}; ++ ++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 u32 imx_ahci_parse_props(struct device *dev, ++ const struct reg_property *prop, size_t num) ++{ ++ 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; ++ } ++ ++ 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; ++ } ++ ++ 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; ++ } ++ } ++ ++ return reg_value; ++} ++ + static int imx_ahci_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -392,6 +549,8 @@ static int imx_ahci_probe(struct platform_device *pdev) + } + + if (imxpriv->type == AHCI_IMX6Q) { ++ u32 reg_value; ++ + imxpriv->gpr = syscon_regmap_lookup_by_compatible( + "fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imxpriv->gpr)) { +@@ -399,6 +558,15 @@ static int imx_ahci_probe(struct platform_device *pdev) + "failed to find fsl,imx6q-iomux-gpr regmap\n"); + return PTR_ERR(imxpriv->gpr); + } ++ ++ 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 | ++ reg_value; + } + + hpriv = ahci_platform_get_resources(pdev); +diff --git a/drivers/cec/Kconfig b/drivers/cec/Kconfig +new file mode 100644 +index 000000000000..d67cfb83de6a +--- /dev/null ++++ b/drivers/cec/Kconfig +@@ -0,0 +1,14 @@ ++# ++# Consumer Electroncs Control support ++# ++ ++menu "Consumer Electronics Control devices" ++ ++config CEC ++ bool ++ ++config HDMI_CEC_CORE ++ tristate ++ select CEC ++ ++endmenu +diff --git a/drivers/cec/Makefile b/drivers/cec/Makefile +new file mode 100644 +index 000000000000..b94278bc8321 +--- /dev/null ++++ b/drivers/cec/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_HDMI_CEC_CORE) += cec-dev.o +diff --git a/drivers/cec/cec-dev.c b/drivers/cec/cec-dev.c +new file mode 100644 +index 000000000000..ba58d8217851 +--- /dev/null ++++ b/drivers/cec/cec-dev.c +@@ -0,0 +1,384 @@ ++/* ++ * HDMI Consumer Electronics Control ++ * ++ * This provides the user API for communication with HDMI CEC complaint ++ * devices in kernel drivers, and is based upon the protocol developed ++ * by Freescale for their i.MX SoCs. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include <linux/cec-dev.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/module.h> ++#include <linux/poll.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++ ++struct cec_event { ++ struct cec_user_event usr; ++ struct list_head node; ++}; ++ ++static struct class *cec_class; ++static int cec_major; ++ ++static void cec_dev_send_message(struct cec_dev *cec_dev, u8 *msg, ++ size_t count) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cec_dev->lock, flags); ++ cec_dev->retries = 5; ++ cec_dev->write_busy = 1; ++ cec_dev->send_message(cec_dev, msg, count); ++ spin_unlock_irqrestore(&cec_dev->lock, flags); ++} ++ ++void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len) ++{ ++ struct cec_event *event; ++ unsigned long flags; ++ ++ event = kzalloc(sizeof(*event), GFP_ATOMIC); ++ if (event) { ++ event->usr.event_type = type; ++ event->usr.msg_len = len; ++ if (msg) ++ memcpy(event->usr.msg, msg, len); ++ ++ spin_lock_irqsave(&cec_dev->lock, flags); ++ list_add_tail(&event->node, &cec_dev->events); ++ spin_unlock_irqrestore(&cec_dev->lock, flags); ++ wake_up(&cec_dev->waitq); ++ } ++} ++EXPORT_SYMBOL_GPL(cec_dev_event); ++ ++static int cec_dev_lock_write(struct cec_dev *cec_dev, struct file *file) ++ __acquires(cec_dev->mutex) ++{ ++ int ret; ++ ++ do { ++ if (file->f_flags & O_NONBLOCK) { ++ if (cec_dev->write_busy) ++ return -EAGAIN; ++ } else { ++ ret = wait_event_interruptible(cec_dev->waitq, ++ !cec_dev->write_busy); ++ if (ret) ++ break; ++ } ++ ++ ret = mutex_lock_interruptible(&cec_dev->mutex); ++ if (ret) ++ break; ++ ++ if (!cec_dev->write_busy) ++ break; ++ ++ mutex_unlock(&cec_dev->mutex); ++ } while (1); ++ ++ return ret; ++} ++ ++static ssize_t cec_dev_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cec_dev *cec_dev = file->private_data; ++ ssize_t ret; ++ ++ if (count > sizeof(struct cec_user_event)) ++ count = sizeof(struct cec_user_event); ++ ++ if (!access_ok(VERIFY_WRITE, buf, count)) ++ return -EFAULT; ++ ++ do { ++ struct cec_event *event = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cec_dev->lock, flags); ++ if (!list_empty(&cec_dev->events)) { ++ event = list_first_entry(&cec_dev->events, ++ struct cec_event, node); ++ list_del(&event->node); ++ } ++ spin_unlock_irqrestore(&cec_dev->lock, flags); ++ ++ if (event) { ++ ret = __copy_to_user(buf, &event->usr, count) ? ++ -EFAULT : count; ++ kfree(event); ++ break; ++ } ++ ++ if (file->f_flags & O_NONBLOCK) { ++ ret = -EAGAIN; ++ break; ++ } ++ ++ ret = wait_event_interruptible(cec_dev->waitq, ++ !list_empty(&cec_dev->events)); ++ if (ret) ++ break; ++ } while (1); ++ ++ return ret; ++} ++ ++static ssize_t cec_dev_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cec_dev *cec_dev = file->private_data; ++ u8 msg[MAX_MESSAGE_LEN]; ++ int ret; ++ ++ if (count > sizeof(msg)) ++ return -E2BIG; ++ ++ if (copy_from_user(msg, buf, count)) ++ return -EFAULT; ++ ++ ret = cec_dev_lock_write(cec_dev, file); ++ if (ret) ++ return ret; ++ ++ cec_dev_send_message(cec_dev, msg, count); ++ ++ mutex_unlock(&cec_dev->mutex); ++ ++ return count; ++} ++ ++static long cec_dev_ioctl(struct file *file, u_int cmd, unsigned long arg) ++{ ++ struct cec_dev *cec_dev = file->private_data; ++ int ret; ++ ++ switch (cmd) { ++ case HDMICEC_IOC_O_SETLOGICALADDRESS: ++ case HDMICEC_IOC_SETLOGICALADDRESS: ++ if (arg > 15) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ ret = cec_dev_lock_write(cec_dev, file); ++ if (ret == 0) { ++ unsigned char msg[1]; ++ ++ cec_dev->addresses = BIT(arg); ++ cec_dev->set_address(cec_dev, cec_dev->addresses); ++ ++ /* ++ * Send a ping message with the source and destination ++ * set to our address; the result indicates whether ++ * unit has chosen our address simultaneously. ++ */ ++ msg[0] = arg << 4 | arg; ++ cec_dev_send_message(cec_dev, msg, sizeof(msg)); ++ mutex_unlock(&cec_dev->mutex); ++ } ++ break; ++ ++ case HDMICEC_IOC_STARTDEVICE: ++ ret = mutex_lock_interruptible(&cec_dev->mutex); ++ if (ret == 0) { ++ cec_dev->addresses = BIT(15); ++ cec_dev->set_address(cec_dev, cec_dev->addresses); ++ mutex_unlock(&cec_dev->mutex); ++ } ++ break; ++ ++ case HDMICEC_IOC_STOPDEVICE: ++ ret = 0; ++ break; ++ ++ case HDMICEC_IOC_GETPHYADDRESS: ++ ret = put_user(cec_dev->physical, (u16 __user *)arg); ++ ret = -ENOIOCTLCMD; ++ break; ++ ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ ++ return ret; ++} ++ ++static unsigned cec_dev_poll(struct file *file, poll_table *wait) ++{ ++ struct cec_dev *cec_dev = file->private_data; ++ unsigned mask = 0; ++ ++ poll_wait(file, &cec_dev->waitq, wait); ++ ++ if (cec_dev->write_busy == 0) ++ mask |= POLLOUT | POLLWRNORM; ++ if (!list_empty(&cec_dev->events)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ return mask; ++} ++ ++static int cec_dev_release(struct inode *inode, struct file *file) ++{ ++ struct cec_dev *cec_dev = file->private_data; ++ ++ mutex_lock(&cec_dev->mutex); ++ if (cec_dev->users >= 1) ++ cec_dev->users -= 1; ++ if (cec_dev->users == 0) { ++ /* ++ * Wait for any write to complete before shutting down. ++ * A message should complete in a maximum of 2.75ms * ++ * 160 bits + 4.7ms, or 444.7ms. Let's call that 500ms. ++ * If we time out, shutdown anyway. ++ */ ++ wait_event_timeout(cec_dev->waitq, !cec_dev->write_busy, ++ msecs_to_jiffies(500)); ++ ++ cec_dev->release(cec_dev); ++ ++ while (!list_empty(&cec_dev->events)) { ++ struct cec_event *event; ++ ++ event = list_first_entry(&cec_dev->events, ++ struct cec_event, node); ++ list_del(&event->node); ++ kfree(event); ++ } ++ } ++ mutex_unlock(&cec_dev->mutex); ++ return 0; ++} ++ ++static int cec_dev_open(struct inode *inode, struct file *file) ++{ ++ struct cec_dev *cec_dev = container_of(inode->i_cdev, struct cec_dev, ++ cdev); ++ int ret = 0; ++ ++ nonseekable_open(inode, file); ++ ++ file->private_data = cec_dev; ++ ++ ret = mutex_lock_interruptible(&cec_dev->mutex); ++ if (ret) ++ return ret; ++ ++ if (cec_dev->users++ == 0) { ++ cec_dev->addresses = BIT(15); ++ ++ ret = cec_dev->open(cec_dev); ++ if (ret < 0) ++ cec_dev->users = 0; ++ } ++ mutex_unlock(&cec_dev->mutex); ++ ++ return ret; ++} ++ ++static const struct file_operations hdmi_cec_fops = { ++ .owner = THIS_MODULE, ++ .read = cec_dev_read, ++ .write = cec_dev_write, ++ .open = cec_dev_open, ++ .unlocked_ioctl = cec_dev_ioctl, ++ .release = cec_dev_release, ++ .poll = cec_dev_poll, ++}; ++ ++void cec_dev_init(struct cec_dev *cec_dev, struct module *module) ++{ ++ cec_dev->devn = MKDEV(cec_major, 0); ++ ++ INIT_LIST_HEAD(&cec_dev->events); ++ init_waitqueue_head(&cec_dev->waitq); ++ spin_lock_init(&cec_dev->lock); ++ mutex_init(&cec_dev->mutex); ++ ++ cec_dev->addresses = BIT(15); ++ ++ cdev_init(&cec_dev->cdev, &hdmi_cec_fops); ++ cec_dev->cdev.owner = module; ++} ++EXPORT_SYMBOL_GPL(cec_dev_init); ++ ++int cec_dev_add(struct cec_dev *cec_dev, struct device *dev, const char *name) ++{ ++ struct device *cd; ++ int ret; ++ ++ ret = cdev_add(&cec_dev->cdev, cec_dev->devn, 1); ++ if (ret < 0) ++ goto err_cdev; ++ ++ cd = device_create(cec_class, dev, cec_dev->devn, NULL, name); ++ if (IS_ERR(cd)) { ++ ret = PTR_ERR(cd); ++ dev_err(dev, "can't create device: %d\n", ret); ++ goto err_dev; ++ } ++ ++ return 0; ++ ++ err_dev: ++ cdev_del(&cec_dev->cdev); ++ err_cdev: ++ return ret; ++} ++EXPORT_SYMBOL_GPL(cec_dev_add); ++ ++void cec_dev_remove(struct cec_dev *cec_dev) ++{ ++ device_destroy(cec_class, cec_dev->devn); ++ cdev_del(&cec_dev->cdev); ++} ++EXPORT_SYMBOL_GPL(cec_dev_remove); ++ ++static int cec_init(void) ++{ ++ dev_t dev; ++ int ret; ++ ++ cec_class = class_create(THIS_MODULE, "hdmi-cec"); ++ if (IS_ERR(cec_class)) { ++ ret = PTR_ERR(cec_class); ++ pr_err("cec: can't create cec class: %d\n", ret); ++ goto err_class; ++ } ++ ++ ret = alloc_chrdev_region(&dev, 0, 1, "hdmi-cec"); ++ if (ret) { ++ pr_err("cec: can't create character devices: %d\n", ret); ++ goto err_chrdev; ++ } ++ ++ cec_major = MAJOR(dev); ++ ++ return 0; ++ ++ err_chrdev: ++ class_destroy(cec_class); ++ err_class: ++ return ret; ++} ++subsys_initcall(cec_init); ++ ++static void cec_exit(void) ++{ ++ unregister_chrdev_region(MKDEV(cec_major, 0), 1); ++ class_destroy(cec_class); ++} ++module_exit(cec_exit); ++ ++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>"); ++MODULE_DESCRIPTION("Generic HDMI CEC driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c +index df281b54db01..20585cf62091 100644 +--- a/drivers/gpu/drm/drm_crtc_helper.c ++++ b/drivers/gpu/drm/drm_crtc_helper.c +@@ -140,16 +140,10 @@ drm_encoder_disable(struct drm_encoder *encoder) + static void __drm_helper_disable_unused_functions(struct drm_device *dev) + { + struct drm_encoder *encoder; +- struct drm_connector *connector; + struct drm_crtc *crtc; + + drm_warn_on_modeset_not_all_locked(dev); + +- list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +- if (!connector->encoder) +- continue; +- } +- + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (!drm_helper_encoder_in_use(encoder)) { + drm_encoder_disable(encoder); +diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c +index 7d0aaed1e23a..f5cf1b0f2748 100644 +--- a/drivers/leds/leds-pwm.c ++++ b/drivers/leds/leds-pwm.c +@@ -69,6 +69,10 @@ static void led_pwm_set(struct led_classdev *led_cdev, + + duty *= brightness; + do_div(duty, max); ++ ++ if (led_dat->active_low) ++ duty = led_dat->period - duty; ++ + led_dat->duty = duty; + + if (led_dat->can_sleep) +@@ -92,55 +96,75 @@ static void led_pwm_cleanup(struct led_pwm_priv *priv) + } + } + +-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, +- "linux,default-trigger", NULL); +- of_property_read_u32(child, "max-brightness", +- &led_dat->cdev.max_brightness); ++ 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); ++ } + +- led_dat->cdev.brightness_set = led_pwm_set; +- led_dat->cdev.brightness = LED_OFF; +- led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; ++ return ret; ++} + +- led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); +- if (led_dat->can_sleep) +- INIT_WORK(&led_dat->work, led_pwm_work); ++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; + +- 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); ++ 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.max_brightness); ++ ++ 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; + } + +@@ -166,51 +190,23 @@ static int led_pwm_probe(struct platform_device *pdev) + + 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 --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c +index acbc3f2aaaf9..41c4033ec765 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -13,11 +13,13 @@ + #include <linux/module.h> + #include <linux/init.h> + #include <linux/interrupt.h> ++#include <linux/clk.h> + #include <linux/completion.h> + #include <linux/device.h> + #include <linux/delay.h> + #include <linux/pagemap.h> + #include <linux/err.h> ++#include <linux/gpio.h> + #include <linux/leds.h> + #include <linux/scatterlist.h> + #include <linux/log2.h> +@@ -1504,6 +1506,43 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) + mmc_host_clk_release(host); + } + ++static void mmc_card_power_up(struct mmc_host *host) ++{ ++ int i; ++ struct gpio_desc **gds = host->card_reset_gpios; ++ ++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { ++ if (gds[i]) { ++ dev_dbg(host->parent, "Asserting reset line %d", i); ++ gpiod_set_value(gds[i], 1); ++ } ++ } ++ ++ if (host->card_regulator) { ++ dev_dbg(host->parent, "Enabling external regulator"); ++ if (regulator_enable(host->card_regulator)) ++ dev_err(host->parent, "Failed to enable external regulator"); ++ } ++ ++ if (host->card_clk) { ++ dev_dbg(host->parent, "Enabling external clock"); ++ clk_prepare_enable(host->card_clk); ++ } ++ ++ /* 2ms delay to let clocks and power settle */ ++ mmc_delay(20); ++ ++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { ++ if (gds[i]) { ++ dev_dbg(host->parent, "Deasserting reset line %d", i); ++ gpiod_set_value(gds[i], 0); ++ } ++ } ++ ++ /* 2ms delay to after reset release */ ++ mmc_delay(20); ++} ++ + /* + * Apply power to the MMC stack. This is a two-stage process. + * First, we enable power to the card without the clock running. +@@ -1520,6 +1559,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) + if (host->ios.power_mode == MMC_POWER_ON) + return; + ++ /* Power up the card/module first, if needed */ ++ mmc_card_power_up(host); ++ + mmc_host_clk_hold(host); + + host->ios.vdd = fls(ocr) - 1; +diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c +index fdea825dbb24..c523f55b317c 100644 +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -12,14 +12,18 @@ + * MMC host class device management + */ + ++#include <linux/kernel.h> ++#include <linux/clk.h> + #include <linux/device.h> + #include <linux/err.h> ++#include <linux/gpio/consumer.h> + #include <linux/idr.h> + #include <linux/of.h> + #include <linux/of_gpio.h> + #include <linux/pagemap.h> + #include <linux/export.h> + #include <linux/leds.h> ++#include <linux/regulator/consumer.h> + #include <linux/slab.h> + #include <linux/suspend.h> + +@@ -457,6 +461,66 @@ int mmc_of_parse(struct mmc_host *host) + + EXPORT_SYMBOL(mmc_of_parse); + ++static int mmc_of_parse_child(struct mmc_host *host) ++{ ++ struct device_node *np; ++ struct clk *clk; ++ int i; ++ ++ if (!host->parent || !host->parent->of_node) ++ return 0; ++ ++ np = host->parent->of_node; ++ ++ host->card_regulator = regulator_get(host->parent, "card-external-vcc"); ++ if (IS_ERR(host->card_regulator)) { ++ if (PTR_ERR(host->card_regulator) == -EPROBE_DEFER) ++ return PTR_ERR(host->card_regulator); ++ host->card_regulator = NULL; ++ } ++ ++ /* Parse card power/reset/clock control */ ++ if (of_find_property(np, "card-reset-gpios", NULL)) { ++ struct gpio_desc *gpd; ++ int level = 0; ++ ++ /* ++ * If the regulator is enabled, then we can hold the ++ * card in reset with an active high resets. Otherwise, ++ * hold the resets low. ++ */ ++ if (host->card_regulator && regulator_is_enabled(host->card_regulator)) ++ level = 1; ++ ++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { ++ gpd = devm_gpiod_get_index(host->parent, "card-reset", i); ++ if (IS_ERR(gpd)) { ++ if (PTR_ERR(gpd) == -EPROBE_DEFER) ++ return PTR_ERR(gpd); ++ break; ++ } ++ gpiod_direction_output(gpd, gpiod_is_active_low(gpd) | level); ++ host->card_reset_gpios[i] = gpd; ++ } ++ ++ gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios)); ++ if (!IS_ERR(gpd)) { ++ dev_warn(host->parent, "More reset gpios than we can handle"); ++ gpiod_put(gpd); ++ } ++ } ++ ++ clk = of_clk_get_by_name(np, "card_ext_clock"); ++ if (IS_ERR(clk)) { ++ if (PTR_ERR(clk) == -EPROBE_DEFER) ++ return PTR_ERR(clk); ++ clk = NULL; ++ } ++ host->card_clk = clk; ++ ++ return 0; ++} ++ + /** + * mmc_alloc_host - initialise the per-host structure. + * @extra: sizeof private data structure +@@ -536,6 +600,10 @@ int mmc_add_host(struct mmc_host *host) + { + int err; + ++ err = mmc_of_parse_child(host); ++ if (err) ++ return err; ++ + WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && + !host->ops->enable_sdio_irq); + +diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c +index aaa90460ed23..5cc13c8d35bb 100644 +--- a/drivers/mmc/core/sdio_irq.c ++++ b/drivers/mmc/core/sdio_irq.c +@@ -90,6 +90,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host) + return ret; + } + ++void sdio_run_irqs(struct mmc_host *host) ++{ ++ mmc_claim_host(host); ++ host->sdio_irq_pending = true; ++ process_sdio_pending_irqs(host); ++ mmc_release_host(host); ++} ++EXPORT_SYMBOL_GPL(sdio_run_irqs); ++ + static int sdio_irq_thread(void *_host) + { + struct mmc_host *host = _host; +@@ -189,14 +198,20 @@ static int sdio_card_irq_get(struct mmc_card *card) + WARN_ON(!host->claimed); + + if (!host->sdio_irqs++) { +- atomic_set(&host->sdio_irq_thread_abort, 0); +- host->sdio_irq_thread = +- kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", +- mmc_hostname(host)); +- if (IS_ERR(host->sdio_irq_thread)) { +- int err = PTR_ERR(host->sdio_irq_thread); +- host->sdio_irqs--; +- return err; ++ if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { ++ atomic_set(&host->sdio_irq_thread_abort, 0); ++ host->sdio_irq_thread = ++ kthread_run(sdio_irq_thread, host, ++ "ksdioirqd/%s", mmc_hostname(host)); ++ if (IS_ERR(host->sdio_irq_thread)) { ++ int err = PTR_ERR(host->sdio_irq_thread); ++ host->sdio_irqs--; ++ return err; ++ } ++ } else { ++ mmc_host_clk_hold(host); ++ host->ops->enable_sdio_irq(host, 1); ++ mmc_host_clk_release(host); + } + } + +@@ -211,8 +226,14 @@ static int sdio_card_irq_put(struct mmc_card *card) + BUG_ON(host->sdio_irqs < 1); + + if (!--host->sdio_irqs) { +- atomic_set(&host->sdio_irq_thread_abort, 1); +- kthread_stop(host->sdio_irq_thread); ++ if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { ++ atomic_set(&host->sdio_irq_thread_abort, 1); ++ kthread_stop(host->sdio_irq_thread); ++ } else { ++ mmc_host_clk_hold(host); ++ host->ops->enable_sdio_irq(host, 0); ++ mmc_host_clk_release(host); ++ } + } + + return 0; +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 8aaf8c1f3f63..d8c1f31ab37f 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -25,8 +25,7 @@ config MMC_PXA + If unsure, say N. + + config MMC_SDHCI +- tristate "Secure Digital Host Controller Interface support" +- depends on HAS_DMA ++ tristate + help + This selects the generic Secure Digital Host Controller Interface. + It is used by manufacturers such as Texas Instruments(R), Ricoh(R) +@@ -59,7 +58,8 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER + + config MMC_SDHCI_PCI + tristate "SDHCI support on PCI bus" +- depends on MMC_SDHCI && PCI ++ depends on PCI && HAS_DMA ++ select MMC_SDHCI + help + This selects the PCI Secure Digital Host Controller Interface. + Most controllers found today are PCI devices. +@@ -83,7 +83,8 @@ config MMC_RICOH_MMC + + config MMC_SDHCI_ACPI + tristate "SDHCI support for ACPI enumerated SDHCI controllers" +- depends on MMC_SDHCI && ACPI ++ depends on ACPI && HAS_DMA ++ select MMC_SDHCI + help + This selects support for ACPI enumerated SDHCI controllers, + identified by ACPI Compatibility ID PNP0D40 or specific +@@ -94,8 +95,8 @@ config MMC_SDHCI_ACPI + If unsure, say N. + + config MMC_SDHCI_PLTFM +- tristate "SDHCI platform and OF driver helper" +- depends on MMC_SDHCI ++ tristate ++ select MMC_SDHCI + help + This selects the common helper functions support for Secure Digital + Host Controller Interface based platform and OF drivers. +@@ -106,8 +107,8 @@ config MMC_SDHCI_PLTFM + + config MMC_SDHCI_OF_ARASAN + tristate "SDHCI OF support for the Arasan SDHCI controllers" +- depends on MMC_SDHCI_PLTFM +- depends on OF ++ depends on OF && HAS_DMA ++ select MMC_SDHCI_PLTFM + help + This selects the Arasan Secure Digital Host Controller Interface + (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. +@@ -118,9 +119,9 @@ config MMC_SDHCI_OF_ARASAN + + config MMC_SDHCI_OF_ESDHC + tristate "SDHCI OF support for the Freescale eSDHC controller" +- depends on MMC_SDHCI_PLTFM +- depends on PPC_OF ++ depends on PPC_OF && HAS_DMA + select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER ++ select MMC_SDHCI_PLTFM + help + This selects the Freescale eSDHC controller support. + +@@ -130,9 +131,9 @@ config MMC_SDHCI_OF_ESDHC + + config MMC_SDHCI_OF_HLWD + tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers" +- depends on MMC_SDHCI_PLTFM +- depends on PPC_OF ++ depends on PPC_OF && HAS_DMA + select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER ++ select MMC_SDHCI_PLTFM + help + This selects the Secure Digital Host Controller Interface (SDHCI) + found in the "Hollywood" chipset of the Nintendo Wii video game +@@ -144,8 +145,8 @@ config MMC_SDHCI_OF_HLWD + + config MMC_SDHCI_CNS3XXX + tristate "SDHCI support on the Cavium Networks CNS3xxx SoC" +- depends on ARCH_CNS3XXX +- depends on MMC_SDHCI_PLTFM ++ depends on ARCH_CNS3XXX && HAS_DMA ++ select MMC_SDHCI_PLTFM + help + This selects the SDHCI support for CNS3xxx System-on-Chip devices. + +@@ -155,9 +156,9 @@ config MMC_SDHCI_CNS3XXX + + config MMC_SDHCI_ESDHC_IMX + tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller" +- depends on ARCH_MXC +- depends on MMC_SDHCI_PLTFM ++ depends on ARCH_MXC && HAS_DMA + select MMC_SDHCI_IO_ACCESSORS ++ select MMC_SDHCI_PLTFM + help + This selects the Freescale eSDHC/uSDHC controller support + found on i.MX25, i.MX35 i.MX5x and i.MX6x. +@@ -168,9 +169,9 @@ config MMC_SDHCI_ESDHC_IMX + + config MMC_SDHCI_DOVE + tristate "SDHCI support on Marvell's Dove SoC" +- depends on ARCH_DOVE +- depends on MMC_SDHCI_PLTFM ++ depends on ARCH_DOVE && HAS_DMA + select MMC_SDHCI_IO_ACCESSORS ++ select MMC_SDHCI_PLTFM + help + This selects the Secure Digital Host Controller Interface in + Marvell's Dove SoC. +@@ -181,9 +182,9 @@ config MMC_SDHCI_DOVE + + config MMC_SDHCI_TEGRA + tristate "SDHCI platform support for the Tegra SD/MMC Controller" +- depends on ARCH_TEGRA +- depends on MMC_SDHCI_PLTFM ++ depends on ARCH_TEGRA && HAS_DMA + select MMC_SDHCI_IO_ACCESSORS ++ select MMC_SDHCI_PLTFM + help + This selects the Tegra SD/MMC controller. If you have a Tegra + platform with SD or MMC devices, say Y or M here. +@@ -192,7 +193,8 @@ config MMC_SDHCI_TEGRA + + config MMC_SDHCI_S3C + tristate "SDHCI support on Samsung S3C SoC" +- depends on MMC_SDHCI && PLAT_SAMSUNG ++ depends on PLAT_SAMSUNG && HAS_DMA ++ select MMC_SDHCI + help + This selects the Secure Digital Host Controller Interface (SDHCI) + often referrered to as the HSMMC block in some of the Samsung S3C +@@ -204,8 +206,8 @@ config MMC_SDHCI_S3C + + config MMC_SDHCI_SIRF + tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs" +- depends on ARCH_SIRF +- depends on MMC_SDHCI_PLTFM ++ depends on ARCH_SIRF && HAS_DMA ++ select MMC_SDHCI_PLTFM + help + This selects the SDHCI support for SiRF System-on-Chip devices. + +@@ -215,8 +217,7 @@ config MMC_SDHCI_SIRF + + config MMC_SDHCI_PXAV3 + tristate "Marvell MMP2 SD Host Controller support (PXAV3)" +- depends on CLKDEV_LOOKUP +- select MMC_SDHCI ++ depends on CLKDEV_LOOKUP && HAS_DMA + select MMC_SDHCI_PLTFM + default CPU_MMP2 + help +@@ -228,8 +229,7 @@ config MMC_SDHCI_PXAV3 + + config MMC_SDHCI_PXAV2 + tristate "Marvell PXA9XX SD Host Controller support (PXAV2)" +- depends on CLKDEV_LOOKUP +- select MMC_SDHCI ++ depends on CLKDEV_LOOKUP && HAS_DMA + select MMC_SDHCI_PLTFM + default CPU_PXA910 + help +@@ -241,7 +241,8 @@ config MMC_SDHCI_PXAV2 + + config MMC_SDHCI_SPEAR + tristate "SDHCI support on ST SPEAr platform" +- depends on MMC_SDHCI && PLAT_SPEAR ++ depends on PLAT_SPEAR && HAS_DMA ++ select MMC_SDHCI + help + This selects the Secure Digital Host Controller Interface (SDHCI) + often referrered to as the HSMMC block in some of the ST SPEAR range +@@ -263,7 +264,7 @@ config MMC_SDHCI_S3C_DMA + + config MMC_SDHCI_BCM_KONA + tristate "SDHCI support on Broadcom KONA platform" +- depends on ARCH_BCM_MOBILE ++ depends on ARCH_BCM_MOBILE && HAS_DMA + select MMC_SDHCI_PLTFM + help + This selects the Broadcom Kona Secure Digital Host Controller +@@ -274,9 +275,9 @@ config MMC_SDHCI_BCM_KONA + + config MMC_SDHCI_BCM2835 + tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" +- depends on ARCH_BCM2835 +- depends on MMC_SDHCI_PLTFM ++ depends on ARCH_BCM2835 && HAS_DMA + select MMC_SDHCI_IO_ACCESSORS ++ select MMC_SDHCI_PLTFM + help + This selects the BCM2835 SD/MMC controller. If you have a BCM2835 + platform with SD or MMC devices, say Y or M here. +diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c +index cced599d5aeb..a2c12ebb376e 100644 +--- a/drivers/mmc/host/dw_mmc.c ++++ b/drivers/mmc/host/dw_mmc.c +@@ -2140,6 +2140,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) + if (!mmc) + return -ENOMEM; + ++ mmc_of_parse(mmc); ++ + slot = mmc_priv(mmc); + slot->id = id; + slot->mmc = mmc; +diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c +index ebb3f392b589..8ce3c28cb76e 100644 +--- a/drivers/mmc/host/sdhci-acpi.c ++++ b/drivers/mmc/host/sdhci-acpi.c +@@ -102,11 +102,19 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) + } + + static const struct sdhci_ops sdhci_acpi_ops_dflt = { ++ .set_clock = sdhci_set_clock, + .enable_dma = sdhci_acpi_enable_dma, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static const struct sdhci_ops sdhci_acpi_ops_int = { ++ .set_clock = sdhci_set_clock, + .enable_dma = sdhci_acpi_enable_dma, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + .hw_reset = sdhci_acpi_int_hw_reset, + }; + +diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c +index 6f166e63b817..dd780c315a63 100644 +--- a/drivers/mmc/host/sdhci-bcm-kona.c ++++ b/drivers/mmc/host/sdhci-bcm-kona.c +@@ -206,9 +206,13 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, + } + + static struct sdhci_ops sdhci_bcm_kona_ops = { ++ .set_clock = sdhci_set_clock, + .get_max_clock = sdhci_bcm_kona_get_max_clk, + .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, + .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + .card_event = sdhci_bcm_kona_card_event, + }; + +diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c +index f6d8d67c545f..46af9a439d7b 100644 +--- a/drivers/mmc/host/sdhci-bcm2835.c ++++ b/drivers/mmc/host/sdhci-bcm2835.c +@@ -131,8 +131,12 @@ static const struct sdhci_ops bcm2835_sdhci_ops = { + .read_l = bcm2835_sdhci_readl, + .read_w = bcm2835_sdhci_readw, + .read_b = bcm2835_sdhci_readb, ++ .set_clock = sdhci_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_min_clock = bcm2835_sdhci_get_min_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = { +diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c +index f2cc26633cb2..14b74075589a 100644 +--- a/drivers/mmc/host/sdhci-cns3xxx.c ++++ b/drivers/mmc/host/sdhci-cns3xxx.c +@@ -30,13 +30,12 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) + u16 clk; + unsigned long timeout; + +- if (clock == host->clock) +- return; ++ host->mmc->actual_clock = 0; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) +- goto out; ++ return; + + while (host->max_clk / div > clock) { + /* +@@ -75,13 +74,14 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +-out: +- host->clock = clock; + } + + static const struct sdhci_ops sdhci_cns3xxx_ops = { + .get_max_clock = sdhci_cns3xxx_get_max_clk, + .set_clock = sdhci_cns3xxx_set_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { +@@ -90,8 +90,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | +- SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | +- SDHCI_QUIRK_NONSTANDARD_CLOCK, ++ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + }; + + static int sdhci_cns3xxx_probe(struct platform_device *pdev) +diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c +index 736d7a2eb7ec..0d315f4496c8 100644 +--- a/drivers/mmc/host/sdhci-dove.c ++++ b/drivers/mmc/host/sdhci-dove.c +@@ -86,6 +86,10 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) + static const struct sdhci_ops sdhci_dove_ops = { + .read_w = sdhci_dove_readw, + .read_l = sdhci_dove_readl, ++ .set_clock = sdhci_set_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static const struct sdhci_pltfm_data sdhci_dove_pdata = { +diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c +index b841bb7cd371..4866d802f9e2 100644 +--- a/drivers/mmc/host/sdhci-esdhc-imx.c ++++ b/drivers/mmc/host/sdhci-esdhc-imx.c +@@ -160,7 +160,6 @@ struct pltfm_imx_data { + MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ + WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ + } multiblock_status; +- u32 uhs_mode; + u32 is_ddr; + }; + +@@ -382,7 +381,6 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) + if (val & ESDHC_MIX_CTRL_SMPCLK_SEL) + ret |= SDHCI_CTRL_TUNED_CLK; + +- ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK); + ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + + return ret; +@@ -429,7 +427,6 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) + else + new_val &= ~ESDHC_VENDOR_SPEC_VSELECT; + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); +- imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK; + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { + new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); + if (val & SDHCI_CTRL_TUNED_CLK) +@@ -600,12 +597,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, + u32 temp, val; + + if (clock == 0) { ++ host->mmc->actual_clock = 0; ++ + if (esdhc_is_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + } +- goto out; ++ return; + } + + if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr) +@@ -645,8 +644,6 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, + } + + mdelay(1); +-out: +- host->clock = clock; + } + + static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) +@@ -668,7 +665,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) + return -ENOSYS; + } + +-static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) ++static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) + { + u32 ctrl; + +@@ -686,8 +683,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) + + esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl, + SDHCI_HOST_CONTROL); +- +- return 0; + } + + static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) +@@ -697,6 +692,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) + /* FIXME: delay a bit for card to be ready for next tuning due to errors */ + mdelay(1); + ++ /* This is balanced by the runtime put in sdhci_tasklet_finish */ + pm_runtime_get_sync(host->mmc->parent); + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); + reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | +@@ -713,13 +709,12 @@ static void esdhc_request_done(struct mmc_request *mrq) + complete(&mrq->completion); + } + +-static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) ++static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode, ++ struct scatterlist *sg) + { + struct mmc_command cmd = {0}; + struct mmc_request mrq = {NULL}; + struct mmc_data data = {0}; +- struct scatterlist sg; +- char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN]; + + cmd.opcode = opcode; + cmd.arg = 0; +@@ -728,11 +723,9 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) + data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; + data.blocks = 1; + data.flags = MMC_DATA_READ; +- data.sg = &sg; ++ data.sg = sg; + data.sg_len = 1; + +- sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern)); +- + mrq.cmd = &cmd; + mrq.cmd->mrq = &mrq; + mrq.data = &data; +@@ -742,14 +735,12 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) + mrq.done = esdhc_request_done; + init_completion(&(mrq.completion)); + +- disable_irq(host->irq); +- spin_lock(&host->lock); ++ spin_lock_irq(&host->lock); + host->mrq = &mrq; + + sdhci_send_command(host, mrq.cmd); + +- spin_unlock(&host->lock); +- enable_irq(host->irq); ++ spin_unlock_irq(&host->lock); + + wait_for_completion(&mrq.completion); + +@@ -772,13 +763,21 @@ static void esdhc_post_tuning(struct sdhci_host *host) + + static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) + { ++ struct scatterlist sg; ++ char *tuning_pattern; + int min, max, avg, ret; + ++ tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL); ++ if (!tuning_pattern) ++ return -ENOMEM; ++ ++ sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN); ++ + /* find the mininum delay first which can pass tuning */ + min = ESDHC_TUNE_CTRL_MIN; + while (min < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, min); +- if (!esdhc_send_tuning_cmd(host, opcode)) ++ if (!esdhc_send_tuning_cmd(host, opcode, &sg)) + break; + min += ESDHC_TUNE_CTRL_STEP; + } +@@ -787,7 +786,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) + max = min + ESDHC_TUNE_CTRL_STEP; + while (max < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, max); +- if (esdhc_send_tuning_cmd(host, opcode)) { ++ if (esdhc_send_tuning_cmd(host, opcode, &sg)) { + max -= ESDHC_TUNE_CTRL_STEP; + break; + } +@@ -797,9 +796,11 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) + /* use average delay to get the best timing */ + avg = (min + max) / 2; + esdhc_prepare_tuning(host, avg); +- ret = esdhc_send_tuning_cmd(host, opcode); ++ ret = esdhc_send_tuning_cmd(host, opcode, &sg); + esdhc_post_tuning(host); + ++ kfree(tuning_pattern); ++ + dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", + ret ? "failed" : "passed", avg, ret); + +@@ -837,28 +838,20 @@ static int esdhc_change_pinstate(struct sdhci_host *host, + return pinctrl_select_state(imx_data->pinctrl, pinctrl); + } + +-static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) ++static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) + { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + struct esdhc_platform_data *boarddata = &imx_data->boarddata; + +- switch (uhs) { ++ switch (timing) { + case MMC_TIMING_UHS_SDR12: +- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12; +- break; + case MMC_TIMING_UHS_SDR25: +- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25; +- break; + case MMC_TIMING_UHS_SDR50: +- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50; +- break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: +- imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104; + break; + case MMC_TIMING_UHS_DDR50: +- imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50; + writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | + ESDHC_MIX_CTRL_DDREN, + host->ioaddr + ESDHC_MIX_CTRL); +@@ -875,7 +868,15 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) + break; + } + +- return esdhc_change_pinstate(host, uhs); ++ esdhc_change_pinstate(host, timing); ++} ++ ++static void esdhc_reset(struct sdhci_host *host, u8 mask) ++{ ++ sdhci_reset(host, mask); ++ ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + } + + static struct sdhci_ops sdhci_esdhc_ops = { +@@ -888,8 +889,9 @@ static struct sdhci_ops sdhci_esdhc_ops = { + .get_max_clock = esdhc_pltfm_get_max_clock, + .get_min_clock = esdhc_pltfm_get_min_clock, + .get_ro = esdhc_pltfm_get_ro, +- .platform_bus_width = esdhc_pltfm_bus_width, ++ .set_bus_width = esdhc_pltfm_set_bus_width, + .set_uhs_signaling = esdhc_set_uhs_signaling, ++ .reset = esdhc_reset, + }; + + static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { +@@ -1170,8 +1172,10 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) + + ret = sdhci_runtime_suspend_host(host); + +- clk_disable_unprepare(imx_data->clk_per); +- clk_disable_unprepare(imx_data->clk_ipg); ++ if (!sdhci_sdio_irq_enabled(host)) { ++ clk_disable_unprepare(imx_data->clk_per); ++ clk_disable_unprepare(imx_data->clk_ipg); ++ } + clk_disable_unprepare(imx_data->clk_ahb); + + return ret; +@@ -1183,8 +1187,10 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + +- clk_prepare_enable(imx_data->clk_per); +- clk_prepare_enable(imx_data->clk_ipg); ++ if (!sdhci_sdio_irq_enabled(host)) { ++ clk_prepare_enable(imx_data->clk_per); ++ clk_prepare_enable(imx_data->clk_ipg); ++ } + clk_prepare_enable(imx_data->clk_ahb); + + return sdhci_runtime_resume_host(host); +diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h +index a7d9f95a7b03..3497cfaf683c 100644 +--- a/drivers/mmc/host/sdhci-esdhc.h ++++ b/drivers/mmc/host/sdhci-esdhc.h +@@ -20,10 +20,8 @@ + + #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ + SDHCI_QUIRK_NO_BUSY_IRQ | \ +- SDHCI_QUIRK_NONSTANDARD_CLOCK | \ + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ +- SDHCI_QUIRK_PIO_NEEDS_DELAY | \ +- SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) ++ SDHCI_QUIRK_PIO_NEEDS_DELAY) + + #define ESDHC_SYSTEM_CONTROL 0x2c + #define ESDHC_CLOCK_MASK 0x0000fff0 +diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c +index f7c7cf62437d..5bd1092310f2 100644 +--- a/drivers/mmc/host/sdhci-of-arasan.c ++++ b/drivers/mmc/host/sdhci-of-arasan.c +@@ -52,8 +52,12 @@ static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) + } + + static struct sdhci_ops sdhci_arasan_ops = { ++ .set_clock = sdhci_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock = sdhci_arasan_get_timeout_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static struct sdhci_pltfm_data sdhci_arasan_pdata = { +diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c +index 0b249970b119..605815e52f5f 100644 +--- a/drivers/mmc/host/sdhci-of-esdhc.c ++++ b/drivers/mmc/host/sdhci-of-esdhc.c +@@ -199,13 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) + + static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) + { +- + int pre_div = 2; + int div = 1; + u32 temp; + ++ host->mmc->actual_clock = 0; ++ + if (clock == 0) +- goto out; ++ return; + + /* Workaround to reduce the clock frequency for p1010 esdhc */ + if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { +@@ -238,24 +239,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) + | (pre_div << ESDHC_PREDIV_SHIFT)); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + mdelay(1); +-out: +- host->clock = clock; +-} +- +-#ifdef CONFIG_PM +-static u32 esdhc_proctl; +-static void esdhc_of_suspend(struct sdhci_host *host) +-{ +- esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + } + +-static void esdhc_of_resume(struct sdhci_host *host) +-{ +- esdhc_of_enable_dma(host); +- sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); +-} +-#endif +- + static void esdhc_of_platform_init(struct sdhci_host *host) + { + u32 vvn; +@@ -269,7 +254,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host) + host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; + } + +-static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) ++static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) + { + u32 ctrl; + +@@ -289,8 +274,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) + + clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, + ESDHC_CTRL_BUSWIDTH_MASK, ctrl); +- +- return 0; + } + + static const struct sdhci_ops sdhci_esdhc_ops = { +@@ -305,13 +288,46 @@ static const struct sdhci_ops sdhci_esdhc_ops = { + .get_max_clock = esdhc_of_get_max_clock, + .get_min_clock = esdhc_of_get_min_clock, + .platform_init = esdhc_of_platform_init, +-#ifdef CONFIG_PM +- .platform_suspend = esdhc_of_suspend, +- .platform_resume = esdhc_of_resume, +-#endif + .adma_workaround = esdhci_of_adma_workaround, +- .platform_bus_width = esdhc_pltfm_bus_width, ++ .set_bus_width = esdhc_pltfm_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, ++}; ++ ++#ifdef CONFIG_PM ++ ++static u32 esdhc_proctl; ++static int esdhc_of_suspend(struct device *dev) ++{ ++ struct sdhci_host *host = dev_get_drvdata(dev); ++ ++ esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); ++ ++ return sdhci_suspend_host(host); ++} ++ ++static void esdhc_of_resume(device *dev) ++{ ++ struct sdhci_host *host = dev_get_drvdata(dev); ++ int ret = sdhci_resume_host(host); ++ ++ if (ret == 0) { ++ /* Isn't this already done by sdhci_resume_host() ? --rmk */ ++ esdhc_of_enable_dma(host); ++ sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); ++ } ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops esdhc_pmops = { ++ .suspend = esdhci_of_suspend, ++ .resume = esdhci_of_resume, + }; ++#define ESDHC_PMOPS (&esdhc_pmops) ++#else ++#define ESDHC_PMOPS NULL ++#endif + + static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { + /* +@@ -374,7 +390,7 @@ static struct platform_driver sdhci_esdhc_driver = { + .name = "sdhci-esdhc", + .owner = THIS_MODULE, + .of_match_table = sdhci_esdhc_of_match, +- .pm = SDHCI_PLTFM_PMOPS, ++ .pm = ESDHC_PMOPS, + }, + .probe = sdhci_esdhc_probe, + .remove = sdhci_esdhc_remove, +diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c +index 57c514a81ca5..b341661369a2 100644 +--- a/drivers/mmc/host/sdhci-of-hlwd.c ++++ b/drivers/mmc/host/sdhci-of-hlwd.c +@@ -58,6 +58,10 @@ static const struct sdhci_ops sdhci_hlwd_ops = { + .write_l = sdhci_hlwd_writel, + .write_w = sdhci_hlwd_writew, + .write_b = sdhci_hlwd_writeb, ++ .set_clock = sdhci_set_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static const struct sdhci_pltfm_data sdhci_hlwd_pdata = { +diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c +index fdc612120362..52c42fcc284c 100644 +--- a/drivers/mmc/host/sdhci-pci.c ++++ b/drivers/mmc/host/sdhci-pci.c +@@ -1031,7 +1031,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) + return 0; + } + +-static int sdhci_pci_bus_width(struct sdhci_host *host, int width) ++static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width) + { + u8 ctrl; + +@@ -1052,8 +1052,6 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width) + } + + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +- +- return 0; + } + + static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) +@@ -1080,8 +1078,11 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) + } + + static const struct sdhci_ops sdhci_pci_ops = { ++ .set_clock = sdhci_set_clock, + .enable_dma = sdhci_pci_enable_dma, +- .platform_bus_width = sdhci_pci_bus_width, ++ .set_bus_width = sdhci_pci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + .hw_reset = sdhci_pci_hw_reset, + }; + +diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c +index bef250e95418..7e834fb78f42 100644 +--- a/drivers/mmc/host/sdhci-pltfm.c ++++ b/drivers/mmc/host/sdhci-pltfm.c +@@ -45,6 +45,10 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host) + EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock); + + static const struct sdhci_ops sdhci_pltfm_ops = { ++ .set_clock = sdhci_set_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + #ifdef CONFIG_OF +diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c +index d51e061ec576..3c0f3c0a1cc8 100644 +--- a/drivers/mmc/host/sdhci-pxav2.c ++++ b/drivers/mmc/host/sdhci-pxav2.c +@@ -51,11 +51,13 @@ + #define MMC_CARD 0x1000 + #define MMC_WIDTH 0x0100 + +-static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) ++static void pxav2_reset(struct sdhci_host *host, u8 mask) + { + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + ++ sdhci_reset(host, mask); ++ + if (mask == SDHCI_RESET_ALL) { + u16 tmp = 0; + +@@ -88,7 +90,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) + } + } + +-static int pxav2_mmc_set_width(struct sdhci_host *host, int width) ++static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) + { + u8 ctrl; + u16 tmp; +@@ -107,14 +109,14 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width) + } + writew(tmp, host->ioaddr + SD_CE_ATA_2); + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); +- +- return 0; + } + + static const struct sdhci_ops pxav2_sdhci_ops = { ++ .set_clock = sdhci_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, +- .platform_reset_exit = pxav2_set_private_registers, +- .platform_bus_width = pxav2_mmc_set_width, ++ .set_bus_width = pxav2_mmc_set_bus_width, ++ .reset = pxav2_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + #ifdef CONFIG_OF +diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c +index 2fd73b38c303..f4f128947561 100644 +--- a/drivers/mmc/host/sdhci-pxav3.c ++++ b/drivers/mmc/host/sdhci-pxav3.c +@@ -112,11 +112,13 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, + return 0; + } + +-static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask) ++static void pxav3_reset(struct sdhci_host *host, u8 mask) + { + struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + ++ sdhci_reset(host, mask); ++ + if (mask == SDHCI_RESET_ALL) { + /* + * tune timing of read data/command when crc error happen +@@ -184,7 +186,7 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode) + pxa->power_mode = power_mode; + } + +-static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) ++static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) + { + u16 ctrl_2; + +@@ -218,15 +220,16 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) + dev_dbg(mmc_dev(host->mmc), + "%s uhs = %d, ctrl_2 = %04X\n", + __func__, uhs, ctrl_2); +- +- return 0; + } + + static const struct sdhci_ops pxav3_sdhci_ops = { +- .platform_reset_exit = pxav3_set_private_registers, ++ .set_clock = sdhci_set_clock, + .set_uhs_signaling = pxav3_set_uhs_signaling, + .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = pxav3_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static struct sdhci_pltfm_data sdhci_pxav3_pdata = { +diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c +index d61eb5a70833..76d7c12d8ef9 100644 +--- a/drivers/mmc/host/sdhci-s3c.c ++++ b/drivers/mmc/host/sdhci-s3c.c +@@ -58,6 +58,8 @@ struct sdhci_s3c { + struct clk *clk_io; + struct clk *clk_bus[MAX_BUS_CLK]; + unsigned long clk_rates[MAX_BUS_CLK]; ++ ++ bool no_divider; + }; + + /** +@@ -70,6 +72,7 @@ struct sdhci_s3c { + */ + struct sdhci_s3c_drv_data { + unsigned int sdhci_quirks; ++ bool no_divider; + }; + + static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) +@@ -119,7 +122,7 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, + * If controller uses a non-standard clock division, find the best clock + * speed possible with selected clock source and skip the division. + */ +- if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { ++ if (ourhost->no_divider) { + rate = clk_round_rate(clksrc, wanted); + return wanted - rate; + } +@@ -161,9 +164,13 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) + int src; + u32 ctrl; + ++ host->mmc->actual_clock = 0; ++ + /* don't bother if the clock is going off. */ +- if (clock == 0) ++ if (clock == 0) { ++ sdhci_set_clock(host, clock); + return; ++ } + + for (src = 0; src < MAX_BUS_CLK; src++) { + delta = sdhci_s3c_consider_clock(ourhost, src, clock); +@@ -215,6 +222,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) + if (clock < 25 * 1000000) + ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2); + writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3); ++ ++ sdhci_set_clock(host, clock); + } + + /** +@@ -295,10 +304,11 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) + unsigned long timeout; + u16 clk = 0; + ++ host->mmc->actual_clock = 0; ++ + /* If the clock is going off, set to 0 at clock control register */ + if (clock == 0) { + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); +- host->clock = clock; + return; + } + +@@ -306,8 +316,6 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) + + clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + +- host->clock = clock; +- + clk = SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + +@@ -329,14 +337,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) + } + + /** +- * sdhci_s3c_platform_bus_width - support 8bit buswidth ++ * sdhci_s3c_set_bus_width - support 8bit buswidth + * @host: The SDHCI host being queried + * @width: MMC_BUS_WIDTH_ macro for the bus width being requested + * + * We have 8-bit width support but is not a v3 controller. + * So we add platform_bus_width() and support 8bit width. + */ +-static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) ++static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width) + { + u8 ctrl; + +@@ -358,15 +366,15 @@ static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) + } + + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +- +- return 0; + } + + static struct sdhci_ops sdhci_s3c_ops = { + .get_max_clock = sdhci_s3c_get_max_clk, + .set_clock = sdhci_s3c_set_clock, + .get_min_clock = sdhci_s3c_get_min_clock, +- .platform_bus_width = sdhci_s3c_platform_bus_width, ++ .set_bus_width = sdhci_s3c_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static void sdhci_s3c_notify_change(struct platform_device *dev, int state) +@@ -606,8 +614,10 @@ static int sdhci_s3c_probe(struct platform_device *pdev) + /* Setup quirks for the controller */ + host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; +- if (drv_data) ++ if (drv_data) { + host->quirks |= drv_data->sdhci_quirks; ++ sc->no_divider = drv_data->no_divider; ++ } + + #ifndef CONFIG_MMC_SDHCI_S3C_DMA + +@@ -656,7 +666,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev) + * If controller does not have internal clock divider, + * we can use overriding functions instead of default. + */ +- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { ++ if (sc->no_divider) { + sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; + sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; + sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; +@@ -797,7 +807,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = { + + #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) + static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { +- .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, ++ .no_divider = true, + }; + #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data) + #else +diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c +index 696122c1b468..17004531d089 100644 +--- a/drivers/mmc/host/sdhci-sirf.c ++++ b/drivers/mmc/host/sdhci-sirf.c +@@ -28,7 +28,11 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) + } + + static struct sdhci_ops sdhci_sirf_ops = { ++ .set_clock = sdhci_set_clock, + .get_max_clock = sdhci_sirf_get_max_clk, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static struct sdhci_pltfm_data sdhci_sirf_pdata = { +diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c +index 0316dec3f006..9d535c7336ef 100644 +--- a/drivers/mmc/host/sdhci-spear.c ++++ b/drivers/mmc/host/sdhci-spear.c +@@ -38,7 +38,10 @@ struct spear_sdhci { + + /* sdhci ops */ + static const struct sdhci_ops sdhci_pltfm_ops = { +- /* Nothing to do for now. */ ++ .set_clock = sdhci_set_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + #ifdef CONFIG_OF +diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c +index a835898a68dd..985247649f46 100644 +--- a/drivers/mmc/host/sdhci-tegra.c ++++ b/drivers/mmc/host/sdhci-tegra.c +@@ -48,19 +48,6 @@ struct sdhci_tegra { + int power_gpio; + }; + +-static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) +-{ +- u32 val; +- +- if (unlikely(reg == SDHCI_PRESENT_STATE)) { +- /* Use wp_gpio here instead? */ +- val = readl(host->ioaddr + reg); +- return val | SDHCI_WRITE_PROTECT; +- } +- +- return readl(host->ioaddr + reg); +-} +- + static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) + { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +@@ -108,12 +95,14 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) + return mmc_gpio_get_ro(host->mmc); + } + +-static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) ++static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) + { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; + ++ sdhci_reset(host, mask); ++ + if (!(mask & SDHCI_RESET_ALL)) + return; + +@@ -127,7 +116,7 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) + } + } + +-static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) ++static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) + { + u32 ctrl; + +@@ -144,16 +133,16 @@ static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) + ctrl &= ~SDHCI_CTRL_4BITBUS; + } + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +- return 0; + } + + static const struct sdhci_ops tegra_sdhci_ops = { + .get_ro = tegra_sdhci_get_ro, +- .read_l = tegra_sdhci_readl, + .read_w = tegra_sdhci_readw, + .write_l = tegra_sdhci_writel, +- .platform_bus_width = tegra_sdhci_buswidth, +- .platform_reset_exit = tegra_sdhci_reset_exit, ++ .set_clock = sdhci_set_clock, ++ .set_bus_width = tegra_sdhci_set_bus_width, ++ .reset = tegra_sdhci_reset, ++ .set_uhs_signaling = sdhci_set_uhs_signaling, + }; + + static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { +diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c +index 9a79fc4b60ca..48f4aef034fd 100644 +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -44,6 +44,8 @@ + + #define MAX_TUNING_LOOP 40 + ++#define ADMA_SIZE ((128 * 2 + 1) * 4) ++ + static unsigned int debug_quirks = 0; + static unsigned int debug_quirks2; + +@@ -131,43 +133,26 @@ static void sdhci_dumpregs(struct sdhci_host *host) + * * + \*****************************************************************************/ + +-static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) +-{ +- u32 ier; +- +- ier = sdhci_readl(host, SDHCI_INT_ENABLE); +- ier &= ~clear; +- ier |= set; +- sdhci_writel(host, ier, SDHCI_INT_ENABLE); +- sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); +-} +- +-static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs) +-{ +- sdhci_clear_set_irqs(host, 0, irqs); +-} +- +-static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs) +-{ +- sdhci_clear_set_irqs(host, irqs, 0); +-} +- + static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) + { +- u32 present, irqs; ++ u32 present; + + if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || + (host->mmc->caps & MMC_CAP_NONREMOVABLE)) + return; + +- present = sdhci_readl(host, SDHCI_PRESENT_STATE) & +- SDHCI_CARD_PRESENT; +- irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; ++ if (enable) { ++ present = sdhci_readl(host, SDHCI_PRESENT_STATE) & ++ SDHCI_CARD_PRESENT; + +- if (enable) +- sdhci_unmask_irqs(host, irqs); +- else +- sdhci_mask_irqs(host, irqs); ++ host->ier |= present ? SDHCI_INT_CARD_REMOVE : ++ SDHCI_INT_CARD_INSERT; ++ } else { ++ host->ier &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); ++ } ++ ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + } + + static void sdhci_enable_card_detection(struct sdhci_host *host) +@@ -180,22 +165,9 @@ static void sdhci_disable_card_detection(struct sdhci_host *host) + sdhci_set_card_detection(host, false); + } + +-static void sdhci_reset(struct sdhci_host *host, u8 mask) ++void sdhci_reset(struct sdhci_host *host, u8 mask) + { + unsigned long timeout; +- u32 uninitialized_var(ier); +- +- if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { +- if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & +- SDHCI_CARD_PRESENT)) +- return; +- } +- +- if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) +- ier = sdhci_readl(host, SDHCI_INT_ENABLE); +- +- if (host->ops->platform_reset_enter) +- host->ops->platform_reset_enter(host, mask); + + sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); + +@@ -220,16 +192,27 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) + timeout--; + mdelay(1); + } ++} ++EXPORT_SYMBOL_GPL(sdhci_reset); ++ ++static void sdhci_do_reset(struct sdhci_host *host, u8 mask) ++{ ++ if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { ++ if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & ++ SDHCI_CARD_PRESENT)) ++ return; ++ } + +- if (host->ops->platform_reset_exit) +- host->ops->platform_reset_exit(host, mask); ++ host->ops->reset(host, mask); + +- if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) +- sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); ++ if (mask & SDHCI_RESET_ALL) { ++ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { ++ if (host->ops->enable_dma) ++ host->ops->enable_dma(host); ++ } + +- if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { +- if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) +- host->ops->enable_dma(host); ++ /* Resetting the controller clears many */ ++ host->preset_enabled = false; + } + } + +@@ -238,15 +221,18 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); + static void sdhci_init(struct sdhci_host *host, int soft) + { + if (soft) +- sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); ++ sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); + else +- sdhci_reset(host, SDHCI_RESET_ALL); ++ sdhci_do_reset(host, SDHCI_RESET_ALL); ++ ++ host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | ++ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | ++ SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | ++ SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | ++ SDHCI_INT_RESPONSE; + +- sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, +- SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | +- SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | +- SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | +- SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE); ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + + if (soft) { + /* force clock reconfiguration */ +@@ -502,11 +488,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, + else + direction = DMA_TO_DEVICE; + +- /* +- * The ADMA descriptor table is mapped further down as we +- * need to fill it with data first. +- */ +- + host->align_addr = dma_map_single(mmc_dev(host->mmc), + host->align_buffer, 128 * 4, direction); + if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) +@@ -567,7 +548,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, + * If this triggers then we have a calculation bug + * somewhere. :/ + */ +- WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4); ++ WARN_ON((desc - host->adma_desc) > ADMA_SIZE); + } + + if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { +@@ -595,17 +576,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, + host->align_addr, 128 * 4, direction); + } + +- host->adma_addr = dma_map_single(mmc_dev(host->mmc), +- host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); +- if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr)) +- goto unmap_entries; +- BUG_ON(host->adma_addr & 0x3); +- + return 0; + +-unmap_entries: +- dma_unmap_sg(mmc_dev(host->mmc), data->sg, +- data->sg_len, direction); + unmap_align: + dma_unmap_single(mmc_dev(host->mmc), host->align_addr, + 128 * 4, direction); +@@ -623,19 +595,25 @@ static void sdhci_adma_table_post(struct sdhci_host *host, + u8 *align; + char *buffer; + unsigned long flags; ++ bool has_unaligned; + + if (data->flags & MMC_DATA_READ) + direction = DMA_FROM_DEVICE; + else + direction = DMA_TO_DEVICE; + +- dma_unmap_single(mmc_dev(host->mmc), host->adma_addr, +- (128 * 2 + 1) * 4, DMA_TO_DEVICE); +- + dma_unmap_single(mmc_dev(host->mmc), host->align_addr, + 128 * 4, direction); + +- if (data->flags & MMC_DATA_READ) { ++ /* Do a quick scan of the SG list for any unaligned mappings */ ++ has_unaligned = false; ++ for_each_sg(data->sg, sg, host->sg_count, i) ++ if (sg_dma_address(sg) & 3) { ++ has_unaligned = true; ++ break; ++ } ++ ++ if (has_unaligned && data->flags & MMC_DATA_READ) { + dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, + data->sg_len, direction); + +@@ -721,9 +699,12 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) + u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; + + if (host->flags & SDHCI_REQ_USE_DMA) +- sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); ++ host->ier = (host->ier & ~pio_irqs) | dma_irqs; + else +- sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); ++ host->ier = (host->ier & ~dma_irqs) | pio_irqs; ++ ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + } + + static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) +@@ -976,8 +957,8 @@ static void sdhci_finish_data(struct sdhci_host *host) + * upon error conditions. + */ + if (data->error) { +- sdhci_reset(host, SDHCI_RESET_CMD); +- sdhci_reset(host, SDHCI_RESET_DATA); ++ sdhci_do_reset(host, SDHCI_RESET_CMD); ++ sdhci_do_reset(host, SDHCI_RESET_DATA); + } + + sdhci_send_command(host, data->stop); +@@ -1107,24 +1088,23 @@ static void sdhci_finish_command(struct sdhci_host *host) + + static u16 sdhci_get_preset_value(struct sdhci_host *host) + { +- u16 ctrl, preset = 0; ++ u16 preset = 0; + +- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- +- switch (ctrl & SDHCI_CTRL_UHS_MASK) { +- case SDHCI_CTRL_UHS_SDR12: ++ switch (host->timing) { ++ case MMC_TIMING_UHS_SDR12: + preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12); + break; +- case SDHCI_CTRL_UHS_SDR25: ++ case MMC_TIMING_UHS_SDR25: + preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25); + break; +- case SDHCI_CTRL_UHS_SDR50: ++ case MMC_TIMING_UHS_SDR50: + preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50); + break; +- case SDHCI_CTRL_UHS_SDR104: ++ case MMC_TIMING_UHS_SDR104: ++ case MMC_TIMING_MMC_HS200: + preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104); + break; +- case SDHCI_CTRL_UHS_DDR50: ++ case MMC_TIMING_UHS_DDR50: + preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); + break; + default: +@@ -1136,32 +1116,22 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) + return preset; + } + +-static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) ++void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) + { + int div = 0; /* Initialized for compiler warning */ + int real_div = div, clk_mul = 1; + u16 clk = 0; + unsigned long timeout; + +- if (clock && clock == host->clock) +- return; +- + host->mmc->actual_clock = 0; + +- if (host->ops->set_clock) { +- host->ops->set_clock(host, clock); +- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) +- return; +- } +- + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) +- goto out; ++ return; + + if (host->version >= SDHCI_SPEC_300) { +- if (sdhci_readw(host, SDHCI_HOST_CONTROL2) & +- SDHCI_CTRL_PRESET_VAL_ENABLE) { ++ if (host->preset_enabled) { + u16 pre_val; + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); +@@ -1247,26 +1217,16 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +- +-out: +- host->clock = clock; +-} +- +-static inline void sdhci_update_clock(struct sdhci_host *host) +-{ +- unsigned int clock; +- +- clock = host->clock; +- host->clock = 0; +- sdhci_set_clock(host, clock); + } ++EXPORT_SYMBOL_GPL(sdhci_set_clock); + +-static int sdhci_set_power(struct sdhci_host *host, unsigned short power) ++static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, ++ unsigned short vdd) + { + u8 pwr = 0; + +- if (power != (unsigned short)-1) { +- switch (1 << power) { ++ if (mode != MMC_POWER_OFF) { ++ switch (1 << vdd) { + case MMC_VDD_165_195: + pwr = SDHCI_POWER_180; + break; +@@ -1284,7 +1244,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) + } + + if (host->pwr == pwr) +- return -1; ++ return; + + host->pwr = pwr; + +@@ -1292,38 +1252,43 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_off(host); +- return 0; +- } +- +- /* +- * Spec says that we should clear the power reg before setting +- * a new value. Some controllers don't seem to like this though. +- */ +- if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) +- sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); ++ vdd = 0; ++ } else { ++ /* ++ * Spec says that we should clear the power reg before setting ++ * a new value. Some controllers don't seem to like this though. ++ */ ++ if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) ++ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + +- /* +- * At least the Marvell CaFe chip gets confused if we set the voltage +- * and set turn on power at the same time, so set the voltage first. +- */ +- if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) +- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); ++ /* ++ * At least the Marvell CaFe chip gets confused if we set the ++ * voltage and set turn on power at the same time, so set the ++ * voltage first. ++ */ ++ if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) ++ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + +- pwr |= SDHCI_POWER_ON; ++ pwr |= SDHCI_POWER_ON; + +- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); ++ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + +- if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) +- sdhci_runtime_pm_bus_on(host); ++ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) ++ sdhci_runtime_pm_bus_on(host); + +- /* +- * Some controllers need an extra 10ms delay of 10ms before they +- * can apply clock after applying power +- */ +- if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) +- mdelay(10); ++ /* ++ * Some controllers need an extra 10ms delay of 10ms before ++ * they can apply clock after applying power ++ */ ++ if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) ++ mdelay(10); ++ } + +- return power; ++ if (host->vmmc) { ++ spin_unlock_irq(&host->lock); ++ mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd); ++ spin_lock_irq(&host->lock); ++ } + } + + /*****************************************************************************\ +@@ -1427,10 +1392,52 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) + spin_unlock_irqrestore(&host->lock, flags); + } + ++void sdhci_set_bus_width(struct sdhci_host *host, int width) ++{ ++ u8 ctrl; ++ ++ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ++ if (width == MMC_BUS_WIDTH_8) { ++ ctrl &= ~SDHCI_CTRL_4BITBUS; ++ if (host->version >= SDHCI_SPEC_300) ++ ctrl |= SDHCI_CTRL_8BITBUS; ++ } else { ++ if (host->version >= SDHCI_SPEC_300) ++ ctrl &= ~SDHCI_CTRL_8BITBUS; ++ if (width == MMC_BUS_WIDTH_4) ++ ctrl |= SDHCI_CTRL_4BITBUS; ++ else ++ ctrl &= ~SDHCI_CTRL_4BITBUS; ++ } ++ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++} ++EXPORT_SYMBOL_GPL(sdhci_set_bus_width); ++ ++void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) ++{ ++ u16 ctrl_2; ++ ++ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ /* Select Bus Speed Mode for host */ ++ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; ++ if ((timing == MMC_TIMING_MMC_HS200) || ++ (timing == MMC_TIMING_UHS_SDR104)) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR104; ++ else if (timing == MMC_TIMING_UHS_SDR12) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR12; ++ else if (timing == MMC_TIMING_UHS_SDR25) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR25; ++ else if (timing == MMC_TIMING_UHS_SDR50) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR50; ++ else if (timing == MMC_TIMING_UHS_DDR50) ++ ctrl_2 |= SDHCI_CTRL_UHS_DDR50; ++ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); ++} ++EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); ++ + static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + { + unsigned long flags; +- int vdd_bit = -1; + u8 ctrl; + + spin_lock_irqsave(&host->lock, flags); +@@ -1456,45 +1463,17 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) + sdhci_enable_preset_value(host, false); + +- sdhci_set_clock(host, ios->clock); +- +- if (ios->power_mode == MMC_POWER_OFF) +- vdd_bit = sdhci_set_power(host, -1); +- else +- vdd_bit = sdhci_set_power(host, ios->vdd); +- +- if (host->vmmc && vdd_bit != -1) { +- spin_unlock_irqrestore(&host->lock, flags); +- mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); +- spin_lock_irqsave(&host->lock, flags); ++ if (!ios->clock || ios->clock != host->clock) { ++ host->ops->set_clock(host, ios->clock); ++ host->clock = ios->clock; + } + ++ sdhci_set_power(host, ios->power_mode, ios->vdd); ++ + if (host->ops->platform_send_init_74_clocks) + host->ops->platform_send_init_74_clocks(host, ios->power_mode); + +- /* +- * If your platform has 8-bit width support but is not a v3 controller, +- * or if it requires special setup code, you should implement that in +- * platform_bus_width(). +- */ +- if (host->ops->platform_bus_width) { +- host->ops->platform_bus_width(host, ios->bus_width); +- } else { +- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); +- if (ios->bus_width == MMC_BUS_WIDTH_8) { +- ctrl &= ~SDHCI_CTRL_4BITBUS; +- if (host->version >= SDHCI_SPEC_300) +- ctrl |= SDHCI_CTRL_8BITBUS; +- } else { +- if (host->version >= SDHCI_SPEC_300) +- ctrl &= ~SDHCI_CTRL_8BITBUS; +- if (ios->bus_width == MMC_BUS_WIDTH_4) +- ctrl |= SDHCI_CTRL_4BITBUS; +- else +- ctrl &= ~SDHCI_CTRL_4BITBUS; +- } +- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +- } ++ host->ops->set_bus_width(host, ios->bus_width); + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + +@@ -1516,13 +1495,13 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + (ios->timing == MMC_TIMING_UHS_SDR25)) + ctrl |= SDHCI_CTRL_HISPD; + +- ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) { ++ if (!host->preset_enabled) { + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + /* + * We only need to set Driver Strength if the + * preset value enable is not set. + */ ++ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; + if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) + ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; +@@ -1546,34 +1525,16 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + + /* Re-enable SD Clock */ +- sdhci_update_clock(host); ++ host->ops->set_clock(host, host->clock); + } + +- + /* Reset SD Clock Enable */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + +- if (host->ops->set_uhs_signaling) +- host->ops->set_uhs_signaling(host, ios->timing); +- else { +- ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- /* Select Bus Speed Mode for host */ +- ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; +- if ((ios->timing == MMC_TIMING_MMC_HS200) || +- (ios->timing == MMC_TIMING_UHS_SDR104)) +- ctrl_2 |= SDHCI_CTRL_UHS_SDR104; +- else if (ios->timing == MMC_TIMING_UHS_SDR12) +- ctrl_2 |= SDHCI_CTRL_UHS_SDR12; +- else if (ios->timing == MMC_TIMING_UHS_SDR25) +- ctrl_2 |= SDHCI_CTRL_UHS_SDR25; +- else if (ios->timing == MMC_TIMING_UHS_SDR50) +- ctrl_2 |= SDHCI_CTRL_UHS_SDR50; +- else if (ios->timing == MMC_TIMING_UHS_DDR50) +- ctrl_2 |= SDHCI_CTRL_UHS_DDR50; +- sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +- } ++ host->ops->set_uhs_signaling(host, ios->timing); ++ host->timing = ios->timing; + + if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && + ((ios->timing == MMC_TIMING_UHS_SDR12) || +@@ -1590,7 +1551,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + } + + /* Re-enable SD Clock */ +- sdhci_update_clock(host); ++ host->ops->set_clock(host, host->clock); + } else + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + +@@ -1600,7 +1561,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + * it on each ios seems to solve the problem. + */ + if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) +- sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); ++ sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); +@@ -1709,24 +1670,16 @@ static int sdhci_get_ro(struct mmc_host *mmc) + + static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) + { +- if (host->flags & SDHCI_DEVICE_DEAD) +- goto out; +- +- if (enable) +- host->flags |= SDHCI_SDIO_IRQ_ENABLED; +- else +- host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; +- +- /* SDIO IRQ will be enabled as appropriate in runtime resume */ +- if (host->runtime_suspended) +- goto out; ++ if (!(host->flags & SDHCI_DEVICE_DEAD)) { ++ if (enable) ++ host->ier |= SDHCI_INT_CARD_INT; ++ else ++ host->ier &= ~SDHCI_INT_CARD_INT; + +- if (enable) +- sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); +- else +- sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); +-out: +- mmiowb(); ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ mmiowb(); ++ } + } + + static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) +@@ -1734,9 +1687,18 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + ++ sdhci_runtime_pm_get(host); ++ + spin_lock_irqsave(&host->lock, flags); ++ if (enable) ++ host->flags |= SDHCI_SDIO_IRQ_ENABLED; ++ else ++ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; ++ + sdhci_enable_sdio_irq_nolock(host, enable); + spin_unlock_irqrestore(&host->lock, flags); ++ ++ sdhci_runtime_pm_put(host); + } + + static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, +@@ -1798,9 +1760,6 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, + ctrl |= SDHCI_CTRL_VDD_180; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + +- /* Wait for 5ms */ +- usleep_range(5000, 5500); +- + /* 1.8V regulator output should be stable within 5 ms */ + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (ctrl & SDHCI_CTRL_VDD_180) +@@ -1855,22 +1814,16 @@ static int sdhci_card_busy(struct mmc_host *mmc) + + static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + { +- struct sdhci_host *host; ++ struct sdhci_host *host = mmc_priv(mmc); + u16 ctrl; +- u32 ier; + int tuning_loop_counter = MAX_TUNING_LOOP; + unsigned long timeout; + int err = 0; +- bool requires_tuning_nonuhs = false; + unsigned long flags; + +- host = mmc_priv(mmc); +- + sdhci_runtime_pm_get(host); + spin_lock_irqsave(&host->lock, flags); + +- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- + /* + * The Host Controller needs tuning only in case of SDR104 mode + * and for SDR50 mode when Use Tuning for SDR50 is set in the +@@ -1878,15 +1831,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + * If the Host Controller supports the HS200 mode then the + * tuning function has to be executed. + */ +- if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && +- (host->flags & SDHCI_SDR50_NEEDS_TUNING || +- host->flags & SDHCI_SDR104_NEEDS_TUNING)) +- requires_tuning_nonuhs = true; +- +- if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || +- requires_tuning_nonuhs) +- ctrl |= SDHCI_CTRL_EXEC_TUNING; +- else { ++ switch (host->timing) { ++ case MMC_TIMING_MMC_HS200: ++ case MMC_TIMING_UHS_SDR104: ++ break; ++ ++ case MMC_TIMING_UHS_SDR50: ++ if (host->flags & SDHCI_SDR50_NEEDS_TUNING || ++ host->flags & SDHCI_SDR104_NEEDS_TUNING) ++ break; ++ /* FALLTHROUGH */ ++ ++ default: + spin_unlock_irqrestore(&host->lock, flags); + sdhci_runtime_pm_put(host); + return 0; +@@ -1899,6 +1855,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + return err; + } + ++ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ ctrl |= SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + /* +@@ -1911,8 +1869,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + * to make sure we don't hit a controller bug, we _only_ + * enable Buffer Read Ready interrupt here. + */ +- ier = sdhci_readl(host, SDHCI_INT_ENABLE); +- sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL); ++ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); ++ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); + + /* + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number +@@ -2044,7 +2002,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + if (err && (host->flags & SDHCI_USING_RETUNING_TIMER)) + err = 0; + +- sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + spin_unlock_irqrestore(&host->lock, flags); + sdhci_runtime_pm_put(host); + +@@ -2054,26 +2013,30 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + + static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) + { +- u16 ctrl; +- + /* Host Controller v3.00 defines preset value registers */ + if (host->version < SDHCI_SPEC_300) + return; + +- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- + /* + * We only enable or disable Preset Value if they are not already + * enabled or disabled respectively. Otherwise, we bail out. + */ +- if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { +- ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; +- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); +- host->flags |= SDHCI_PV_ENABLED; +- } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { +- ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; ++ if (host->preset_enabled != enable) { ++ u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ ++ if (enable) ++ ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; ++ else ++ ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; ++ + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); +- host->flags &= ~SDHCI_PV_ENABLED; ++ ++ if (enable) ++ host->flags |= SDHCI_PV_ENABLED; ++ else ++ host->flags &= ~SDHCI_PV_ENABLED; ++ ++ host->preset_enabled = enable; + } + } + +@@ -2095,8 +2058,8 @@ static void sdhci_card_event(struct mmc_host *mmc) + pr_err("%s: Resetting controller.\n", + mmc_hostname(host->mmc)); + +- sdhci_reset(host, SDHCI_RESET_CMD); +- sdhci_reset(host, SDHCI_RESET_DATA); ++ sdhci_do_reset(host, SDHCI_RESET_CMD); ++ sdhci_do_reset(host, SDHCI_RESET_DATA); + + host->mrq->cmd->error = -ENOMEDIUM; + tasklet_schedule(&host->finish_tasklet); +@@ -2124,15 +2087,6 @@ static const struct mmc_host_ops sdhci_ops = { + * * + \*****************************************************************************/ + +-static void sdhci_tasklet_card(unsigned long param) +-{ +- struct sdhci_host *host = (struct sdhci_host*)param; +- +- sdhci_card_event(host->mmc); +- +- mmc_detect_change(host->mmc, msecs_to_jiffies(200)); +-} +- + static void sdhci_tasklet_finish(unsigned long param) + { + struct sdhci_host *host; +@@ -2169,12 +2123,12 @@ static void sdhci_tasklet_finish(unsigned long param) + /* Some controllers need this kick or reset won't work here */ + if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) + /* This is to force an update */ +- sdhci_update_clock(host); ++ host->ops->set_clock(host, host->clock); + + /* Spec says we should do both at the same time, but Ricoh + controllers do not like that. */ +- sdhci_reset(host, SDHCI_RESET_CMD); +- sdhci_reset(host, SDHCI_RESET_DATA); ++ sdhci_do_reset(host, SDHCI_RESET_CMD); ++ sdhci_do_reset(host, SDHCI_RESET_DATA); + } + + host->mrq = NULL; +@@ -2424,101 +2378,94 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) + + static irqreturn_t sdhci_irq(int irq, void *dev_id) + { +- irqreturn_t result; ++ irqreturn_t result = IRQ_NONE; + struct sdhci_host *host = dev_id; +- u32 intmask, unexpected = 0; +- int cardint = 0, max_loops = 16; ++ u32 intmask, mask, unexpected = 0; ++ int max_loops = 16; + + spin_lock(&host->lock); + +- if (host->runtime_suspended) { ++ if (host->runtime_suspended && !sdhci_sdio_irq_enabled(host)) { + spin_unlock(&host->lock); + return IRQ_NONE; + } + + intmask = sdhci_readl(host, SDHCI_INT_STATUS); +- + if (!intmask || intmask == 0xffffffff) { + result = IRQ_NONE; + goto out; + } + +-again: +- DBG("*** %s got interrupt: 0x%08x\n", +- mmc_hostname(host->mmc), intmask); +- +- if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { +- u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & +- SDHCI_CARD_PRESENT; +- +- /* +- * There is a observation on i.mx esdhc. INSERT bit will be +- * immediately set again when it gets cleared, if a card is +- * inserted. We have to mask the irq to prevent interrupt +- * storm which will freeze the system. And the REMOVE gets +- * the same situation. +- * +- * More testing are needed here to ensure it works for other +- * platforms though. +- */ +- sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT : +- SDHCI_INT_CARD_REMOVE); +- sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE : +- SDHCI_INT_CARD_INSERT); ++ do { ++ /* Clear selected interrupts. */ ++ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_BUS_POWER); ++ sdhci_writel(host, mask, SDHCI_INT_STATUS); + +- sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | +- SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); +- intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); +- tasklet_schedule(&host->card_tasklet); +- } ++ DBG("*** %s got interrupt: 0x%08x\n", ++ mmc_hostname(host->mmc), intmask); + +- if (intmask & SDHCI_INT_CMD_MASK) { +- sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, +- SDHCI_INT_STATUS); +- sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); +- } ++ if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { ++ u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & ++ SDHCI_CARD_PRESENT; + +- if (intmask & SDHCI_INT_DATA_MASK) { +- sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK, +- SDHCI_INT_STATUS); +- sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); +- } +- +- intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); ++ /* ++ * There is a observation on i.mx esdhc. INSERT ++ * bit will be immediately set again when it gets ++ * cleared, if a card is inserted. We have to mask ++ * the irq to prevent interrupt storm which will ++ * freeze the system. And the REMOVE gets the ++ * same situation. ++ * ++ * More testing are needed here to ensure it works ++ * for other platforms though. ++ */ ++ host->ier &= ~(SDHCI_INT_CARD_INSERT | ++ SDHCI_INT_CARD_REMOVE); ++ host->ier |= present ? SDHCI_INT_CARD_REMOVE : ++ SDHCI_INT_CARD_INSERT; ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ ++ sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | ++ SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); ++ ++ host->thread_isr |= intmask & (SDHCI_INT_CARD_INSERT | ++ SDHCI_INT_CARD_REMOVE); ++ result = IRQ_WAKE_THREAD; ++ } + +- intmask &= ~SDHCI_INT_ERROR; ++ if (intmask & SDHCI_INT_CMD_MASK) ++ sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); + +- if (intmask & SDHCI_INT_BUS_POWER) { +- pr_err("%s: Card is consuming too much power!\n", +- mmc_hostname(host->mmc)); +- sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS); +- } ++ if (intmask & SDHCI_INT_DATA_MASK) ++ sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); + +- intmask &= ~SDHCI_INT_BUS_POWER; ++ if (intmask & SDHCI_INT_BUS_POWER) ++ pr_err("%s: Card is consuming too much power!\n", ++ mmc_hostname(host->mmc)); + +- if (intmask & SDHCI_INT_CARD_INT) +- cardint = 1; ++ if (intmask & SDHCI_INT_CARD_INT) { ++ sdhci_enable_sdio_irq_nolock(host, false); ++ host->thread_isr |= SDHCI_INT_CARD_INT; ++ result = IRQ_WAKE_THREAD; ++ } + +- intmask &= ~SDHCI_INT_CARD_INT; ++ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | ++ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | ++ SDHCI_INT_CARD_INT); + +- if (intmask) { +- unexpected |= intmask; +- sdhci_writel(host, intmask, SDHCI_INT_STATUS); +- } +- +- result = IRQ_HANDLED; ++ if (intmask) { ++ unexpected |= intmask; ++ sdhci_writel(host, intmask, SDHCI_INT_STATUS); ++ } + +- intmask = sdhci_readl(host, SDHCI_INT_STATUS); ++ if (result == IRQ_NONE) ++ result = IRQ_HANDLED; + +- /* +- * If we know we'll call the driver to signal SDIO IRQ, disregard +- * further indications of Card Interrupt in the status to avoid a +- * needless loop. +- */ +- if (cardint) +- intmask &= ~SDHCI_INT_CARD_INT; +- if (intmask && --max_loops) +- goto again; ++ intmask = sdhci_readl(host, SDHCI_INT_STATUS); ++ } while (intmask && --max_loops); + out: + spin_unlock(&host->lock); + +@@ -2527,15 +2474,38 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) + mmc_hostname(host->mmc), unexpected); + sdhci_dumpregs(host); + } +- /* +- * We have to delay this as it calls back into the driver. +- */ +- if (cardint) +- mmc_signal_sdio_irq(host->mmc); + + return result; + } + ++static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) ++{ ++ struct sdhci_host *host = dev_id; ++ unsigned long flags; ++ u32 isr; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ isr = host->thread_isr; ++ host->thread_isr = 0; ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { ++ sdhci_card_event(host->mmc); ++ mmc_detect_change(host->mmc, msecs_to_jiffies(200)); ++ } ++ ++ if (isr & SDHCI_INT_CARD_INT) { ++ sdio_run_irqs(host->mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) ++ sdhci_enable_sdio_irq_nolock(host, true); ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ ++ return isr ? IRQ_HANDLED : IRQ_NONE; ++} ++ + /*****************************************************************************\ + * * + * Suspend/resume * +@@ -2572,9 +2542,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); + + int sdhci_suspend_host(struct sdhci_host *host) + { +- if (host->ops->platform_suspend) +- host->ops->platform_suspend(host); +- + sdhci_disable_card_detection(host); + + /* Disable tuning since we are suspending */ +@@ -2584,7 +2551,9 @@ int sdhci_suspend_host(struct sdhci_host *host) + } + + if (!device_may_wakeup(mmc_dev(host->mmc))) { +- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); ++ host->ier = 0; ++ sdhci_writel(host, 0, SDHCI_INT_ENABLE); ++ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + free_irq(host->irq, host); + } else { + sdhci_enable_irq_wakeups(host); +@@ -2605,8 +2574,9 @@ int sdhci_resume_host(struct sdhci_host *host) + } + + if (!device_may_wakeup(mmc_dev(host->mmc))) { +- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, +- mmc_hostname(host->mmc), host); ++ ret = request_threaded_irq(host->irq, sdhci_irq, ++ sdhci_thread_irq, IRQF_SHARED, ++ mmc_hostname(host->mmc), host); + if (ret) + return ret; + } else { +@@ -2628,9 +2598,6 @@ int sdhci_resume_host(struct sdhci_host *host) + + sdhci_enable_card_detection(host); + +- if (host->ops->platform_resume) +- host->ops->platform_resume(host); +- + /* Set the re-tuning expiration flag */ + if (host->flags & SDHCI_USING_RETUNING_TIMER) + host->flags |= SDHCI_NEEDS_RETUNING; +@@ -2682,10 +2649,12 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) + } + + spin_lock_irqsave(&host->lock, flags); +- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); ++ host->ier &= SDHCI_INT_CARD_INT; ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + spin_unlock_irqrestore(&host->lock, flags); + +- synchronize_irq(host->irq); ++ synchronize_hardirq(host->irq); + + spin_lock_irqsave(&host->lock, flags); + host->runtime_suspended = true; +@@ -2729,7 +2698,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) + host->runtime_suspended = false; + + /* Enable SDIO IRQ */ +- if ((host->flags & SDHCI_SDIO_IRQ_ENABLED)) ++ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) + sdhci_enable_sdio_irq_nolock(host, true); + + /* Enable Card Detection */ +@@ -2788,7 +2757,7 @@ int sdhci_add_host(struct sdhci_host *host) + if (debug_quirks2) + host->quirks2 = debug_quirks2; + +- sdhci_reset(host, SDHCI_RESET_ALL); ++ sdhci_do_reset(host, SDHCI_RESET_ALL); + + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + host->version = (host->version & SDHCI_SPEC_VER_MASK) +@@ -2848,15 +2817,29 @@ int sdhci_add_host(struct sdhci_host *host) + * (128) and potentially one alignment transfer for + * each of those entries. + */ +- host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL); ++ host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc), ++ ADMA_SIZE, &host->adma_addr, ++ GFP_KERNEL); + host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); + if (!host->adma_desc || !host->align_buffer) { +- kfree(host->adma_desc); ++ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, ++ host->adma_desc, host->adma_addr); + kfree(host->align_buffer); + pr_warning("%s: Unable to allocate ADMA " + "buffers. Falling back to standard DMA.\n", + mmc_hostname(mmc)); + host->flags &= ~SDHCI_USE_ADMA; ++ host->adma_desc = NULL; ++ host->align_buffer = NULL; ++ } else if (host->adma_addr & 3) { ++ pr_warning("%s: unable to allocate aligned ADMA descriptor\n", ++ mmc_hostname(mmc)); ++ host->flags &= ~SDHCI_USE_ADMA; ++ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, ++ host->adma_desc, host->adma_addr); ++ kfree(host->align_buffer); ++ host->adma_desc = NULL; ++ host->align_buffer = NULL; + } + } + +@@ -2941,6 +2924,7 @@ int sdhci_add_host(struct sdhci_host *host) + mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; + + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; ++ mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + + if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) + host->flags |= SDHCI_AUTO_CMD12; +@@ -3212,8 +3196,6 @@ int sdhci_add_host(struct sdhci_host *host) + /* + * Init tasklets. + */ +- tasklet_init(&host->card_tasklet, +- sdhci_tasklet_card, (unsigned long)host); + tasklet_init(&host->finish_tasklet, + sdhci_tasklet_finish, (unsigned long)host); + +@@ -3230,8 +3212,8 @@ int sdhci_add_host(struct sdhci_host *host) + + sdhci_init(host, 0); + +- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, +- mmc_hostname(mmc), host); ++ ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, ++ IRQF_SHARED, mmc_hostname(mmc), host); + if (ret) { + pr_err("%s: Failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); +@@ -3273,12 +3255,12 @@ int sdhci_add_host(struct sdhci_host *host) + + #ifdef SDHCI_USE_LEDS_CLASS + reset: +- sdhci_reset(host, SDHCI_RESET_ALL); +- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); ++ sdhci_do_reset(host, SDHCI_RESET_ALL); ++ sdhci_writel(host, 0, SDHCI_INT_ENABLE); ++ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + free_irq(host->irq, host); + #endif + untasklet: +- tasklet_kill(&host->card_tasklet); + tasklet_kill(&host->finish_tasklet); + + return ret; +@@ -3315,14 +3297,14 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) + #endif + + if (!dead) +- sdhci_reset(host, SDHCI_RESET_ALL); ++ sdhci_do_reset(host, SDHCI_RESET_ALL); + +- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); ++ sdhci_writel(host, 0, SDHCI_INT_ENABLE); ++ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + free_irq(host->irq, host); + + del_timer_sync(&host->timer); + +- tasklet_kill(&host->card_tasklet); + tasklet_kill(&host->finish_tasklet); + + if (host->vmmc) { +@@ -3335,7 +3317,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) + regulator_put(host->vqmmc); + } + +- kfree(host->adma_desc); ++ if (host->adma_desc) ++ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, ++ host->adma_desc, host->adma_addr); + kfree(host->align_buffer); + + host->adma_desc = NULL; +diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h +index 0a3ed01887db..4a5cd5e3fa3e 100644 +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -281,18 +281,14 @@ struct sdhci_ops { + unsigned int (*get_max_clock)(struct sdhci_host *host); + unsigned int (*get_min_clock)(struct sdhci_host *host); + unsigned int (*get_timeout_clock)(struct sdhci_host *host); +- int (*platform_bus_width)(struct sdhci_host *host, +- int width); ++ void (*set_bus_width)(struct sdhci_host *host, int width); + void (*platform_send_init_74_clocks)(struct sdhci_host *host, + u8 power_mode); + unsigned int (*get_ro)(struct sdhci_host *host); +- void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); +- void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); ++ void (*reset)(struct sdhci_host *host, u8 mask); + int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); +- int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); ++ void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); + void (*hw_reset)(struct sdhci_host *host); +- void (*platform_suspend)(struct sdhci_host *host); +- void (*platform_resume)(struct sdhci_host *host); + void (*adma_workaround)(struct sdhci_host *host, u32 intmask); + void (*platform_init)(struct sdhci_host *host); + void (*card_event)(struct sdhci_host *host); +@@ -397,6 +393,16 @@ extern void sdhci_remove_host(struct sdhci_host *host, int dead); + extern void sdhci_send_command(struct sdhci_host *host, + struct mmc_command *cmd); + ++static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) ++{ ++ return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); ++} ++ ++void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); ++void sdhci_set_bus_width(struct sdhci_host *host, int width); ++void sdhci_reset(struct sdhci_host *host, u8 mask); ++void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing); ++ + #ifdef CONFIG_PM + extern int sdhci_suspend_host(struct sdhci_host *host); + extern int sdhci_resume_host(struct sdhci_host *host); +diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c +index 7c397bb81e01..6da2fbbb9bd5 100644 +--- a/drivers/regulator/anatop-regulator.c ++++ b/drivers/regulator/anatop-regulator.c +@@ -267,6 +267,7 @@ static int anatop_regulator_probe(struct platform_device *pdev) + config.driver_data = sreg; + config.of_node = pdev->dev.of_node; + config.regmap = sreg->anatop; ++ config.ena_gpio = -EINVAL; + + /* Only core regulators have the ramp up delay configuration. */ + if (sreg->control_reg && sreg->delay_bit_width) { +diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c +index 9a09f3cdbabb..19c4077374b6 100644 +--- a/drivers/regulator/core.c ++++ b/drivers/regulator/core.c +@@ -3459,7 +3459,7 @@ regulator_register(const struct regulator_desc *regulator_desc, + + dev_set_drvdata(&rdev->dev, rdev); + +- if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) { ++ if (gpio_is_valid(config->ena_gpio)) { + ret = regulator_ena_gpio_request(rdev, config); + if (ret != 0) { + rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", +diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c +index 2436db9e2ca3..acc4ea593c95 100644 +--- a/drivers/regulator/dummy.c ++++ b/drivers/regulator/dummy.c +@@ -48,6 +48,7 @@ static int dummy_regulator_probe(struct platform_device *pdev) + + config.dev = &pdev->dev; + config.init_data = &dummy_initdata; ++ config.ena_gpio = -EINVAL; + + dummy_regulator_rdev = regulator_register(&dummy_desc, &config); + if (IS_ERR(dummy_regulator_rdev)) { +diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c +index c61f7e97e4f8..aabf10dc8a50 100644 +--- a/drivers/regulator/fixed.c ++++ b/drivers/regulator/fixed.c +@@ -161,9 +161,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) + drvdata->desc.n_voltages = 1; + + drvdata->desc.fixed_uV = config->microvolts; +- +- if (config->gpio >= 0) +- cfg.ena_gpio = config->gpio; ++ cfg.ena_gpio = config->gpio; + cfg.ena_gpio_invert = !config->enable_high; + if (config->enabled_at_boot) { + if (config->enable_high) +diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig +index c6e8ba7b3e4e..52516a7cf26f 100644 +--- a/drivers/staging/imx-drm/Kconfig ++++ b/drivers/staging/imx-drm/Kconfig +@@ -35,6 +35,7 @@ config DRM_IMX_TVE + config DRM_IMX_LDB + tristate "Support for LVDS displays" + depends on DRM_IMX && MFD_SYSCON ++ select DRM_PANEL + help + Choose this to enable the internal LVDS Display Bridge (LDB) + found on i.MX53 and i.MX6 processors. +@@ -60,3 +61,20 @@ config DRM_IMX_HDMI + depends on DRM_IMX + help + Choose this if you want to use HDMI on i.MX6. ++ ++config DRM_DW_HDMI_AUDIO ++ tristate "Synopsis Designware Audio interface" ++ depends on DRM_IMX_HDMI != n ++ help ++ Support the Audio interface which is part of the Synopsis ++ Designware HDMI block. This is used in conjunction with ++ the i.MX HDMI driver. ++ ++config DRM_DW_HDMI_CEC ++ tristate "Synopsis Designware CEC interface" ++ depends on DRM_IMX_HDMI != n ++ select HDMI_CEC_CORE ++ help ++ Support the CEC interface which is part of the Synposis ++ Designware HDMI block. This is used in conjunction with ++ the i.MX HDMI driver. +diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile +index 129e3a3f59f1..5eabd5e4e456 100644 +--- a/drivers/staging/imx-drm/Makefile ++++ b/drivers/staging/imx-drm/Makefile +@@ -3,6 +3,7 @@ imxdrm-objs := imx-drm-core.o + + obj-$(CONFIG_DRM_IMX) += imxdrm.o + ++obj-$(CONFIG_DRM_IMX) += drm-ddc-connector.o + obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o + obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o + obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o +@@ -11,3 +12,5 @@ obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/ + imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o + obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o + obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o ++obj-$(CONFIG_DRM_DW_HDMI_AUDIO) += dw-hdmi-audio.o ++obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o +diff --git a/drivers/staging/imx-drm/drm-ddc-connector.c b/drivers/staging/imx-drm/drm-ddc-connector.c +new file mode 100644 +index 000000000000..a36ed4b06ebe +--- /dev/null ++++ b/drivers/staging/imx-drm/drm-ddc-connector.c +@@ -0,0 +1,92 @@ ++#include <linux/i2c.h> ++#include <linux/module.h> ++#include <drm/drmP.h> ++#include <drm/drm_crtc_helper.h> ++#include <drm/drm_edid.h> ++ ++#include "drm-ddc-connector.h" ++ ++static enum drm_connector_status ++drm_ddc_connector_detect(struct drm_connector *connector, bool force) ++{ ++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector); ++ ++ return ddc_conn->detect ? ddc_conn->detect(connector, force) : ++ connector_status_connected; ++} ++ ++int drm_ddc_connector_get_modes(struct drm_connector *connector) ++{ ++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector); ++ struct edid *edid; ++ int ret = 0; ++ ++ if (!ddc_conn->ddc) ++ return 0; ++ ++ edid = drm_get_edid(connector, ddc_conn->ddc); ++ if (edid) { ++ drm_mode_connector_update_edid_property(connector, edid); ++ ret = drm_add_edid_modes(connector, edid); ++ /* Store the ELD */ ++ drm_edid_to_eld(connector, edid); ++ kfree(edid); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(drm_ddc_connector_get_modes); ++ ++static void drm_ddc_connector_destroy(struct drm_connector *connector) ++{ ++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector); ++ ++ drm_sysfs_connector_remove(connector); ++ drm_connector_cleanup(connector); ++ if (ddc_conn->ddc) ++ i2c_put_adapter(ddc_conn->ddc); ++} ++ ++static const struct drm_connector_funcs drm_ddc_connector_funcs = { ++ .dpms = drm_helper_connector_dpms, ++ .fill_modes = drm_helper_probe_single_connector_modes, ++ .detect = drm_ddc_connector_detect, ++ .destroy = drm_ddc_connector_destroy, ++}; ++ ++int drm_ddc_connector_add(struct drm_device *drm, ++ struct drm_ddc_connector *ddc_conn, int connector_type) ++{ ++ drm_connector_init(drm, &ddc_conn->connector, &drm_ddc_connector_funcs, ++ connector_type); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(drm_ddc_connector_add); ++ ++struct drm_ddc_connector *drm_ddc_connector_create(struct drm_device *drm, ++ struct device_node *np, void *private) ++{ ++ struct drm_ddc_connector *ddc_conn; ++ struct device_node *ddc_node; ++ ++ ddc_conn = devm_kzalloc(drm->dev, sizeof(*ddc_conn), GFP_KERNEL); ++ if (!ddc_conn) ++ return ERR_PTR(-ENOMEM); ++ ++ ddc_conn->private = private; ++ ++ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); ++ if (ddc_node) { ++ ddc_conn->ddc = of_find_i2c_adapter_by_node(ddc_node); ++ of_node_put(ddc_node); ++ if (!ddc_conn->ddc) ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ return ddc_conn; ++} ++EXPORT_SYMBOL_GPL(drm_ddc_connector_create); ++ ++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>"); ++MODULE_DESCRIPTION("Generic DRM DDC connector module"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/staging/imx-drm/drm-ddc-connector.h b/drivers/staging/imx-drm/drm-ddc-connector.h +new file mode 100644 +index 000000000000..38068e5105d3 +--- /dev/null ++++ b/drivers/staging/imx-drm/drm-ddc-connector.h +@@ -0,0 +1,26 @@ ++#ifndef DRM_DDC_CONNECTOR_H ++#define DRM_DDC_CONNECTOR_H ++ ++struct drm_ddc_connector { ++ struct i2c_adapter *ddc; ++ struct drm_connector connector; ++ enum drm_connector_status (*detect)(struct drm_connector *, bool); ++ void *private; ++}; ++ ++#define to_ddc_conn(c) container_of(c, struct drm_ddc_connector, connector) ++ ++int drm_ddc_connector_get_modes(struct drm_connector *connector); ++int drm_ddc_connector_add(struct drm_device *drm, ++ struct drm_ddc_connector *ddc_conn, int connector_type); ++struct drm_ddc_connector *drm_ddc_connector_create(struct drm_device *drm, ++ struct device_node *np, void *private); ++ ++static inline void *drm_ddc_private(struct drm_connector *connector) ++{ ++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector); ++ ++ return ddc_conn->private; ++} ++ ++#endif +diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c +new file mode 100644 +index 000000000000..e7495265ed84 +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-audio.c +@@ -0,0 +1,652 @@ ++/* ++ * DesignWare HDMI audio driver ++ * ++ * 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. ++ * ++ * Written and tested against the (alleged) DW HDMI Tx found in iMX6S. ++ */ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++ ++#include <sound/asoundef.h> ++#include <sound/core.h> ++#include <sound/initval.h> ++#include <sound/pcm.h> ++ ++#include "dw-hdmi-audio.h" ++ ++#define DRIVER_NAME "dw-hdmi-audio" ++ ++/* Provide some bits rather than bit offsets */ ++enum { ++ HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7), ++ HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3), ++ HDMI_AHB_DMA_START_START = BIT(0), ++ HDMI_AHB_DMA_STOP_STOP = BIT(0), ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5), ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4), ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3), ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2), ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1), ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0), ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL = ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR | ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST | ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY | ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE | ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL | ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY, ++ HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5), ++ HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4), ++ HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3), ++ HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2), ++ HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1), ++ HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0), ++ HDMI_IH_AHBDMAAUD_STAT0_ALL = ++ HDMI_IH_AHBDMAAUD_STAT0_ERROR | ++ HDMI_IH_AHBDMAAUD_STAT0_LOST | ++ HDMI_IH_AHBDMAAUD_STAT0_RETRY | ++ HDMI_IH_AHBDMAAUD_STAT0_DONE | ++ HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL | ++ HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY, ++ HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1, ++ HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1, ++ HDMI_AHB_DMA_CONF0_INCR4 = 0, ++ HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0), ++ HDMI_AHB_DMA_MASK_DONE = BIT(7), ++ HDMI_REVISION_ID = 0x0001, ++ HDMI_IH_AHBDMAAUD_STAT0 = 0x0109, ++ HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189, ++ HDMI_AUD_N1 = 0x3200, ++ HDMI_AUD_CTS1 = 0x3203, ++ HDMI_AHB_DMA_CONF0 = 0x3600, ++ HDMI_AHB_DMA_START = 0x3601, ++ HDMI_AHB_DMA_STOP = 0x3602, ++ HDMI_AHB_DMA_THRSLD = 0x3603, ++ HDMI_AHB_DMA_STRADDR0 = 0x3604, ++ HDMI_AHB_DMA_STPADDR0 = 0x3608, ++ HDMI_AHB_DMA_STAT = 0x3612, ++ HDMI_AHB_DMA_STAT_FULL = BIT(1), ++ HDMI_AHB_DMA_MASK = 0x3614, ++ HDMI_AHB_DMA_POL = 0x3615, ++ HDMI_AHB_DMA_CONF1 = 0x3616, ++ HDMI_AHB_DMA_BUFFPOL = 0x361a, ++}; ++ ++struct snd_dw_hdmi { ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ struct dw_hdmi_audio_data data; ++ struct snd_pcm_substream *substream; ++ void (*reformat)(struct snd_dw_hdmi *, size_t, size_t); ++ void *buf_src; ++ void *buf_dst; ++ dma_addr_t buf_addr; ++ unsigned buf_offset; ++ unsigned buf_period; ++ unsigned buf_size; ++ unsigned channels; ++ uint8_t revision; ++ uint8_t iec_offset; ++ uint8_t cs[192][8]; ++}; ++ ++static void dw_hdmi_writel(unsigned long val, void __iomem *ptr) ++{ ++ writeb_relaxed(val, ptr); ++ writeb_relaxed(val >> 8, ptr + 1); ++ writeb_relaxed(val >> 16, ptr + 2); ++ writeb_relaxed(val >> 24, ptr + 3); ++} ++ ++/* ++ * Convert to hardware format: The userspace buffer contains IEC958 samples, ++ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4. We ++ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio ++ * samples in 23..0. ++ * ++ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd ++ * ++ * Ideally, we could do with having the data properly formatted in userspace. ++ */ ++static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw, ++ size_t offset, size_t bytes) ++{ ++ uint32_t *src = dw->buf_src + offset; ++ uint32_t *dst = dw->buf_dst + offset; ++ uint32_t *end = dw->buf_src + offset + bytes; ++ ++ do { ++ uint32_t b, sample = *src++; ++ ++ b = (sample & 8) << (28 - 3); ++ ++ sample >>= 4; ++ ++ *dst++ = sample | b; ++ } while (src < end); ++} ++ ++static uint32_t parity(uint32_t sample) ++{ ++ sample ^= sample >> 16; ++ sample ^= sample >> 8; ++ sample ^= sample >> 4; ++ sample ^= sample >> 2; ++ sample ^= sample >> 1; ++ return (sample & 1) << 27; ++} ++ ++static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw, ++ size_t offset, size_t bytes) ++{ ++ uint32_t *src = dw->buf_src + offset; ++ uint32_t *dst = dw->buf_dst + offset; ++ uint32_t *end = dw->buf_src + offset + bytes; ++ ++ do { ++ unsigned i; ++ uint8_t *cs; ++ ++ cs = dw->cs[dw->iec_offset++]; ++ if (dw->iec_offset >= 192) ++ dw->iec_offset = 0; ++ ++ i = dw->channels; ++ do { ++ uint32_t sample = *src++; ++ ++ sample &= ~0xff000000; ++ sample |= *cs++ << 24; ++ sample |= parity(sample & ~0xf8000000); ++ ++ *dst++ = sample; ++ } while (--i); ++ } while (src < end); ++} ++ ++static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw, ++ struct snd_pcm_runtime *runtime) ++{ ++ uint8_t cs[4]; ++ unsigned ch, i, j; ++ ++ cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE; ++ cs[1] = IEC958_AES1_CON_GENERAL; ++ cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC; ++ cs[3] = IEC958_AES3_CON_CLOCK_1000PPM; ++ ++ switch (runtime->rate) { ++ case 32000: ++ cs[3] |= IEC958_AES3_CON_FS_32000; ++ break; ++ case 44100: ++ cs[3] |= IEC958_AES3_CON_FS_44100; ++ break; ++ case 48000: ++ cs[3] |= IEC958_AES3_CON_FS_48000; ++ break; ++ case 88200: ++ cs[3] |= IEC958_AES3_CON_FS_88200; ++ break; ++ case 96000: ++ cs[3] |= IEC958_AES3_CON_FS_96000; ++ break; ++ case 176400: ++ cs[3] |= IEC958_AES3_CON_FS_176400; ++ break; ++ case 192000: ++ cs[3] |= IEC958_AES3_CON_FS_192000; ++ break; ++ } ++ ++ memset(dw->cs, 0, sizeof(dw->cs)); ++ ++ for (ch = 0; ch < 8; ch++) { ++ cs[2] &= ~IEC958_AES2_CON_CHANNEL; ++ cs[2] |= (ch + 1) << 4; ++ ++ for (i = 0; i < ARRAY_SIZE(cs); i++) { ++ unsigned c = cs[i]; ++ ++ for (j = 0; j < 8; j++, c >>= 1) ++ dw->cs[i * 8 + j][ch] = (c & 1) << 2; ++ } ++ } ++ dw->cs[0][0] |= BIT(4); ++} ++ ++static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw) ++{ ++ void __iomem *base = dw->data.base; ++ unsigned offset = dw->buf_offset; ++ unsigned period = dw->buf_period; ++ u32 start, stop; ++ ++ dw->reformat(dw, offset, period); ++ ++ /* Clear all irqs before enabling irqs and starting DMA */ ++ writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL, ++ base + HDMI_IH_AHBDMAAUD_STAT0); ++ ++ start = dw->buf_addr + offset; ++ stop = start + period - 1; ++ ++ /* Setup the hardware start/stop addresses */ ++ dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0); ++ dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0); ++ ++ writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK); ++ writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START); ++ ++ offset += period; ++ if (offset >= dw->buf_size) ++ offset = 0; ++ dw->buf_offset = offset; ++} ++ ++static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw) ++{ ++ dw->substream = NULL; ++ ++ /* Disable interrupts before disabling DMA */ ++ writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK); ++ writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP); ++} ++ ++static irqreturn_t snd_dw_hdmi_irq(int irq, void *data) ++{ ++ struct snd_dw_hdmi *dw = data; ++ struct snd_pcm_substream *substream; ++ unsigned stat; ++ ++ stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0); ++ if (!stat) ++ return IRQ_NONE; ++ ++ writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0); ++ ++ substream = dw->substream; ++ if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) { ++ snd_pcm_period_elapsed(substream); ++ if (dw->substream) ++ dw_hdmi_start_dma(dw); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static struct snd_pcm_hardware dw_hdmi_hw = { ++ .info = SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID, ++ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | ++ SNDRV_PCM_FMTBIT_S24_LE, ++ .rates = SNDRV_PCM_RATE_32000 | ++ SNDRV_PCM_RATE_44100 | ++ SNDRV_PCM_RATE_48000 | ++ SNDRV_PCM_RATE_88200 | ++ SNDRV_PCM_RATE_96000 | ++ SNDRV_PCM_RATE_176400 | ++ SNDRV_PCM_RATE_192000, ++ .channels_min = 2, ++ .channels_max = 8, ++ .buffer_bytes_max = 64 * 1024, ++ .period_bytes_min = 256, ++ .period_bytes_max = 8192, /* ERR004323: must limit to 8k */ ++ .periods_min = 2, ++ .periods_max = 16, ++ .fifo_size = 0, ++}; ++ ++static unsigned rates_mask[] = { ++ SNDRV_PCM_RATE_32000, ++ SNDRV_PCM_RATE_44100, ++ SNDRV_PCM_RATE_48000, ++ SNDRV_PCM_RATE_88200, ++ SNDRV_PCM_RATE_96000, ++ SNDRV_PCM_RATE_176400, ++ SNDRV_PCM_RATE_192000, ++}; ++ ++static void dw_hdmi_parse_eld(struct snd_dw_hdmi *dw, ++ struct snd_pcm_runtime *runtime) ++{ ++ u8 *sad, *eld = dw->data.eld; ++ unsigned eld_ver, mnl, sad_count, rates, rate_mask, i; ++ unsigned max_channels; ++ ++ eld_ver = eld[0] >> 3; ++ if (eld_ver != 2 && eld_ver != 31) ++ return; ++ ++ mnl = eld[4] & 0x1f; ++ if (mnl > 16) ++ return; ++ ++ sad_count = eld[5] >> 4; ++ sad = eld + 20 + mnl; ++ ++ /* Start from the basic audio settings */ ++ max_channels = 2; ++ rates = 7; ++ while (sad_count > 0) { ++ switch (sad[0] & 0x78) { ++ case 0x08: /* PCM */ ++ max_channels = max(max_channels, (sad[0] & 7) + 1u); ++ rates |= sad[1]; ++ break; ++ } ++ sad += 3; ++ sad_count -= 1; ++ } ++ ++ for (rate_mask = i = 0; i < ARRAY_SIZE(rates_mask); i++) ++ if (rates & 1 << i) ++ rate_mask |= rates_mask[i]; ++ ++ runtime->hw.rates &= rate_mask; ++ runtime->hw.channels_max = min(runtime->hw.channels_max, max_channels); ++} ++ ++static int dw_hdmi_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_dw_hdmi *dw = substream->private_data; ++ void __iomem *base = dw->data.base; ++ int ret; ++ ++ /* Clear FIFO */ ++ writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST, ++ base + HDMI_AHB_DMA_CONF0); ++ ++ /* Configure interrupt polarities */ ++ writeb_relaxed(~0, base + HDMI_AHB_DMA_POL); ++ writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL); ++ ++ /* Keep interrupts masked, and clear any pending */ ++ writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK); ++ writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0); ++ ++ ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED, ++ "dw-hdmi-audio", dw); ++ if (ret) ++ return ret; ++ ++ /* Un-mute done interrupt */ ++ writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL & ++ ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE, ++ base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); ++ ++ runtime->hw = dw_hdmi_hw; ++ dw_hdmi_parse_eld(dw, runtime); ++ snd_pcm_limit_hw_rates(runtime); ++ ++ return 0; ++} ++ ++static int dw_hdmi_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_dw_hdmi *dw = substream->private_data; ++ ++ /* Mute all interrupts */ ++ writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, ++ dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); ++ ++ free_irq(dw->data.irq, dw); ++ ++ return 0; ++} ++ ++static int dw_hdmi_hw_free(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_lib_free_vmalloc_buffer(substream); ++} ++ ++static int dw_hdmi_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ return snd_pcm_lib_alloc_vmalloc_buffer(substream, ++ params_buffer_bytes(params)); ++} ++ ++static int dw_hdmi_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_dw_hdmi *dw = substream->private_data; ++ uint8_t threshold, conf0, conf1; ++ ++ /* Setup as per 3.0.5 FSL 4.1.0 BSP */ ++ switch (dw->revision) { ++ case 0x0a: ++ conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE | ++ HDMI_AHB_DMA_CONF0_INCR4; ++ if (runtime->channels == 2) ++ threshold = 126; ++ else ++ threshold = 124; ++ break; ++ case 0x1a: ++ conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE | ++ HDMI_AHB_DMA_CONF0_INCR8; ++ threshold = 128; ++ break; ++ default: ++ /* NOTREACHED */ ++ return -EINVAL; ++ } ++ ++ dw->data.set_sample_rate(dw->data.hdmi, runtime->rate); ++ ++ /* Minimum number of bytes in the fifo. */ ++ runtime->hw.fifo_size = threshold * 32; ++ ++ conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK; ++ conf1 = (1 << runtime->channels) - 1; ++ ++ writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD); ++ writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0); ++ writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1); ++ ++ switch (runtime->format) { ++ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: ++ dw->reformat = dw_hdmi_reformat_iec958; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ dw_hdmi_create_cs(dw, runtime); ++ dw->reformat = dw_hdmi_reformat_s24; ++ break; ++ } ++ dw->iec_offset = 0; ++ dw->channels = runtime->channels; ++ dw->buf_src = runtime->dma_area; ++ dw->buf_dst = substream->dma_buffer.area; ++ dw->buf_addr = substream->dma_buffer.addr; ++ dw->buf_period = snd_pcm_lib_period_bytes(substream); ++ dw->buf_size = snd_pcm_lib_buffer_bytes(substream); ++ ++ return 0; ++} ++ ++static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct snd_dw_hdmi *dw = substream->private_data; ++ void __iomem *base = dw->data.base; ++ unsigned n[3], cts[3]; ++ int ret = 0, i; ++ bool err005174; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ err005174 = dw->revision == 0x0a; ++ if (err005174) { ++ for (i = 2; i >= 1; i--) { ++ n[i] = readb_relaxed(base + HDMI_AUD_N1 + i); ++ cts[i] = readb_relaxed(base + HDMI_AUD_CTS1 + i); ++ writeb_relaxed(0, base + HDMI_AUD_N1 + i); ++ writeb_relaxed(0, base + HDMI_AUD_CTS1 + i); ++ } ++ } ++ ++ dw->buf_offset = 0; ++ dw->substream = substream; ++ dw_hdmi_start_dma(dw); ++ ++ if (err005174) { ++ for (i = 2; i >= 1; i--) ++ writeb_relaxed(cts[i], base + HDMI_AUD_CTS1 + i); ++ for (i = 2; i >= 1; i--) ++ writeb_relaxed(n[i], base + HDMI_AUD_N1 + i); ++ } ++ ++ substream->runtime->delay = substream->runtime->period_size; ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ dw_hdmi_stop_dma(dw); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_dw_hdmi *dw = substream->private_data; ++ ++ return bytes_to_frames(runtime, dw->buf_offset); ++} ++ ++static struct snd_pcm_ops snd_dw_hdmi_ops = { ++ .open = dw_hdmi_open, ++ .close = dw_hdmi_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = dw_hdmi_hw_params, ++ .hw_free = dw_hdmi_hw_free, ++ .prepare = dw_hdmi_prepare, ++ .trigger = dw_hdmi_trigger, ++ .pointer = dw_hdmi_pointer, ++ .page = snd_pcm_lib_get_vmalloc_page, ++}; ++ ++static int snd_dw_hdmi_probe(struct platform_device *pdev) ++{ ++ const struct dw_hdmi_audio_data *data = pdev->dev.platform_data; ++ struct device *dev = pdev->dev.parent; ++ struct snd_dw_hdmi *dw; ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ unsigned revision; ++ int ret; ++ ++ writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, ++ data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); ++ revision = readb_relaxed(data->base + HDMI_REVISION_ID); ++ if (revision != 0x0a && revision != 0x1a) { ++ dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n", ++ revision); ++ return -ENXIO; ++ } ++ ++ ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, ++ THIS_MODULE, sizeof(struct snd_dw_hdmi), &card); ++ if (ret < 0) ++ return ret; ++ ++ strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver)); ++ strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname)); ++ snprintf(card->longname, sizeof(card->longname), ++ "%s rev 0x%02x, irq %d", card->shortname, revision, ++ data->irq); ++ ++ dw = card->private_data; ++ dw->card = card; ++ dw->data = *data; ++ dw->revision = revision; ++ ++ ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm); ++ if (ret < 0) ++ goto err; ++ ++ dw->pcm = pcm; ++ pcm->private_data = dw; ++ strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name)); ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops); ++ ++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, ++ dev, 64 * 1024, 64 * 1024); ++ ++ ret = snd_card_register(card); ++ if (ret < 0) ++ goto err; ++ ++ platform_set_drvdata(pdev, dw); ++ ++ return 0; ++ ++err: ++ snd_card_free(card); ++ return ret; ++} ++ ++static int snd_dw_hdmi_remove(struct platform_device *pdev) ++{ ++ struct snd_dw_hdmi *dw = platform_get_drvdata(pdev); ++ ++ snd_card_free(dw->card); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int snd_dw_hdmi_suspend(struct device *dev) ++{ ++ struct snd_dw_hdmi *dw = dev_get_drvdata(dev); ++ ++ snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold); ++ snd_pcm_suspend_all(dw->pcm); ++ ++ return 0; ++} ++ ++static int snd_dw_hdmi_resume(struct device *dev) ++{ ++ struct snd_dw_hdmi *dw = dev_get_drvdata(dev); ++ ++ snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0); ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend, ++ snd_dw_hdmi_resume); ++#define PM_OPS &snd_dw_hdmi_pm ++#else ++#define PM_OPS NULL ++#endif ++ ++static struct platform_driver snd_dw_hdmi_driver = { ++ .probe = snd_dw_hdmi_probe, ++ .remove = snd_dw_hdmi_remove, ++ .driver = { ++ .name = "dw-hdmi-audio", ++ .owner = THIS_MODULE, ++ .pm = PM_OPS, ++ }, ++}; ++ ++module_platform_driver(snd_dw_hdmi_driver); ++ ++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.h b/drivers/staging/imx-drm/dw-hdmi-audio.h +new file mode 100644 +index 000000000000..2d91d709381d +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-audio.h +@@ -0,0 +1,15 @@ ++#ifndef DW_HDMI_AUDIO_H ++#define DW_HDMI_AUDIO_H ++ ++struct imx_hdmi; ++ ++struct dw_hdmi_audio_data { ++ phys_addr_t phys; ++ void __iomem *base; ++ int irq; ++ struct imx_hdmi *hdmi; ++ u8 *eld; ++ void (*set_sample_rate)(struct imx_hdmi *, unsigned); ++}; ++ ++#endif +diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.c b/drivers/staging/imx-drm/dw-hdmi-cec.c +new file mode 100644 +index 000000000000..c94b75aa037b +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-cec.c +@@ -0,0 +1,205 @@ ++/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */ ++#include <linux/cec-dev.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++ ++#include "imx-hdmi.h" ++#include "dw-hdmi-cec.h" ++ ++#define DEV_NAME "mxc_hdmi_cec" ++ ++enum { ++ CEC_STAT_DONE = BIT(0), ++ CEC_STAT_EOM = BIT(1), ++ CEC_STAT_NACK = BIT(2), ++ CEC_STAT_ARBLOST = BIT(3), ++ CEC_STAT_ERROR_INIT = BIT(4), ++ CEC_STAT_ERROR_FOLL = BIT(5), ++ CEC_STAT_WAKEUP = BIT(6), ++ ++ CEC_CTRL_START = BIT(0), ++ CEC_CTRL_NORMAL = 1 << 1, ++}; ++ ++struct dw_hdmi_cec { ++ struct cec_dev cec; ++ ++ struct device *dev; ++ void __iomem *base; ++ const struct dw_hdmi_cec_ops *ops; ++ void *ops_data; ++ int irq; ++}; ++ ++static void dw_hdmi_set_address(struct cec_dev *cec_dev, unsigned addresses) ++{ ++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec); ++ ++ writeb(addresses & 255, cec->base + HDMI_CEC_ADDR_L); ++ writeb(addresses >> 8, cec->base + HDMI_CEC_ADDR_H); ++} ++ ++static void dw_hdmi_send_message(struct cec_dev *cec_dev, u8 *msg, ++ size_t count) ++{ ++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec); ++ unsigned i; ++ ++ for (i = 0; i < count; i++) ++ writeb(msg[i], cec->base + HDMI_CEC_TX_DATA0 + i); ++ ++ writeb(count, cec->base + HDMI_CEC_TX_CNT); ++ writeb(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL); ++} ++ ++static irqreturn_t dw_hdmi_cec_irq(int irq, void *data) ++{ ++ struct dw_hdmi_cec *cec = data; ++ struct cec_dev *cec_dev = &cec->cec; ++ unsigned stat = readb(cec->base + HDMI_IH_CEC_STAT0); ++ ++ if (stat == 0) ++ return IRQ_NONE; ++ ++ writeb(stat, cec->base + HDMI_IH_CEC_STAT0); ++ ++ if (stat & CEC_STAT_ERROR_INIT) { ++ if (cec->cec.retries) { ++ unsigned v = readb(cec->base + HDMI_CEC_CTRL); ++ writeb(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL); ++ cec->cec.retries -= 1; ++ } else { ++ cec->cec.write_busy = 0; ++ cec_dev_event(cec_dev, MESSAGE_TYPE_SEND_ERROR, NULL, 0); ++ } ++ } else if (stat & (CEC_STAT_DONE | CEC_STAT_NACK)) ++ cec_dev_send_complete(cec_dev, stat & CEC_STAT_DONE); ++ ++ if (stat & CEC_STAT_EOM) { ++ unsigned len, i; ++ u8 msg[MAX_MESSAGE_LEN]; ++ ++ len = readb(cec->base + HDMI_CEC_RX_CNT); ++ if (len > sizeof(msg)) ++ len = sizeof(msg); ++ ++ for (i = 0; i < len; i++) ++ msg[i] = readb(cec->base + HDMI_CEC_RX_DATA0 + i); ++ ++ writeb(0, cec->base + HDMI_CEC_LOCK); ++ ++ cec_dev_receive(cec_dev, msg, len); ++ } ++ ++ return IRQ_HANDLED; ++} ++EXPORT_SYMBOL(dw_hdmi_cec_irq); ++ ++static void dw_hdmi_cec_release(struct cec_dev *cec_dev) ++{ ++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec); ++ ++ writeb(~0, cec->base + HDMI_CEC_MASK); ++ writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0); ++ writeb(0, cec->base + HDMI_CEC_POLARITY); ++ ++ free_irq(cec->irq, cec); ++ ++ cec->ops->disable(cec->ops_data); ++} ++ ++static int dw_hdmi_cec_open(struct cec_dev *cec_dev) ++{ ++ struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec); ++ unsigned irqs; ++ int ret; ++ ++ writeb(0, cec->base + HDMI_CEC_CTRL); ++ writeb(~0, cec->base + HDMI_IH_CEC_STAT0); ++ writeb(0, cec->base + HDMI_CEC_LOCK); ++ ++ ret = request_irq(cec->irq, dw_hdmi_cec_irq, IRQF_SHARED, ++ DEV_NAME, cec); ++ if (ret < 0) ++ return ret; ++ ++ dw_hdmi_set_address(cec_dev, cec_dev->addresses); ++ ++ cec->ops->enable(cec->ops_data); ++ ++ irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM | ++ CEC_STAT_DONE; ++ writeb(irqs, cec->base + HDMI_CEC_POLARITY); ++ writeb(~irqs, cec->base + HDMI_CEC_MASK); ++ writeb(~irqs, cec->base + HDMI_IH_MUTE_CEC_STAT0); ++ ++ return 0; ++} ++ ++static int dw_hdmi_cec_probe(struct platform_device *pdev) ++{ ++ struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev); ++ struct dw_hdmi_cec *cec; ++ ++ if (!data) ++ return -ENXIO; ++ ++ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); ++ if (!cec) ++ return -ENOMEM; ++ ++ cec->dev = &pdev->dev; ++ cec->base = data->base; ++ cec->irq = data->irq; ++ cec->ops = data->ops; ++ cec->ops_data = data->ops_data; ++ cec->cec.open = dw_hdmi_cec_open; ++ cec->cec.release = dw_hdmi_cec_release; ++ cec->cec.send_message = dw_hdmi_send_message; ++ cec->cec.set_address = dw_hdmi_set_address; ++ ++ cec_dev_init(&cec->cec, THIS_MODULE); ++ ++ /* FIXME: soft-reset the CEC interface */ ++ ++ dw_hdmi_set_address(&cec->cec, cec->cec.addresses); ++ writeb(0, cec->base + HDMI_CEC_TX_CNT); ++ writeb(~0, cec->base + HDMI_CEC_MASK); ++ writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0); ++ writeb(0, cec->base + HDMI_CEC_POLARITY); ++ ++ /* ++ * Our device is just a convenience - we want to link to the real ++ * hardware device here, so that userspace can see the association ++ * between the HDMI hardware and its associated CEC chardev. ++ */ ++ return cec_dev_add(&cec->cec, cec->dev->parent, DEV_NAME); ++} ++ ++static int dw_hdmi_cec_remove(struct platform_device *pdev) ++{ ++ struct dw_hdmi_cec *cec = platform_get_drvdata(pdev); ++ ++ cec_dev_remove(&cec->cec); ++ ++ return 0; ++} ++ ++static struct platform_driver dw_hdmi_cec_driver = { ++ .probe = dw_hdmi_cec_probe, ++ .remove = dw_hdmi_cec_remove, ++ .driver = { ++ .name = "dw-hdmi-cec", ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(dw_hdmi_cec_driver); ++ ++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>"); ++MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec"); +diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.h b/drivers/staging/imx-drm/dw-hdmi-cec.h +new file mode 100644 +index 000000000000..5ff40cc237a8 +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-cec.h +@@ -0,0 +1,16 @@ ++#ifndef DW_HDMI_CEC_H ++#define DW_HDMI_CEC_H ++ ++struct dw_hdmi_cec_ops { ++ void (*enable)(void *); ++ void (*disable)(void *); ++}; ++ ++struct dw_hdmi_cec_data { ++ void __iomem *base; ++ int irq; ++ const struct dw_hdmi_cec_ops *ops; ++ void *ops_data; ++}; ++ ++#endif +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index d47dedd2cdb4..627b4b608794 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -28,6 +28,9 @@ + #include <drm/drm_edid.h> + #include <drm/drm_encoder_slave.h> + ++#include "drm-ddc-connector.h" ++#include "dw-hdmi-audio.h" ++#include "dw-hdmi-cec.h" + #include "ipu-v3/imx-ipu-v3.h" + #include "imx-hdmi.h" + #include "imx-drm.h" +@@ -112,27 +115,27 @@ struct hdmi_data_info { + }; + + struct imx_hdmi { +- struct drm_connector connector; ++ struct drm_ddc_connector *ddc_conn; + struct drm_encoder encoder; + ++ struct platform_device *audio; ++ struct platform_device *cec; + enum imx_hdmi_devtype dev_type; + struct device *dev; + struct clk *isfr_clk; + struct clk *iahb_clk; + +- enum drm_connector_status connector_status; +- + struct hdmi_data_info hdmi_data; + int vic; + + u8 edid[HDMI_EDID_LEN]; ++ u8 mc_clkdis; + bool cable_plugin; + + bool phy_enabled; + struct drm_display_mode previous_mode; + + struct regmap *regmap; +- struct i2c_adapter *ddc; + void __iomem *regs; + + unsigned int sample_rate; +@@ -362,6 +365,12 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi) + hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); + } + ++static void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned rate) ++{ ++ hdmi->sample_rate = rate; ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); ++} ++ + /* + * this submodule is responsible for the video data synchronization. + * for example, for RGB 4:4:4 input, the data map is defined as +@@ -1148,8 +1157,6 @@ static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi) + /* HDMI Initialization Step B.4 */ + static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) + { +- u8 clkdis; +- + /* control period minimum duration */ + hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR); + hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR); +@@ -1161,23 +1168,28 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) + hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); + + /* Enable pixel clock and tmds data path */ +- clkdis = 0x7F; +- clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE | ++ HDMI_MC_CLKDIS_CSCCLK_DISABLE | ++ HDMI_MC_CLKDIS_AUDCLK_DISABLE | ++ HDMI_MC_CLKDIS_PREPCLK_DISABLE | ++ HDMI_MC_CLKDIS_TMDSCLK_DISABLE; ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + +- clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + + /* Enable csc path */ + if (is_color_space_conversion(hdmi)) { +- clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + } + } + + static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi) + { +- hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + } + + /* Workaround to clear the overflow condition */ +@@ -1380,41 +1392,16 @@ static void imx_hdmi_poweroff(struct imx_hdmi *hdmi) + static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector + *connector, bool force) + { +- struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, +- connector); +- return hdmi->connector_status; +-} +- +-static int imx_hdmi_connector_get_modes(struct drm_connector *connector) +-{ +- struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, +- connector); +- struct edid *edid; +- int ret; +- +- if (!hdmi->ddc) +- return 0; +- +- edid = drm_get_edid(connector, hdmi->ddc); +- if (edid) { +- dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", +- edid->width_cm, edid->height_cm); +- +- drm_mode_connector_update_edid_property(connector, edid); +- ret = drm_add_edid_modes(connector, edid); +- kfree(edid); +- } else { +- dev_dbg(hdmi->dev, "failed to get edid\n"); +- } ++ struct imx_hdmi *hdmi = drm_ddc_private(connector); + +- return 0; ++ return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? ++ connector_status_connected : connector_status_disconnected; + } + + static struct drm_encoder *imx_hdmi_connector_best_encoder(struct drm_connector + *connector) + { +- struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, +- connector); ++ struct imx_hdmi *hdmi = drm_ddc_private(connector); + + return &hdmi->encoder; + } +@@ -1483,15 +1470,8 @@ static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = { + .disable = imx_hdmi_encoder_disable, + }; + +-static struct drm_connector_funcs imx_hdmi_connector_funcs = { +- .dpms = drm_helper_connector_dpms, +- .fill_modes = drm_helper_probe_single_connector_modes, +- .detect = imx_hdmi_connector_detect, +- .destroy = imx_drm_connector_destroy, +-}; +- + static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { +- .get_modes = imx_hdmi_connector_get_modes, ++ .get_modes = drm_ddc_connector_get_modes, + .mode_valid = imx_drm_connector_mode_valid, + .best_encoder = imx_hdmi_connector_best_encoder, + }; +@@ -1524,7 +1504,6 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) + + hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0); + +- hdmi->connector_status = connector_status_connected; + imx_hdmi_poweron(hdmi); + } else { + dev_dbg(hdmi->dev, "EVENT=plugout\n"); +@@ -1532,10 +1511,9 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) + hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, + HDMI_PHY_POL0); + +- hdmi->connector_status = connector_status_disconnected; + imx_hdmi_poweroff(hdmi); + } +- drm_helper_hpd_irq_event(hdmi->connector.dev); ++ drm_helper_hpd_irq_event(hdmi->ddc_conn->connector.dev); + } + + hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); +@@ -1553,24 +1531,42 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi) + if (ret) + return ret; + +- hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; ++ hdmi->ddc_conn->connector.polled = DRM_CONNECTOR_POLL_HPD; + + drm_encoder_helper_add(&hdmi->encoder, &imx_hdmi_encoder_helper_funcs); + drm_encoder_init(drm, &hdmi->encoder, &imx_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + +- drm_connector_helper_add(&hdmi->connector, ++ drm_connector_helper_add(&hdmi->ddc_conn->connector, + &imx_hdmi_connector_helper_funcs); +- drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs, +- DRM_MODE_CONNECTOR_HDMIA); +- +- hdmi->connector.encoder = &hdmi->encoder; ++ drm_ddc_connector_add(drm, hdmi->ddc_conn, DRM_MODE_CONNECTOR_HDMIA); + +- drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder); ++ drm_mode_connector_attach_encoder(&hdmi->ddc_conn->connector, &hdmi->encoder); + + return 0; + } + ++static void imx_hdmi_cec_enable(void *data) ++{ ++ struct imx_hdmi *hdmi = data; ++ ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); ++} ++ ++static void imx_hdmi_cec_disable(void *data) ++{ ++ struct imx_hdmi *hdmi = data; ++ ++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); ++} ++ ++static const struct dw_hdmi_cec_ops imx_hdmi_cec_ops = { ++ .enable = imx_hdmi_cec_enable, ++ .disable = imx_hdmi_cec_disable, ++}; ++ + static struct platform_device_id imx_hdmi_devtype[] = { + { + .name = "imx6q-hdmi", +@@ -1592,11 +1588,13 @@ MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids); + static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); ++ struct platform_device_info pdevinfo; + const struct of_device_id *of_id = + of_match_device(imx_hdmi_dt_ids, dev); + struct drm_device *drm = data; + struct device_node *np = dev->of_node; +- struct device_node *ddc_node; ++ struct dw_hdmi_audio_data audio; ++ struct dw_hdmi_cec_data cec; + struct imx_hdmi *hdmi; + struct resource *iores; + int ret, irq; +@@ -1605,27 +1603,22 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + if (!hdmi) + return -ENOMEM; + ++ hdmi->ddc_conn = drm_ddc_connector_create(drm, np, hdmi); ++ if (IS_ERR(hdmi->ddc_conn)) ++ return PTR_ERR(hdmi->ddc_conn); ++ ++ hdmi->ddc_conn->detect = imx_hdmi_connector_detect; ++ + hdmi->dev = dev; +- hdmi->connector_status = connector_status_disconnected; + hdmi->sample_rate = 48000; + hdmi->ratio = 100; ++ hdmi->mc_clkdis = 0x7f; + + if (of_id) { + const struct platform_device_id *device_id = of_id->data; + hdmi->dev_type = device_id->driver_data; + } + +- ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); +- if (ddc_node) { +- hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); +- if (!hdmi->ddc) +- dev_dbg(hdmi->dev, "failed to read ddc node\n"); +- +- of_node_put(ddc_node); +- } else { +- dev_dbg(hdmi->dev, "no ddc property found\n"); +- } +- + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; +@@ -1711,6 +1704,35 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + /* Unmute interrupts */ + hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + ++ memset(&pdevinfo, 0, sizeof(pdevinfo)); ++ pdevinfo.parent = dev; ++ pdevinfo.id = PLATFORM_DEVID_AUTO; ++ ++ audio.phys = iores->start; ++ audio.base = hdmi->regs; ++ audio.irq = irq; ++ audio.hdmi = hdmi; ++ audio.eld = hdmi->ddc_conn->connector.eld; ++ audio.set_sample_rate = imx_hdmi_set_sample_rate; ++ ++ pdevinfo.name = "dw-hdmi-audio"; ++ pdevinfo.data = &audio; ++ pdevinfo.size_data = sizeof(audio); ++ pdevinfo.dma_mask = DMA_BIT_MASK(32); ++ hdmi->audio = platform_device_register_full(&pdevinfo); ++ ++ cec.base = hdmi->regs; ++ cec.irq = irq; ++ cec.ops = &imx_hdmi_cec_ops; ++ cec.ops_data = hdmi; ++ ++ pdevinfo.name = "dw-hdmi-cec"; ++ pdevinfo.data = &cec; ++ pdevinfo.size_data = sizeof(cec); ++ pdevinfo.dma_mask = 0; ++ ++ hdmi->cec = platform_device_register_full(&pdevinfo); ++ + dev_set_drvdata(dev, hdmi); + + return 0; +@@ -1728,15 +1750,19 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master, + { + struct imx_hdmi *hdmi = dev_get_drvdata(dev); + ++ if (!IS_ERR(hdmi->audio)) ++ platform_device_unregister(hdmi->audio); ++ if (!IS_ERR(hdmi->cec)) ++ platform_device_unregister(hdmi->cec); ++ + /* Disable all interrupts */ + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + +- hdmi->connector.funcs->destroy(&hdmi->connector); ++ hdmi->ddc_conn->connector.funcs->destroy(&hdmi->ddc_conn->connector); + hdmi->encoder.funcs->destroy(&hdmi->encoder); + + clk_disable_unprepare(hdmi->iahb_clk); + clk_disable_unprepare(hdmi->isfr_clk); +- i2c_put_adapter(hdmi->ddc); + } + + static const struct component_ops hdmi_ops = { +diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c +index fe4c1ef4e7a5..3a3bacec2e11 100644 +--- a/drivers/staging/imx-drm/imx-ldb.c ++++ b/drivers/staging/imx-drm/imx-ldb.c +@@ -24,6 +24,7 @@ + #include <drm/drmP.h> + #include <drm/drm_fb_helper.h> + #include <drm/drm_crtc_helper.h> ++#include <drm/drm_panel.h> + #include <linux/mfd/syscon.h> + #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> + #include <linux/of_address.h> +@@ -60,6 +61,7 @@ struct imx_ldb_channel { + struct imx_ldb *ldb; + struct drm_connector connector; + struct drm_encoder encoder; ++ struct drm_panel *panel; + struct device_node *child; + int chno; + void *edid; +@@ -96,6 +98,13 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector) + struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); + int num_modes = 0; + ++ if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs && ++ imx_ldb_ch->panel->funcs->get_modes) { ++ num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel); ++ if (num_modes > 0) ++ return num_modes; ++ } ++ + if (imx_ldb_ch->edid) { + drm_mode_connector_update_edid_property(connector, + imx_ldb_ch->edid); +@@ -243,6 +252,8 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder) + } + + regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); ++ ++ drm_panel_enable(imx_ldb_ch->panel); + } + + static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, +@@ -294,6 +305,8 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder) + (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0) + return; + ++ drm_panel_disable(imx_ldb_ch->panel); ++ + if (imx_ldb_ch == &ldb->channel[0]) + ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; + else if (imx_ldb_ch == &ldb->channel[1]) +@@ -379,6 +392,9 @@ static int imx_ldb_register(struct drm_device *drm, + drm_connector_init(drm, &imx_ldb_ch->connector, + &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS); + ++ if (imx_ldb_ch->panel) ++ drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector); ++ + drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, + &imx_ldb_ch->encoder); + +@@ -493,6 +509,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) + + for_each_child_of_node(np, child) { + struct imx_ldb_channel *channel; ++ struct device_node *panel_node; + + ret = of_property_read_u32(child, "reg", &i); + if (ret || i < 0 || i > 1) +@@ -556,6 +573,10 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) + return -EINVAL; + } + ++ panel_node = of_parse_phandle(child, "fsl,panel", 0); ++ if (panel_node) ++ channel->panel = of_drm_find_panel(panel_node); ++ + ret = imx_ldb_register(drm, channel); + if (ret) + return ret; +diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c +index a23f4f773146..6eafba5072ad 100644 +--- a/drivers/staging/imx-drm/imx-tve.c ++++ b/drivers/staging/imx-drm/imx-tve.c +@@ -22,7 +22,6 @@ + #include <linux/clk-provider.h> + #include <linux/component.h> + #include <linux/module.h> +-#include <linux/i2c.h> + #include <linux/regmap.h> + #include <linux/regulator/consumer.h> + #include <linux/spinlock.h> +@@ -31,6 +30,7 @@ + #include <drm/drm_fb_helper.h> + #include <drm/drm_crtc_helper.h> + ++#include "drm-ddc-connector.h" + #include "ipu-v3/imx-ipu-v3.h" + #include "imx-drm.h" + +@@ -111,7 +111,7 @@ enum { + }; + + struct imx_tve { +- struct drm_connector connector; ++ struct drm_ddc_connector *ddc_conn; + struct drm_encoder encoder; + struct device *dev; + spinlock_t lock; /* register lock */ +@@ -120,7 +120,6 @@ struct imx_tve { + + struct regmap *regmap; + struct regulator *dac_reg; +- struct i2c_adapter *ddc; + struct clk *clk; + struct clk *di_sel_clk; + struct clk_hw clk_hw_di; +@@ -219,35 +218,10 @@ static int tve_setup_vga(struct imx_tve *tve) + return 0; + } + +-static enum drm_connector_status imx_tve_connector_detect( +- struct drm_connector *connector, bool force) +-{ +- return connector_status_connected; +-} +- +-static int imx_tve_connector_get_modes(struct drm_connector *connector) +-{ +- struct imx_tve *tve = con_to_tve(connector); +- struct edid *edid; +- int ret = 0; +- +- if (!tve->ddc) +- return 0; +- +- edid = drm_get_edid(connector, tve->ddc); +- if (edid) { +- drm_mode_connector_update_edid_property(connector, edid); +- ret = drm_add_edid_modes(connector, edid); +- kfree(edid); +- } +- +- return ret; +-} +- + static int imx_tve_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) + { +- struct imx_tve *tve = con_to_tve(connector); ++ struct imx_tve *tve = to_ddc_conn(connector)->private; + unsigned long rate; + int ret; + +@@ -274,7 +248,7 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector, + static struct drm_encoder *imx_tve_connector_best_encoder( + struct drm_connector *connector) + { +- struct imx_tve *tve = con_to_tve(connector); ++ struct imx_tve *tve = drm_ddc_private(connector); + + return &tve->encoder; + } +@@ -362,15 +336,8 @@ static void imx_tve_encoder_disable(struct drm_encoder *encoder) + tve_disable(tve); + } + +-static struct drm_connector_funcs imx_tve_connector_funcs = { +- .dpms = drm_helper_connector_dpms, +- .fill_modes = drm_helper_probe_single_connector_modes, +- .detect = imx_tve_connector_detect, +- .destroy = imx_drm_connector_destroy, +-}; +- + static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { +- .get_modes = imx_tve_connector_get_modes, ++ .get_modes = drm_ddc_connector_get_modes, + .best_encoder = imx_tve_connector_best_encoder, + .mode_valid = imx_tve_connector_mode_valid, + }; +@@ -513,12 +480,11 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve) + drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs, + encoder_type); + +- drm_connector_helper_add(&tve->connector, ++ drm_connector_helper_add(&tve->ddc_conn->connector, + &imx_tve_connector_helper_funcs); +- drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs, +- DRM_MODE_CONNECTOR_VGA); ++ drm_ddc_connector_add(drm, tve->ddc_conn, DRM_MODE_CONNECTOR_VGA); + +- drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder); ++ drm_mode_connector_attach_encoder(&tve->ddc_conn->connector, &tve->encoder); + + return 0; + } +@@ -567,7 +533,6 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = data; + struct device_node *np = dev->of_node; +- struct device_node *ddc_node; + struct imx_tve *tve; + struct resource *res; + void __iomem *base; +@@ -579,15 +544,13 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) + if (!tve) + return -ENOMEM; + ++ tve->ddc_conn = drm_ddc_connector_create(drm, np, tve); ++ if (IS_ERR(tve->ddc_conn)) ++ return PTR_ERR(tve->ddc_conn); ++ + tve->dev = dev; + spin_lock_init(&tve->lock); + +- ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); +- if (ddc_node) { +- tve->ddc = of_find_i2c_adapter_by_node(ddc_node); +- of_node_put(ddc_node); +- } +- + tve->mode = of_get_tve_mode(np); + if (tve->mode != TVE_MODE_VGA) { + dev_err(dev, "only VGA mode supported, currently\n"); +@@ -694,7 +657,7 @@ static void imx_tve_unbind(struct device *dev, struct device *master, + { + struct imx_tve *tve = dev_get_drvdata(dev); + +- tve->connector.funcs->destroy(&tve->connector); ++ tve->ddc_conn->connector.funcs->destroy(&tve->ddc_conn->connector); + tve->encoder.funcs->destroy(&tve->encoder); + + if (!IS_ERR(tve->dac_reg)) +diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +index c4d14ead5837..c2c6fab05eaa 100644 +--- a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h ++++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +@@ -76,6 +76,7 @@ enum ipu_channel_irq { + IPU_IRQ_EOS = 192, + }; + ++int ipu_map_irq(struct ipu_soc *ipu, int irq); + int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, + enum ipu_channel_irq irq); + +@@ -114,8 +115,10 @@ struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel); + void ipu_dc_put(struct ipu_dc *dc); + int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, + u32 pixel_fmt, u32 width); ++void ipu_dc_enable(struct ipu_soc *ipu); + void ipu_dc_enable_channel(struct ipu_dc *dc); + void ipu_dc_disable_channel(struct ipu_dc *dc); ++void ipu_dc_disable(struct ipu_soc *ipu); + + /* + * IPU Display Interface (di) functions +@@ -152,8 +155,10 @@ void ipu_dmfc_put(struct dmfc_channel *dmfc); + + struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow); + void ipu_dp_put(struct ipu_dp *); ++int ipu_dp_enable(struct ipu_soc *ipu); + int ipu_dp_enable_channel(struct ipu_dp *dp); + void ipu_dp_disable_channel(struct ipu_dp *dp); ++void ipu_dp_disable(struct ipu_soc *ipu); + int ipu_dp_setup_channel(struct ipu_dp *dp, + enum ipu_color_space in, enum ipu_color_space out); + int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos); +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c +index ca85d3d70ae3..8fb4c207f3f1 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c +@@ -697,6 +697,12 @@ int ipu_idmac_enable_channel(struct ipuv3_channel *channel) + } + EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); + ++bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) ++{ ++ return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); ++} ++EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); ++ + int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) + { + struct ipu_soc *ipu = channel->ipu; +@@ -714,6 +720,22 @@ int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) + } + EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); + ++int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) ++{ ++ unsigned long timeout; ++ ++ timeout = jiffies + msecs_to_jiffies(ms); ++ ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); ++ while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { ++ if (time_after(jiffies, timeout)) ++ return -ETIMEDOUT; ++ cpu_relax(); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ipu_wait_interrupt); ++ + int ipu_idmac_disable_channel(struct ipuv3_channel *channel) + { + struct ipu_soc *ipu = channel->ipu; +@@ -933,15 +955,22 @@ static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) + chained_irq_exit(chip, desc); + } + +-int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, +- enum ipu_channel_irq irq_type) ++int ipu_map_irq(struct ipu_soc *ipu, int irq) + { +- int irq = irq_linear_revmap(ipu->domain, irq_type + channel->num); ++ int virq; + +- if (!irq) +- irq = irq_create_mapping(ipu->domain, irq_type + channel->num); ++ virq = irq_linear_revmap(ipu->domain, irq); ++ if (!virq) ++ virq = irq_create_mapping(ipu->domain, irq); + +- return irq; ++ return virq; ++} ++EXPORT_SYMBOL_GPL(ipu_map_irq); ++ ++int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, ++ enum ipu_channel_irq irq_type) ++{ ++ return ipu_map_irq(ipu, irq_type + channel->num); + } + EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); + +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c +index d5de8bb5c803..97cf6fad2427 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c +@@ -18,6 +18,7 @@ + #include <linux/types.h> + #include <linux/errno.h> + #include <linux/delay.h> ++#include <linux/interrupt.h> + #include <linux/io.h> + + #include "../imx-drm.h" +@@ -92,6 +93,7 @@ enum ipu_dc_map { + IPU_DC_MAP_GBR24, /* TVEv2 */ + IPU_DC_MAP_BGR666, + IPU_DC_MAP_BGR24, ++ IPU_DC_MAP_RGB666, + }; + + struct ipu_dc { +@@ -110,6 +112,9 @@ struct ipu_dc_priv { + struct device *dev; + struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; + struct mutex mutex; ++ struct completion comp; ++ int dc_irq; ++ int dp_irq; + }; + + static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) +@@ -155,6 +160,8 @@ static int ipu_pixfmt_to_map(u32 fmt) + return IPU_DC_MAP_BGR666; + case V4L2_PIX_FMT_BGR24: + return IPU_DC_MAP_BGR24; ++ case V4L2_PIX_FMT_RGB666: ++ return IPU_DC_MAP_RGB666; + default: + return -EINVAL; + } +@@ -220,12 +227,16 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, + writel(0x0, dc->base + DC_WR_CH_ADDR); + writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); + +- ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); +- + return 0; + } + EXPORT_SYMBOL_GPL(ipu_dc_init_sync); + ++void ipu_dc_enable(struct ipu_soc *ipu) ++{ ++ ipu_module_enable(ipu, IPU_CONF_DC_EN); ++} ++EXPORT_SYMBOL_GPL(ipu_dc_enable); ++ + void ipu_dc_enable_channel(struct ipu_dc *dc) + { + int di; +@@ -239,41 +250,55 @@ void ipu_dc_enable_channel(struct ipu_dc *dc) + } + EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); + ++static irqreturn_t dc_irq_handler(int irq, void *dev_id) ++{ ++ struct ipu_dc *dc = dev_id; ++ u32 reg; ++ ++ reg = readl(dc->base + DC_WR_CH_CONF); ++ reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; ++ writel(reg, dc->base + DC_WR_CH_CONF); ++ ++ /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */ ++ ++ complete(&dc->priv->comp); ++ return IRQ_HANDLED; ++} ++ + void ipu_dc_disable_channel(struct ipu_dc *dc) + { + struct ipu_dc_priv *priv = dc->priv; ++ int irq, ret; + u32 val; +- int irq = 0, timeout = 50; + ++ /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */ + if (dc->chno == 1) +- irq = IPU_IRQ_DC_FC_1; ++ irq = priv->dc_irq; + else if (dc->chno == 5) +- irq = IPU_IRQ_DP_SF_END; ++ irq = priv->dp_irq; + else + return; + +- /* should wait for the interrupt here */ +- mdelay(50); ++ init_completion(&priv->comp); ++ enable_irq(irq); ++ ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50)); ++ disable_irq(irq); ++ if (ret <= 0) { ++ dev_warn(priv->dev, "DC stop timeout after 50 ms\n"); + +- if (dc->di == 0) +- val = 0x00000002; +- else +- val = 0x00000020; +- +- /* Wait for DC triple buffer to empty */ +- while ((readl(priv->dc_reg + DC_STAT) & val) != val) { +- usleep_range(2000, 20000); +- timeout -= 2; +- if (timeout <= 0) +- break; ++ val = readl(dc->base + DC_WR_CH_CONF); ++ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; ++ writel(val, dc->base + DC_WR_CH_CONF); + } +- +- val = readl(dc->base + DC_WR_CH_CONF); +- val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; +- writel(val, dc->base + DC_WR_CH_CONF); + } + EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); + ++void ipu_dc_disable(struct ipu_soc *ipu) ++{ ++ ipu_module_disable(ipu, IPU_CONF_DC_EN); ++} ++EXPORT_SYMBOL_GPL(ipu_dc_disable); ++ + static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, + int byte_num, int offset, int mask) + { +@@ -340,7 +365,7 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, + struct ipu_dc_priv *priv; + static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, + 0x78, 0, 0x94, 0xb4}; +- int i; ++ int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -361,6 +386,23 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, + priv->channels[i].base = priv->dc_reg + channel_offsets[i]; + } + ++ priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1); ++ if (!priv->dc_irq) ++ return -EINVAL; ++ ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL, ++ &priv->channels[1]); ++ if (ret < 0) ++ return ret; ++ disable_irq(priv->dc_irq); ++ priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END); ++ if (!priv->dp_irq) ++ return -EINVAL; ++ ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL, ++ &priv->channels[5]); ++ if (ret < 0) ++ return ret; ++ disable_irq(priv->dp_irq); ++ + writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | + DC_WR_CH_CONF_PROG_DI_ID, + priv->channels[1].base + DC_WR_CH_CONF); +@@ -404,6 +446,12 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, + ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ + ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ + ++ /* rgb666 */ ++ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB666); ++ ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 0, 5, 0xfc); /* blue */ ++ ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 1, 11, 0xfc); /* green */ ++ ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 2, 17, 0xfc); /* red */ ++ + return 0; + } + +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +index 82a9ebad697c..849b3e120ef0 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +@@ -595,7 +595,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + } + } + +- if (!sig->clk_pol) ++ if (sig->clk_pol) + di_gen |= DI_GEN_POLARITY_DISP_CLK; + + ipu_di_write(di, di_gen, DI_GENERAL); +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c +index 45213017fa4b..59f182b28fc1 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c +@@ -28,7 +28,12 @@ + #define DMFC_GENERAL1 0x0014 + #define DMFC_GENERAL2 0x0018 + #define DMFC_IC_CTRL 0x001c +-#define DMFC_STAT 0x0020 ++#define DMFC_WR_CHAN_ALT 0x0020 ++#define DMFC_WR_CHAN_DEF_ALT 0x0024 ++#define DMFC_DP_CHAN_ALT 0x0028 ++#define DMFC_DP_CHAN_DEF_ALT 0x002c ++#define DMFC_GENERAL1_ALT 0x0030 ++#define DMFC_STAT 0x0034 + + #define DMFC_WR_CHAN_1_28 0 + #define DMFC_WR_CHAN_2_41 8 +@@ -133,6 +138,20 @@ int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) + } + EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); + ++static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(1000); ++ ++ while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) { ++ if (time_after(jiffies, timeout)) { ++ dev_warn(priv->dev, ++ "Timeout waiting for DMFC FIFOs to clear\n"); ++ break; ++ } ++ cpu_relax(); ++ } ++} ++ + void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) + { + struct ipu_dmfc_priv *priv = dmfc->priv; +@@ -141,8 +160,10 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) + + priv->use_count--; + +- if (!priv->use_count) ++ if (!priv->use_count) { ++ ipu_dmfc_wait_fifos(priv); + ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); ++ } + + if (priv->use_count < 0) + priv->use_count = 0; +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c +index 58f87c8d7c07..d90f82a87d19 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c +@@ -215,10 +215,9 @@ int ipu_dp_setup_channel(struct ipu_dp *dp, + } + EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); + +-int ipu_dp_enable_channel(struct ipu_dp *dp) ++int ipu_dp_enable(struct ipu_soc *ipu) + { +- struct ipu_flow *flow = to_flow(dp); +- struct ipu_dp_priv *priv = flow->priv; ++ struct ipu_dp_priv *priv = ipu->dp_priv; + + mutex_lock(&priv->mutex); + +@@ -227,15 +226,28 @@ int ipu_dp_enable_channel(struct ipu_dp *dp) + + priv->use_count++; + +- if (dp->foreground) { +- u32 reg; ++ mutex_unlock(&priv->mutex); + +- reg = readl(flow->base + DP_COM_CONF); +- reg |= DP_COM_CONF_FG_EN; +- writel(reg, flow->base + DP_COM_CONF); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ipu_dp_enable); + +- ipu_srm_dp_sync_update(priv->ipu); +- } ++int ipu_dp_enable_channel(struct ipu_dp *dp) ++{ ++ struct ipu_flow *flow = to_flow(dp); ++ struct ipu_dp_priv *priv = flow->priv; ++ u32 reg; ++ ++ if (!dp->foreground) ++ return 0; ++ ++ mutex_lock(&priv->mutex); ++ ++ reg = readl(flow->base + DP_COM_CONF); ++ reg |= DP_COM_CONF_FG_EN; ++ writel(reg, flow->base + DP_COM_CONF); ++ ++ ipu_srm_dp_sync_update(priv->ipu); + + mutex_unlock(&priv->mutex); + +@@ -247,25 +259,38 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) + { + struct ipu_flow *flow = to_flow(dp); + struct ipu_dp_priv *priv = flow->priv; ++ u32 reg, csc; ++ ++ if (!dp->foreground) ++ return; + + mutex_lock(&priv->mutex); + +- priv->use_count--; ++ reg = readl(flow->base + DP_COM_CONF); ++ csc = reg & DP_COM_CONF_CSC_DEF_MASK; ++ if (csc == DP_COM_CONF_CSC_DEF_FG) ++ reg &= ~DP_COM_CONF_CSC_DEF_MASK; + +- if (dp->foreground) { +- u32 reg, csc; ++ reg &= ~DP_COM_CONF_FG_EN; ++ writel(reg, flow->base + DP_COM_CONF); + +- reg = readl(flow->base + DP_COM_CONF); +- csc = reg & DP_COM_CONF_CSC_DEF_MASK; +- if (csc == DP_COM_CONF_CSC_DEF_FG) +- reg &= ~DP_COM_CONF_CSC_DEF_MASK; ++ writel(0, flow->base + DP_FG_POS); ++ ipu_srm_dp_sync_update(priv->ipu); + +- reg &= ~DP_COM_CONF_FG_EN; +- writel(reg, flow->base + DP_COM_CONF); ++ if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC)) ++ ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50); + +- writel(0, flow->base + DP_FG_POS); +- ipu_srm_dp_sync_update(priv->ipu); +- } ++ mutex_unlock(&priv->mutex); ++} ++EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); ++ ++void ipu_dp_disable(struct ipu_soc *ipu) ++{ ++ struct ipu_dp_priv *priv = ipu->dp_priv; ++ ++ mutex_lock(&priv->mutex); ++ ++ priv->use_count--; + + if (!priv->use_count) + ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); +@@ -275,7 +300,7 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) + + mutex_unlock(&priv->mutex); + } +-EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); ++EXPORT_SYMBOL_GPL(ipu_dp_disable); + + struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) + { +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h +index 4df00501adc2..bfc1b3366488 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h +@@ -185,6 +185,9 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu); + int ipu_module_enable(struct ipu_soc *ipu, u32 mask); + int ipu_module_disable(struct ipu_soc *ipu, u32 mask); + ++bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); ++int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms); ++ + int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, + unsigned long base, u32 module, struct clk *ipu_clk); + void ipu_di_exit(struct ipu_soc *ipu, int id); +diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c +index c48f640db006..47bec5e17358 100644 +--- a/drivers/staging/imx-drm/ipuv3-crtc.c ++++ b/drivers/staging/imx-drm/ipuv3-crtc.c +@@ -60,24 +60,32 @@ struct ipu_crtc { + + static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) + { ++ struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); ++ + if (ipu_crtc->enabled) + return; + +- ipu_di_enable(ipu_crtc->di); +- ipu_dc_enable_channel(ipu_crtc->dc); ++ ipu_dc_enable(ipu); + ipu_plane_enable(ipu_crtc->plane[0]); ++ /* Start DC channel and DI after IDMAC */ ++ ipu_dc_enable_channel(ipu_crtc->dc); ++ ipu_di_enable(ipu_crtc->di); + + ipu_crtc->enabled = 1; + } + + static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) + { ++ struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); ++ + if (!ipu_crtc->enabled) + return; + +- ipu_plane_disable(ipu_crtc->plane[0]); ++ /* Stop DC channel and DI before IDMAC */ + ipu_dc_disable_channel(ipu_crtc->dc); + ipu_di_disable(ipu_crtc->di); ++ ipu_plane_disable(ipu_crtc->plane[0]); ++ ipu_dc_disable(ipu); + + ipu_crtc->enabled = 0; + } +@@ -158,7 +166,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, + sig_cfg.Vsync_pol = 1; + + sig_cfg.enable_pol = 1; +- sig_cfg.clk_pol = 1; ++ sig_cfg.clk_pol = 0; + sig_cfg.width = mode->hdisplay; + sig_cfg.height = mode->vdisplay; + sig_cfg.pixel_fmt = out_pixel_fmt; +diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/staging/imx-drm/ipuv3-plane.c +index 27a8d735dae0..5697e59ddf1d 100644 +--- a/drivers/staging/imx-drm/ipuv3-plane.c ++++ b/drivers/staging/imx-drm/ipuv3-plane.c +@@ -239,6 +239,8 @@ int ipu_plane_get_resources(struct ipu_plane *ipu_plane) + + void ipu_plane_enable(struct ipu_plane *ipu_plane) + { ++ if (ipu_plane->dp) ++ ipu_dp_enable(ipu_plane->ipu); + ipu_dmfc_enable_channel(ipu_plane->dmfc); + ipu_idmac_enable_channel(ipu_plane->ipu_ch); + if (ipu_plane->dp) +@@ -257,6 +259,8 @@ void ipu_plane_disable(struct ipu_plane *ipu_plane) + ipu_dp_disable_channel(ipu_plane->dp); + ipu_idmac_disable_channel(ipu_plane->ipu_ch); + ipu_dmfc_disable_channel(ipu_plane->dmfc); ++ if (ipu_plane->dp) ++ ipu_dp_disable(ipu_plane->ipu); + } + + static void ipu_plane_dpms(struct ipu_plane *ipu_plane, int mode) +diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c +index c60b6c645f42..01b7ce50fc30 100644 +--- a/drivers/staging/imx-drm/parallel-display.c ++++ b/drivers/staging/imx-drm/parallel-display.c +@@ -219,6 +219,8 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) + imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB565; + else if (!strcmp(fmt, "bgr666")) + imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666; ++ else if (!strcmp(fmt, "rgb666")) ++ imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB666; + } + + panel_node = of_parse_phandle(np, "fsl,panel", 0); +diff --git a/include/linux/cec-dev.h b/include/linux/cec-dev.h +new file mode 100644 +index 000000000000..76a7d7f6a72d +--- /dev/null ++++ b/include/linux/cec-dev.h +@@ -0,0 +1,69 @@ ++#ifndef _LINUX_CEC_DEV_H ++#define _LINUX_CEC_DEV_H ++ ++#include <linux/cdev.h> ++#include <linux/list.h> ++#include <linux/mutex.h> ++#include <linux/spinlock.h> ++#include <linux/wait.h> ++ ++#include <uapi/linux/cec-dev.h> ++ ++struct device; ++ ++struct cec_dev { ++ struct cdev cdev; ++ dev_t devn; ++ ++ struct mutex mutex; ++ unsigned users; ++ ++ spinlock_t lock; ++ wait_queue_head_t waitq; ++ struct list_head events; ++ u8 write_busy; ++ ++ u8 retries; ++ u16 addresses; ++ u16 physical; ++ ++ int (*open)(struct cec_dev *); ++ void (*release)(struct cec_dev *); ++ void (*send_message)(struct cec_dev *, u8 *, size_t); ++ void (*set_address)(struct cec_dev *, unsigned); ++}; ++ ++void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len); ++ ++static inline void cec_dev_receive(struct cec_dev *cec_dev, u8 *msg, ++ unsigned len) ++{ ++ cec_dev_event(cec_dev, MESSAGE_TYPE_RECEIVE_SUCCESS, msg, len); ++} ++ ++static inline void cec_dev_send_complete(struct cec_dev *cec_dev, int ack) ++{ ++ cec_dev->retries = 0; ++ cec_dev->write_busy = 0; ++ ++ cec_dev_event(cec_dev, ack ? MESSAGE_TYPE_SEND_SUCCESS : ++ MESSAGE_TYPE_NOACK, NULL, 0); ++} ++ ++static inline void cec_dev_disconnect(struct cec_dev *cec_dev) ++{ ++ cec_dev->physical = 0; ++ cec_dev_event(cec_dev, MESSAGE_TYPE_DISCONNECTED, NULL, 0); ++} ++ ++static inline void cec_dev_connect(struct cec_dev *cec_dev, u32 phys) ++{ ++ cec_dev->physical = phys; ++ cec_dev_event(cec_dev, MESSAGE_TYPE_CONNECTED, NULL, 0); ++} ++ ++void cec_dev_init(struct cec_dev *cec_dev, struct module *); ++int cec_dev_add(struct cec_dev *cec_dev, struct device *, const char *name); ++void cec_dev_remove(struct cec_dev *cec_dev); ++ ++#endif +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index cb61ea4d6945..2fdfe30295d5 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -278,6 +278,7 @@ struct mmc_host { + #define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \ + MMC_CAP2_PACKED_WR) + #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ ++#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 15) + + mmc_pm_flag_t pm_caps; /* supported pm features */ + +@@ -293,6 +294,11 @@ struct mmc_host { + unsigned long clkgate_delay; + #endif + ++ /* card specific properties to deal with power and reset */ ++ struct regulator *card_regulator; /* External VCC needed by the card */ ++ struct gpio_desc *card_reset_gpios[2]; /* External resets, active low */ ++ struct clk *card_clk; /* External clock needed by the card */ ++ + /* host specific block data */ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ + unsigned short max_segs; /* see blk_queue_max_segments */ +@@ -391,6 +397,8 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) + wake_up_process(host->sdio_irq_thread); + } + ++void sdio_run_irqs(struct mmc_host *host); ++ + #ifdef CONFIG_REGULATOR + int mmc_regulator_get_ocrmask(struct regulator *supply); + int mmc_regulator_set_ocr(struct mmc_host *mmc, +diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h +index 7be12b883485..08abe9941884 100644 +--- a/include/linux/mmc/sdhci.h ++++ b/include/linux/mmc/sdhci.h +@@ -57,12 +57,8 @@ struct sdhci_host { + #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) + /* Controller reports inverted write-protect state */ + #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) +-/* Controller has nonstandard clock management */ +-#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) + /* Controller does not like fast PIO transfers */ + #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) +-/* Controller losing signal/interrupt enable states after reset */ +-#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) + /* Controller has to be forced to use block size of 2048 bytes */ + #define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) + /* Controller cannot do multi-block transfers */ +@@ -147,6 +143,7 @@ struct sdhci_host { + + bool runtime_suspended; /* Host is runtime suspended */ + bool bus_on; /* Bus power prevents runtime suspend */ ++ bool preset_enabled; /* Preset is enabled */ + + struct mmc_request *mrq; /* Current request */ + struct mmc_command *cmd; /* Current command */ +@@ -164,8 +161,7 @@ struct sdhci_host { + dma_addr_t adma_addr; /* Mapped ADMA descr. table */ + dma_addr_t align_addr; /* Mapped bounce buffer */ + +- struct tasklet_struct card_tasklet; /* Tasklet structures */ +- struct tasklet_struct finish_tasklet; ++ struct tasklet_struct finish_tasklet; /* Tasklet structures */ + + struct timer_list timer; /* Timer for timeouts */ + +@@ -177,6 +173,13 @@ struct sdhci_host { + unsigned int ocr_avail_mmc; + u32 ocr_mask; /* available voltages */ + ++ unsigned timing; /* Current timing */ ++ ++ u32 thread_isr; ++ ++ /* cached registers */ ++ u32 ier; ++ + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ + unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ + +diff --git a/include/uapi/linux/cec-dev.h b/include/uapi/linux/cec-dev.h +new file mode 100644 +index 000000000000..fb7a41704c77 +--- /dev/null ++++ b/include/uapi/linux/cec-dev.h +@@ -0,0 +1,34 @@ ++#ifndef _UAPI_LINUX_CEC_DEV_H ++#define _UAPI_LINUX_CEC_DEV_H ++ ++#include <linux/ioctl.h> ++#include <linux/types.h> ++ ++#define MAX_MESSAGE_LEN 16 ++ ++enum { ++ HDMICEC_IOC_MAGIC = 'H', ++ /* This is wrong: we pass the argument as a number, not a pointer */ ++ HDMICEC_IOC_O_SETLOGICALADDRESS = _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char), ++ HDMICEC_IOC_SETLOGICALADDRESS = _IO(HDMICEC_IOC_MAGIC, 1), ++ HDMICEC_IOC_STARTDEVICE = _IO(HDMICEC_IOC_MAGIC, 2), ++ HDMICEC_IOC_STOPDEVICE = _IO(HDMICEC_IOC_MAGIC, 3), ++ HDMICEC_IOC_GETPHYADDRESS = _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]), ++}; ++ ++enum { ++ MESSAGE_TYPE_RECEIVE_SUCCESS = 1, ++ MESSAGE_TYPE_NOACK, ++ MESSAGE_TYPE_DISCONNECTED, ++ MESSAGE_TYPE_CONNECTED, ++ MESSAGE_TYPE_SEND_SUCCESS, ++ MESSAGE_TYPE_SEND_ERROR, ++}; ++ ++struct cec_user_event { ++ __u32 event_type; ++ __u32 msg_len; ++ __u8 msg[MAX_MESSAGE_LEN]; ++}; ++ ++#endif +diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h +index ea468ee8fe21..d5d818a86676 100644 +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -299,6 +299,7 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */ + #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ + #define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */ ++#define V4L2_PIX_FMT_RGB666 v4l2_fourcc('R', 'G', 'B', 'H') /* 18 RGB-6-6-6 */ + #define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ + #define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ + #define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */ +diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c +index 2585ae44e634..a86d84854c7f 100644 +--- a/sound/soc/fsl/imx-pcm-dma.c ++++ b/sound/soc/fsl/imx-pcm-dma.c +@@ -44,7 +44,7 @@ static const struct snd_pcm_hardware imx_pcm_hardware = { + .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 65535, /* Limited by SDMA engine */ +- .periods_min = 2, ++ .periods_min = 4, + .periods_max = 255, + .fifo_size = 0, + }; diff --git a/target/arm/cubox-i/patches/3.15/sdma-firmware-cubox-i.patch b/target/arm/cubox-i/patches/3.15/sdma-firmware-cubox-i.patch new file mode 100644 index 000000000..13f94a208 --- /dev/null +++ b/target/arm/cubox-i/patches/3.15/sdma-firmware-cubox-i.patch @@ -0,0 +1,131 @@ +diff -Nur linux-3.14.1.orig/firmware/imx/sdma/sdma-imx6q.bin.ihex linux-3.14.1/firmware/imx/sdma/sdma-imx6q.bin.ihex +--- linux-3.14.1.orig/firmware/imx/sdma/sdma-imx6q.bin.ihex 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.1/firmware/imx/sdma/sdma-imx6q.bin.ihex 2014-04-24 07:58:28.028222137 +0200 +@@ -0,0 +1,116 @@ ++:1000000053444D4101000000010000001C000000AD ++:1000100026000000B40000007A0600008202000002 ++:10002000FFFFFFFF00000000FFFFFFFFFFFFFFFFDC ++:10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0 ++:10004000FFFFFFFFFFFFFFFF6A1A0000FFFFFFFF38 ++:10005000EB020000BB180000FFFFFFFF08040000D8 ++:10006000FFFFFFFFC0030000FFFFFFFFFFFFFFFFD9 ++:10007000FFFFFFFFAB020000FFFFFFFF7B0300005D ++:10008000FFFFFFFFFFFFFFFF4C0400006E040000B6 ++:10009000FFFFFFFF00180000FFFFFFFFFFFFFFFF54 ++:1000A000000000000018000062180000161A00008E ++:1000B000061B0000E3C1DB57E35FE357F352016A1D ++:1000C0008F00D500017D8D00A005EB5D7804037DD8 ++:1000D00079042C7D367C79041F7CEE56000F600677 ++:1000E000057D0965437E0A62417E20980A623E7E54 ++:1000F00009653C7E12051205AD026007037DFB55C4 ++:10010000D36D2B98FB55041DD36DC86A2F7F011F3B ++:1001100003200048E47C5398FB55D76D1500057803 ++:100120000962C86A0962C86AD76D5298FB55D76DD3 ++:100130001500150005780A62C86A0A62C86AD76D98 ++:100140005298FB55D76D15001500150005780B6208 ++:10015000C86A0B62C86AD76D097CDF6D077F000033 ++:10016000EB55004D077DFAC1E35706980700CC68B0 ++:100170000C6813C20AC20398D9C1E3C1DB57E35F1D ++:10018000E357F352216A8F00D500017D8D00A00551 ++:10019000EB5DFB567804037D79042A7D317C79047C ++:1001A000207C700B1103EB53000F6003057D096584 ++:1001B000377E0A62357E86980A62327E0965307E15 ++:1001C00012051205AD026007027C065A8E98265A67 ++:1001D000277F011F03200048E87C700B1103135395 ++:1001E000AF98150004780962065A0962265AAE983B ++:1001F0001500150004780A62065A0A62265AAE985B ++:1002000015001500150004780B62065A0B62265A79 ++:10021000077C0000EB55004D067DFAC1E357699855 ++:1002200007000C6813C20AC26698700B11031353BF ++:100230006C07017CD9C1FB5E8A066B07017CD9C1C2 ++:10024000F35EDB59D3588F0110010F398B003CC18D ++:100250002B7DC05AC85B4EC1277C88038906E35CAE ++:10026000FF0D1105FF1DBC053E07004D187D7008F0 ++:1002700011007E07097D7D07027D2852E698F8521D ++:10028000DB54BC02CC02097C7C07027D2852EF982B ++:10029000F852D354BC02CC02097D0004DD988B00D7 ++:1002A000C052C85359C1D67D0002CD98FF08BF0087 ++:1002B0007F07157D8804D500017D8D00A005EB5DCD ++:1002C0008F0212021202FF3ADA05027C3E071899E9 ++:1002D000A402DD02027D3E0718995E071899EB55CE ++:1002E0009805EB5DF352FB546A07267D6C07017D90 ++:1002F00055996B07577C6907047D6807027D010EDD ++:100300002F999358D600017D8E009355A005935DDB ++:10031000A00602780255045D1D7C004E087C69072A ++:10032000037D0255177E3C99045D147F8906935026 ++:100330000048017D2799A099150006780255045DB3 ++:100340004F070255245D2F07017CA09917006F0706 ++:10035000017C012093559D000700A7D9F598D36C27 ++:100360006907047D6807027D010E64999358D600E1 ++:10037000017D8E009355A005935DA006027802557D ++:10038000C86D0F7C004E087C6907037D0255097E0D ++:100390007199C86D067F890693500048017D5C996C ++:1003A000A0999A99C36A6907047D6807027D010EC6 ++:1003B00087999358D600017D8E009355A005935DD3 ++:1003C000A0060278C865045D0F7C004E087C6907B2 ++:1003D000037DC865097E9499045D067F8906935064 ++:1003E0000048017D7F99A09993559D000700FF6CFF ++:1003F000A7D9F5980000E354EB55004D017CF59822 ++:10040000DD98E354EB55FF0A1102FF1A7F07027CC7 ++:10041000A005B4999D008C05BA05A0051002BA0488 ++:10042000AD0454040600E3C1DB57FB52C36AF35228 ++:10043000056A8F00D500017D8D00A005EB5D780475 ++:10044000037D79042B7D1E7C7904337CEE56000FEE ++:10045000FB556007027DC36DD599041DC36DC8624D ++:100460003B7E6006027D10021202096A357F12028D ++:10047000096A327F1202096A2F7F011F0320004898 ++:10048000E77C099AFB55C76D150015001500057826 ++:10049000C8620B6AC8620B6AC76D089AFB55C76DC4 ++:1004A000150015000578C8620A6AC8620A6AC76D35 ++:1004B000089AFB55C76D15000578C862096AC862BD ++:1004C000096AC76D097C286A077F0000EB55004D5B ++:1004D000057DFAC1DB57BF9977C254040AC2BA99A5 ++:1004E000D9C1E3C1DB57F352056A8F00D500017D06 ++:1004F0008D00A005FB567804037D7904297D1F7CBF ++:1005000079042E7CE35D700D1105ED55000F600739 ++:10051000027D0652329A2652337E6005027D100219 ++:100520001202096A2D7F1202096A2A7F1202096AE1 ++:10053000277F011F03200048EA7CE3555D9A1500E0 ++:1005400015001500047806520B6A26520B6A5C9A55 ++:1005500015001500047806520A6A26520A6A5C9A47 ++:10056000150004780652096A2652096A097C286A2D ++:10057000077F0000DB57004D057DFAC1DB571B9A52 ++:1005800077C254040AC2189AE3C1DB57F352056AD2 ++:10059000FB568E02941AC36AC8626902247D941EB7 ++:1005A000C36ED36EC8624802C86A9426981EC36E92 ++:1005B000D36EC8624C02C86A9826C36E981EC36E7A ++:1005C000C8629826C36E6002097CC8626E02247DF0 ++:1005D000096A1E7F0125004D257D849A286A187FAF ++:1005E00004627AC2B89AE36E8F00D805017D8D004F ++:1005F000A005C8626E02107D096A0A7F0120F97C9D ++:10060000286A067F0000004D0D7DFAC1DB576E9A07 ++:10061000070004620C6AB59A286AFA7F04627AC2FB ++:1006200058045404286AF47F0AC26B9AD9C1E3C102 ++:10063000DB57F352056AFB568E02941A0252690286 ++:100640001D7D941E06524802065A9426981E065294 ++:100650004C02065A9826981E065260020A7C98267A ++:1006600006526E02237D096A1D7F0125004D247DFF ++:10067000D19A286A177F04627AC2029B8F00D8053C ++:10068000017D8D00A00506526E02107D096A0A7F69 ++:100690000120F97C286A067F0000004D0D7DFAC11B ++:1006A000DB57C19A070004620C6AFF9A286AFA7F36 ++:1006B00004627AC258045404286AF47F0AC2BE9ABB ++:1006C000016E0B612F7E0B622D7E0B632B7E0C0D5A ++:1006D0001704170417049D04081DCC05017C0C0D9C ++:1006E000D16A000F4207C86FDD6F1C7F8E009D002E ++:1006F00001680B67177ED56B04080278C86F120774 ++:10070000117C0B670F7E04080278C86F12070A7C01 ++:10071000DD6F087FD169010FC86FDD6F037F0101B5 ++:0E0720000004129B0700FF680C680002129B89 ++:00000001FF +diff -Nur linux-3.14.1.orig/firmware/Makefile linux-3.14.1/firmware/Makefile +--- linux-3.14.1.orig/firmware/Makefile 2014-04-14 15:50:10.000000000 +0200 ++++ linux-3.14.1/firmware/Makefile 2014-04-24 13:01:55.802324681 +0200 +@@ -61,6 +61,7 @@ + radeon/RV770_pfp.bin radeon/RV770_me.bin \ + radeon/RV730_pfp.bin radeon/RV730_me.bin \ + radeon/RV710_pfp.bin radeon/RV710_me.bin ++fw-shipped-$(CONFIG_IMX_SDMA) += imx/sdma/sdma-imx6q.bin + fw-shipped-$(CONFIG_DVB_AV7110) += av7110/bootcode.bin + fw-shipped-$(CONFIG_DVB_TTUSB_BUDGET) += ttusb-budget/dspbootcode.bin + fw-shipped-$(CONFIG_E100) += e100/d101m_ucode.bin e100/d101s_ucode.bin \ |