diff options
Diffstat (limited to 'target/arm/solidrun-imx6/patches/3.17.4')
-rw-r--r-- | target/arm/solidrun-imx6/patches/3.17.4/rmk.patch | 7601 |
1 files changed, 7601 insertions, 0 deletions
diff --git a/target/arm/solidrun-imx6/patches/3.17.4/rmk.patch b/target/arm/solidrun-imx6/patches/3.17.4/rmk.patch new file mode 100644 index 000000000..a0e0e7495 --- /dev/null +++ b/target/arm/solidrun-imx6/patches/3.17.4/rmk.patch @@ -0,0 +1,7601 @@ +diff -Nur linux-3.16.6.orig/arch/arm/boot/dts/imx6dl-hummingboard.dts linux-3.16.6/arch/arm/boot/dts/imx6dl-hummingboard.dts +--- linux-3.16.6.orig/arch/arm/boot/dts/imx6dl-hummingboard.dts 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/arch/arm/boot/dts/imx6dl-hummingboard.dts 2014-10-23 12:37:45.114220003 -0500 +@@ -56,15 +56,32 @@ + }; + }; + ++ sound-sgtl5000 { ++ audio-codec = <&sgtl5000>; ++ audio-routing = ++ "MIC_IN", "Mic Jack", ++ "Mic Jack", "Mic Bias", ++ "Headphone Jack", "HP_OUT"; ++ compatible = "fsl,imx-audio-sgtl5000"; ++ model = "On-board Codec"; ++ mux-ext-port = <5>; ++ mux-int-port = <1>; ++ ssi-controller = <&ssi1>; ++ }; ++ + sound-spdif { + compatible = "fsl,imx-audio-spdif"; +- model = "imx-spdif"; ++ model = "On-board SPDIF"; + /* IMX6 doesn't implement this yet */ + spdif-controller = <&spdif>; + spdif-out; + }; + }; + ++&audmux { ++ status = "okay"; ++}; ++ + &can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_flexcan1>; +@@ -81,16 +98,24 @@ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_i2c1>; +- +- /* +- * Not fitted on Carrier-1 board... yet + status = "okay"; + ++ /* Pro model */ + rtc: pcf8523@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + }; +- */ ++ ++ /* Pro model */ ++ sgtl5000: sgtl5000@0a { ++ clocks = <&clks 201>; ++ compatible = "fsl,sgtl5000"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_hummingboard_sgtl5000>; ++ reg = <0x0a>; ++ VDDA-supply = <®_3p3v>; ++ VDDIO-supply = <®_3p3v>; ++ }; + }; + + &i2c2 { +@@ -135,6 +160,16 @@ + >; + }; + ++ pinctrl_hummingboard_sgtl5000: hummingboard-sgtl5000 { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x130b0 /*brk*/ ++ MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x130b0 /*ok*/ ++ MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x110b0 /*brk*/ ++ MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x130b0 /*ok*/ ++ MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x130b0 ++ >; ++ }; ++ + pinctrl_hummingboard_spdif: hummingboard-spdif { + fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>; + }; +@@ -180,12 +215,19 @@ + status = "okay"; + }; + ++&ssi1 { ++ fsl,mode = "i2s-slave"; ++ status = "okay"; ++}; ++ + &usbh1 { ++ disable-over-current; + vbus-supply = <®_usbh1_vbus>; + status = "okay"; + }; + + &usbotg { ++ disable-over-current; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usbotg_id>; + vbus-supply = <®_usbotg_vbus>; +diff -Nur linux-3.16.6.orig/arch/arm/boot/dts/imx6q-cubox-i.dts linux-3.16.6/arch/arm/boot/dts/imx6q-cubox-i.dts +--- linux-3.16.6.orig/arch/arm/boot/dts/imx6q-cubox-i.dts 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/arch/arm/boot/dts/imx6q-cubox-i.dts 2014-10-23 12:26:42.106220014 -0500 +@@ -13,4 +13,8 @@ + + &sata { + status = "okay"; ++ fsl,transmit-level-mV = <1104>; ++ fsl,transmit-boost-mdB = <0>; ++ fsl,transmit-atten-16ths = <9>; ++ fsl,no-spread-spectrum; + }; +diff -Nur linux-3.16.6.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi linux-3.16.6/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +--- linux-3.16.6.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2014-10-23 12:34:41.266219992 -0500 +@@ -61,7 +61,7 @@ + + sound-spdif { + compatible = "fsl,imx-audio-spdif"; +- model = "imx-spdif"; ++ model = "Integrated SPDIF"; + /* IMX6 doesn't implement this yet */ + spdif-controller = <&spdif>; + spdif-out; +@@ -130,16 +130,23 @@ + fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>; + }; + ++ pinctrl_cubox_i_usbh1: cubox-i-usbh1 { ++ fsl,pins = <MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0>; ++ }; ++ + pinctrl_cubox_i_usbh1_vbus: cubox-i-usbh1-vbus { + fsl,pins = <MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x4001b0b0>; + }; + +- pinctrl_cubox_i_usbotg_id: cubox-i-usbotg-id { ++ pinctrl_cubox_i_usbotg: cubox-i-usbotg { + /* +- * The Cubox-i pulls this low, but as it's pointless ++ * The Cubox-i pulls ID low, but as it's pointless + * leaving it as a pull-up, even if it is just 10uA. + */ +- fsl,pins = <MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059>; ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059 ++ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0 ++ >; + }; + + pinctrl_cubox_i_usbotg_vbus: cubox-i-usbotg-vbus { +@@ -163,6 +170,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 ++ >; ++ }; + }; + }; + +@@ -173,20 +202,24 @@ + }; + + &usbh1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usbh1>; + vbus-supply = <®_usbh1_vbus>; + status = "okay"; + }; + + &usbotg { + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_cubox_i_usbotg_id>; ++ pinctrl-0 = <&pinctrl_cubox_i_usbotg>; + vbus-supply = <®_usbotg_vbus>; + status = "okay"; + }; + + &usdhc2 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2>; ++ pinctrl-1 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_100mhz>; ++ pinctrl-2 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_200mhz>; + vmmc-supply = <®_3p3v>; + cd-gpios = <&gpio1 4 0>; + status = "okay"; +diff -Nur linux-3.16.6.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi.orig linux-3.16.6/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi.orig +--- linux-3.16.6.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi.orig 2014-10-23 12:27:10.986220036 -0500 +@@ -0,0 +1,202 @@ ++/* ++ * Copyright (C) 2014 Russell King ++ */ ++#include "imx6qdl-microsom.dtsi" ++#include "imx6qdl-microsom-ar8035.dtsi" ++ ++/ { ++ ir_recv: ir-receiver { ++ compatible = "gpio-ir-receiver"; ++ gpios = <&gpio3 9 1>; ++ pinctrl-names = "default"; ++ 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"; ++ ++ reg_3p3v: 3p3v { ++ compatible = "regulator-fixed"; ++ regulator-name = "3P3V"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ reg_usbh1_vbus: usb-h1-vbus { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio1 0 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usbh1_vbus>; ++ regulator-name = "usb_h1_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ ++ reg_usbotg_vbus: usb-otg-vbus { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio3 22 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usbotg_vbus>; ++ regulator-name = "usb_otg_vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ }; ++ ++ sound-spdif { ++ compatible = "fsl,imx-audio-spdif"; ++ model = "Integrated SPDIF"; ++ /* IMX6 doesn't implement this yet */ ++ spdif-controller = <&spdif>; ++ spdif-out; ++ }; ++}; ++ ++&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>; ++ ++ status = "okay"; ++ ++ rtc: pcf8523@68 { ++ compatible = "nxp,pcf8523"; ++ reg = <0x68>; ++ }; ++}; ++ ++&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 ++ MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 ++ >; ++ }; ++ ++ pinctrl_cubox_i_ir: cubox-i-ir { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x80000000 ++ >; ++ }; ++ ++ 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>; ++ }; ++ ++ pinctrl_cubox_i_usbh1: cubox-i-usbh1 { ++ fsl,pins = <MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0>; ++ }; ++ ++ pinctrl_cubox_i_usbh1_vbus: cubox-i-usbh1-vbus { ++ fsl,pins = <MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x4001b0b0>; ++ }; ++ ++ pinctrl_cubox_i_usbotg: cubox-i-usbotg { ++ /* ++ * The Cubox-i pulls ID low, but as it's pointless ++ * leaving it as a pull-up, even if it is just 10uA. ++ */ ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059 ++ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0 ++ >; ++ }; ++ ++ pinctrl_cubox_i_usbotg_vbus: cubox-i-usbotg-vbus { ++ fsl,pins = <MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x4001b0b0>; ++ }; ++ ++ pinctrl_cubox_i_usdhc2_aux: cubox-i-usdhc2-aux { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 ++ MX6QDL_PAD_KEY_ROW1__SD2_VSELECT 0x1b071 ++ >; ++ }; ++ ++ pinctrl_cubox_i_usdhc2: cubox-i-usdhc2 { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 ++ >; ++ }; ++ }; ++}; ++ ++&spdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_spdif>; ++ status = "okay"; ++}; ++ ++&usbh1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usbh1>; ++ vbus-supply = <®_usbh1_vbus>; ++ status = "okay"; ++}; ++ ++&usbotg { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usbotg>; ++ vbus-supply = <®_usbotg_vbus>; ++ status = "okay"; ++}; ++ ++&usdhc2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2>; ++ vmmc-supply = <®_3p3v>; ++ cd-gpios = <&gpio1 4 0>; ++ status = "okay"; ++}; +diff -Nur linux-3.16.6.orig/arch/arm/boot/dts/imx6qdl-microsom.dtsi linux-3.16.6/arch/arm/boot/dts/imx6qdl-microsom.dtsi +--- linux-3.16.6.orig/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2014-10-23 12:34:48.394220240 -0500 +@@ -1,15 +1,95 @@ + /* + * 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_bt: microsom-brcm-bt { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x40013070 ++ MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x40013070 ++ MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_osc_reg: microsom-brcm-osc-reg { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_reg: microsom-brcm-reg { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_wifi: microsom-brcm-wifi { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x1b0b0 ++ MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x40013070 ++ MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x40013070 ++ MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x40013070 ++ >; ++ }; ++ + pinctrl_microsom_uart1: microsom-uart1 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 + >; + }; ++ ++ pinctrl_microsom_uart4_1: microsom-uart4 { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_microsom_usdhc1: microsom-usdhc1 { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 ++ MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 ++ MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 ++ MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 ++ MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 ++ MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 ++ >; ++ }; + }; + }; + +@@ -18,3 +98,23 @@ + pinctrl-0 = <&pinctrl_microsom_uart1>; + 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; ++ status = "okay"; ++}; ++ ++/* USDHC1 - Connected to optional BRCM Wifi/BT/FM */ ++&usdhc1 { ++ card-external-vcc-supply = <®_brcm>; ++ card-reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>, <&gpio6 0 GPIO_ACTIVE_LOW>; ++ keep-power-in-suspend; ++ non-removable; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_wifi &pinctrl_microsom_usdhc1>; ++ vmmc-supply = <®_brcm>; ++ status = "okay"; ++}; +diff -Nur linux-3.16.6.orig/arch/arm/mach-imx/clk-imx6q.c linux-3.16.6/arch/arm/mach-imx/clk-imx6q.c +--- linux-3.16.6.orig/arch/arm/mach-imx/clk-imx6q.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/arch/arm/mach-imx/clk-imx6q.c 2014-10-23 12:36:09.214219998 -0500 +@@ -461,6 +461,9 @@ + 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]); ++ + /* + * The gpmi needs 100MHz frequency in the EDO/Sync mode, + * We can not get the 100MHz from the pll2_pfd0_352m. +diff -Nur linux-3.16.6.orig/arch/arm/mach-imx/clk-pllv3.c linux-3.16.6/arch/arm/mach-imx/clk-pllv3.c +--- linux-3.16.6.orig/arch/arm/mach-imx/clk-pllv3.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/arch/arm/mach-imx/clk-pllv3.c 2014-10-23 12:36:01.390219997 -0500 +@@ -273,9 +273,10 @@ + 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 @@ + 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); ++ ++ 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 clk_pllv3_wait_lock(pll); ++ return ret; + } + + static const struct clk_ops clk_pllv3_av_ops = { +diff -Nur linux-3.16.6.orig/Documentation/devicetree/bindings/ata/ahci-platform.txt linux-3.16.6/Documentation/devicetree/bindings/ata/ahci-platform.txt +--- linux-3.16.6.orig/Documentation/devicetree/bindings/ata/ahci-platform.txt 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/Documentation/devicetree/bindings/ata/ahci-platform.txt 2014-10-23 12:15:35.154220017 -0500 +@@ -6,8 +6,6 @@ + Required properties: + - compatible : compatible string, one of: + - "allwinner,sun4i-a10-ahci" +- - "fsl,imx53-ahci" +- - "fsl,imx6q-ahci" + - "hisilicon,hisi-ahci" + - "ibm,476gtr-ahci" + - "marvell,armada-380-ahci" +@@ -22,10 +20,6 @@ + - clocks : a list of phandle + clock specifier pairs + - target-supply : regulator for SATA target power + +-"fsl,imx53-ahci", "fsl,imx6q-ahci" required properties: +-- clocks : must contain the sata, sata_ref and ahb clocks +-- clock-names : must contain "ahb" for the ahb clock +- + Examples: + sata@ffe08000 { + compatible = "snps,spear-ahci"; +diff -Nur linux-3.16.6.orig/Documentation/devicetree/bindings/ata/imx-sata.txt linux-3.16.6/Documentation/devicetree/bindings/ata/imx-sata.txt +--- linux-3.16.6.orig/Documentation/devicetree/bindings/ata/imx-sata.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/Documentation/devicetree/bindings/ata/imx-sata.txt 2014-10-23 12:26:27.434219953 -0500 +@@ -0,0 +1,36 @@ ++* Freescale i.MX AHCI SATA Controller ++ ++The Freescale i.MX SATA controller mostly conforms to the AHCI interface ++with some special extensions at integration level. ++ ++Required properties: ++- compatible : should be one of the following: ++ - "fsl,imx53-ahci" for i.MX53 SATA controller ++ - "fsl,imx6q-ahci" for i.MX6Q SATA controller ++- interrupts : interrupt mapping for SATA IRQ ++- reg : registers mapping ++- clocks : list of clock specifiers, must contain an entry for each ++ required entry in clock-names ++- clock-names : should include "sata", "sata_ref" and "ahb" entries ++ ++Optional properties: ++- fsl,transmit-level-mV : transmit voltage level, in millivolts. ++- fsl,transmit-boost-mdB : transmit boost level, in milli-decibels ++- fsl,transmit-atten-16ths : transmit attenuation, in 16ths ++- fsl,receive-eq-mdB : receive equalisation, in milli-decibels ++ Please refer to the technical documentation or the driver source code ++ for the list of legal values for these options. ++- fsl,no-spread-spectrum : disable spread-spectrum clocking on the SATA ++ link. ++ ++Examples: ++ ++sata@02200000 { ++ compatible = "fsl,imx6q-ahci"; ++ reg = <0x02200000 0x4000>; ++ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks IMX6QDL_CLK_SATA>, ++ <&clks IMX6QDL_CLK_SATA_REF_100M>, ++ <&clks IMX6QDL_CLK_AHB>; ++ clock-names = "sata", "sata_ref", "ahb"; ++}; +diff -Nur linux-3.16.6.orig/Documentation/devicetree/bindings/mmc/mmc.txt linux-3.16.6/Documentation/devicetree/bindings/mmc/mmc.txt +--- linux-3.16.6.orig/Documentation/devicetree/bindings/mmc/mmc.txt 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/Documentation/devicetree/bindings/mmc/mmc.txt 2014-10-23 12:34:18.694220003 -0500 +@@ -5,6 +5,8 @@ + Interpreted by the OF core: + - reg: Registers location and length. + - interrupts: Interrupts used by the MMC controller. ++- clocks: Clocks needed for the host controller, if any. ++- clock-names: Goes with clocks above. + + Card detection: + If no property below is supplied, host native card detect is used. +@@ -41,6 +43,15 @@ + - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported + - mmc-hs400-1_2v: eMMC HS400 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 -Nur linux-3.16.6.orig/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt linux-3.16.6/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt +--- linux-3.16.6.orig/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt 2014-10-23 12:35:30.946219998 -0500 +@@ -60,8 +60,8 @@ + - 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" +- and "lvds666". ++ display interface. Currently supported types: "rgb24", "rgb565", "bgr666", ++ "rgb666" and "lvds666". + - edid: verbatim EDID data block describing attached display. + - ddc: phandle describing the i2c bus handling the display data + channel +diff -Nur linux-3.16.6.orig/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml linux-3.16.6/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml +--- linux-3.16.6.orig/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml 2014-10-23 12:35:23.678220004 -0500 +@@ -279,6 +279,45 @@ + <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 -Nur linux-3.16.6.orig/drivers/ata/ahci_imx.c linux-3.16.6/drivers/ata/ahci_imx.c +--- linux-3.16.6.orig/drivers/ata/ahci_imx.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/ata/ahci_imx.c 2014-10-23 12:26:19.770220044 -0500 +@@ -64,6 +64,7 @@ + struct regmap *gpr; + bool no_device; + bool first_time; ++ u32 phy_params; + }; + + static int ahci_imx_hotplug; +@@ -248,14 +249,7 @@ + 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); +@@ -369,6 +363,165 @@ + }; + 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 }, ++ { 1110, IMX6Q_GPR13_SATA_TX_BOOST_1_11_DB }, ++ { 1480, IMX6Q_GPR13_SATA_TX_BOOST_1_48_DB }, ++ { 1850, IMX6Q_GPR13_SATA_TX_BOOST_1_85_DB }, ++ { 2220, IMX6Q_GPR13_SATA_TX_BOOST_2_22_DB }, ++ { 2590, IMX6Q_GPR13_SATA_TX_BOOST_2_59_DB }, ++ { 2960, IMX6Q_GPR13_SATA_TX_BOOST_2_96_DB }, ++ { 3330, IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB }, ++ { 3700, IMX6Q_GPR13_SATA_TX_BOOST_3_70_DB }, ++ { 4070, IMX6Q_GPR13_SATA_TX_BOOST_4_07_DB }, ++ { 4440, IMX6Q_GPR13_SATA_TX_BOOST_4_44_DB }, ++ { 4810, IMX6Q_GPR13_SATA_TX_BOOST_4_81_DB }, ++ { 5280, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB }, ++ { 5750, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB } ++}; ++ ++static 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; +@@ -410,6 +563,8 @@ + } + + if (imxpriv->type == AHCI_IMX6Q) { ++ u32 reg_value; ++ + imxpriv->gpr = syscon_regmap_lookup_by_compatible( + "fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imxpriv->gpr)) { +@@ -417,6 +572,15 @@ + "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 -Nur linux-3.16.6.orig/drivers/ata/ahci_imx.c.orig linux-3.16.6/drivers/ata/ahci_imx.c.orig +--- linux-3.16.6.orig/drivers/ata/ahci_imx.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/ata/ahci_imx.c.orig 2014-10-23 12:18:59.602219672 -0500 +@@ -0,0 +1,679 @@ ++/* ++ * copyright (c) 2013 Freescale Semiconductor, Inc. ++ * Freescale IMX AHCI SATA platform driver ++ * ++ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <linux/ahci_platform.h> ++#include <linux/of_device.h> ++#include <linux/mfd/syscon.h> ++#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> ++#include <linux/libata.h> ++#include "ahci.h" ++ ++enum { ++ /* Timer 1-ms Register */ ++ IMX_TIMER1MS = 0x00e0, ++ /* Port0 PHY Control Register */ ++ IMX_P0PHYCR = 0x0178, ++ IMX_P0PHYCR_TEST_PDDQ = 1 << 20, ++ IMX_P0PHYCR_CR_READ = 1 << 19, ++ IMX_P0PHYCR_CR_WRITE = 1 << 18, ++ IMX_P0PHYCR_CR_CAP_DATA = 1 << 17, ++ IMX_P0PHYCR_CR_CAP_ADDR = 1 << 16, ++ /* Port0 PHY Status Register */ ++ IMX_P0PHYSR = 0x017c, ++ IMX_P0PHYSR_CR_ACK = 1 << 18, ++ IMX_P0PHYSR_CR_DATA_OUT = 0xffff << 0, ++ /* Lane0 Output Status Register */ ++ IMX_LANE0_OUT_STAT = 0x2003, ++ IMX_LANE0_OUT_STAT_RX_PLL_STATE = 1 << 1, ++ /* Clock Reset Register */ ++ IMX_CLOCK_RESET = 0x7f3f, ++ IMX_CLOCK_RESET_RESET = 1 << 0, ++}; ++ ++enum ahci_imx_type { ++ AHCI_IMX53, ++ AHCI_IMX6Q, ++}; ++ ++struct imx_ahci_priv { ++ struct platform_device *ahci_pdev; ++ enum ahci_imx_type type; ++ struct clk *sata_clk; ++ struct clk *sata_ref_clk; ++ struct clk *ahb_clk; ++ struct regmap *gpr; ++ bool no_device; ++ bool first_time; ++ u32 phy_params; ++}; ++ ++static int ahci_imx_hotplug; ++module_param_named(hotplug, ahci_imx_hotplug, int, 0644); ++MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); ++ ++static void ahci_imx_host_stop(struct ata_host *host); ++ ++static int imx_phy_crbit_assert(void __iomem *mmio, u32 bit, bool assert) ++{ ++ int timeout = 10; ++ u32 crval; ++ u32 srval; ++ ++ /* Assert or deassert the bit */ ++ crval = readl(mmio + IMX_P0PHYCR); ++ if (assert) ++ crval |= bit; ++ else ++ crval &= ~bit; ++ writel(crval, mmio + IMX_P0PHYCR); ++ ++ /* Wait for the cr_ack signal */ ++ do { ++ srval = readl(mmio + IMX_P0PHYSR); ++ if ((assert ? srval : ~srval) & IMX_P0PHYSR_CR_ACK) ++ break; ++ usleep_range(100, 200); ++ } while (--timeout); ++ ++ return timeout ? 0 : -ETIMEDOUT; ++} ++ ++static int imx_phy_reg_addressing(u16 addr, void __iomem *mmio) ++{ ++ u32 crval = addr; ++ int ret; ++ ++ /* Supply the address on cr_data_in */ ++ writel(crval, mmio + IMX_P0PHYCR); ++ ++ /* Assert the cr_cap_addr signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, true); ++ if (ret) ++ return ret; ++ ++ /* Deassert cr_cap_addr */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, false); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int imx_phy_reg_write(u16 val, void __iomem *mmio) ++{ ++ u32 crval = val; ++ int ret; ++ ++ /* Supply the data on cr_data_in */ ++ writel(crval, mmio + IMX_P0PHYCR); ++ ++ /* Assert the cr_cap_data signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, true); ++ if (ret) ++ return ret; ++ ++ /* Deassert cr_cap_data */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, false); ++ if (ret) ++ return ret; ++ ++ if (val & IMX_CLOCK_RESET_RESET) { ++ /* ++ * In case we're resetting the phy, it's unable to acknowledge, ++ * so we return immediately here. ++ */ ++ crval |= IMX_P0PHYCR_CR_WRITE; ++ writel(crval, mmio + IMX_P0PHYCR); ++ goto out; ++ } ++ ++ /* Assert the cr_write signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, true); ++ if (ret) ++ return ret; ++ ++ /* Deassert cr_write */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, false); ++ if (ret) ++ return ret; ++ ++out: ++ return 0; ++} ++ ++static int imx_phy_reg_read(u16 *val, void __iomem *mmio) ++{ ++ int ret; ++ ++ /* Assert the cr_read signal */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, true); ++ if (ret) ++ return ret; ++ ++ /* Capture the data from cr_data_out[] */ ++ *val = readl(mmio + IMX_P0PHYSR) & IMX_P0PHYSR_CR_DATA_OUT; ++ ++ /* Deassert cr_read */ ++ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, false); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int imx_sata_phy_reset(struct ahci_host_priv *hpriv) ++{ ++ void __iomem *mmio = hpriv->mmio; ++ int timeout = 10; ++ u16 val; ++ int ret; ++ ++ /* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */ ++ ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio); ++ if (ret) ++ return ret; ++ ret = imx_phy_reg_write(IMX_CLOCK_RESET_RESET, mmio); ++ if (ret) ++ return ret; ++ ++ /* Wait for PHY RX_PLL to be stable */ ++ do { ++ usleep_range(100, 200); ++ ret = imx_phy_reg_addressing(IMX_LANE0_OUT_STAT, mmio); ++ if (ret) ++ return ret; ++ ret = imx_phy_reg_read(&val, mmio); ++ if (ret) ++ return ret; ++ if (val & IMX_LANE0_OUT_STAT_RX_PLL_STATE) ++ break; ++ } while (--timeout); ++ ++ return timeout ? 0 : -ETIMEDOUT; ++} ++ ++static int imx_sata_enable(struct ahci_host_priv *hpriv) ++{ ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; ++ struct device *dev = &imxpriv->ahci_pdev->dev; ++ int ret; ++ ++ if (imxpriv->no_device) ++ return 0; ++ ++ if (hpriv->target_pwr) { ++ ret = regulator_enable(hpriv->target_pwr); ++ if (ret) ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(imxpriv->sata_ref_clk); ++ if (ret < 0) ++ goto disable_regulator; ++ ++ if (imxpriv->type == AHCI_IMX6Q) { ++ /* ++ * set PHY Paremeters, two steps to configure the GPR13, ++ * one write for rest of parameters, mask of first write ++ * is 0x07ffffff, and the other one write for setting ++ * the mpll_clk_en. ++ */ ++ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, ++ IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | ++ IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | ++ IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | ++ IMX6Q_GPR13_SATA_SPD_MODE_MASK | ++ IMX6Q_GPR13_SATA_MPLL_SS_EN | ++ IMX6Q_GPR13_SATA_TX_ATTEN_MASK | ++ IMX6Q_GPR13_SATA_TX_BOOST_MASK | ++ IMX6Q_GPR13_SATA_TX_LVL_MASK | ++ IMX6Q_GPR13_SATA_MPLL_CLK_EN | ++ IMX6Q_GPR13_SATA_TX_EDGE_RATE, ++ imxpriv->phy_params); ++ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, ++ IMX6Q_GPR13_SATA_MPLL_CLK_EN, ++ IMX6Q_GPR13_SATA_MPLL_CLK_EN); ++ ++ usleep_range(100, 200); ++ ++ ret = imx_sata_phy_reset(hpriv); ++ if (ret) { ++ dev_err(dev, "failed to reset phy: %d\n", ret); ++ goto disable_regulator; ++ } ++ } ++ ++ usleep_range(1000, 2000); ++ ++ return 0; ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ ++ return ret; ++} ++ ++static void imx_sata_disable(struct ahci_host_priv *hpriv) ++{ ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; ++ ++ if (imxpriv->no_device) ++ return; ++ ++ if (imxpriv->type == AHCI_IMX6Q) { ++ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, ++ IMX6Q_GPR13_SATA_MPLL_CLK_EN, ++ !IMX6Q_GPR13_SATA_MPLL_CLK_EN); ++ } ++ ++ clk_disable_unprepare(imxpriv->sata_ref_clk); ++ ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++ ++static void ahci_imx_error_handler(struct ata_port *ap) ++{ ++ u32 reg_val; ++ struct ata_device *dev; ++ struct ata_host *host = dev_get_drvdata(ap->dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ void __iomem *mmio = hpriv->mmio; ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; ++ ++ ahci_error_handler(ap); ++ ++ if (!(imxpriv->first_time) || ahci_imx_hotplug) ++ return; ++ ++ imxpriv->first_time = false; ++ ++ ata_for_each_dev(dev, &ap->link, ENABLED) ++ return; ++ /* ++ * Disable link to save power. An imx ahci port can't be recovered ++ * without full reset once the pddq mode is enabled making it ++ * impossible to use as part of libata LPM. ++ */ ++ reg_val = readl(mmio + IMX_P0PHYCR); ++ writel(reg_val | IMX_P0PHYCR_TEST_PDDQ, mmio + IMX_P0PHYCR); ++ imx_sata_disable(hpriv); ++ imxpriv->no_device = true; ++ ++ dev_info(ap->dev, "no device found, disabling link.\n"); ++ dev_info(ap->dev, "pass " MODULE_PARAM_PREFIX ".hotplug=1 to enable hotplug\n"); ++} ++ ++static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, ++ unsigned long deadline) ++{ ++ struct ata_port *ap = link->ap; ++ struct ata_host *host = dev_get_drvdata(ap->dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; ++ int ret = -EIO; ++ ++ if (imxpriv->type == AHCI_IMX53) ++ ret = ahci_pmp_retry_srst_ops.softreset(link, class, deadline); ++ else if (imxpriv->type == AHCI_IMX6Q) ++ ret = ahci_ops.softreset(link, class, deadline); ++ ++ return ret; ++} ++ ++static struct ata_port_operations ahci_imx_ops = { ++ .inherits = &ahci_ops, ++ .host_stop = ahci_imx_host_stop, ++ .error_handler = ahci_imx_error_handler, ++ .softreset = ahci_imx_softreset, ++}; ++ ++static const struct ata_port_info ahci_imx_port_info = { ++ .flags = AHCI_FLAG_COMMON, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_imx_ops, ++}; ++ ++static const struct of_device_id imx_ahci_of_match[] = { ++ { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, ++ { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, imx_ahci_of_match); ++ ++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; ++}; ++ ++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 }, ++ { 1110, IMX6Q_GPR13_SATA_TX_BOOST_1_11_DB }, ++ { 1480, IMX6Q_GPR13_SATA_TX_BOOST_1_48_DB }, ++ { 1850, IMX6Q_GPR13_SATA_TX_BOOST_1_85_DB }, ++ { 2220, IMX6Q_GPR13_SATA_TX_BOOST_2_22_DB }, ++ { 2590, IMX6Q_GPR13_SATA_TX_BOOST_2_59_DB }, ++ { 2960, IMX6Q_GPR13_SATA_TX_BOOST_2_96_DB }, ++ { 3330, IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB }, ++ { 3700, IMX6Q_GPR13_SATA_TX_BOOST_3_70_DB }, ++ { 4070, IMX6Q_GPR13_SATA_TX_BOOST_4_07_DB }, ++ { 4440, IMX6Q_GPR13_SATA_TX_BOOST_4_44_DB }, ++ { 4810, IMX6Q_GPR13_SATA_TX_BOOST_4_81_DB }, ++ { 5280, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB }, ++ { 5750, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB } ++}; ++ ++static 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, ++ }, ++}; ++ ++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 (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; ++ const struct of_device_id *of_id; ++ struct ahci_host_priv *hpriv; ++ struct imx_ahci_priv *imxpriv; ++ unsigned int reg_val; ++ int ret; ++ ++ of_id = of_match_device(imx_ahci_of_match, dev); ++ if (!of_id) ++ return -EINVAL; ++ ++ imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); ++ if (!imxpriv) ++ return -ENOMEM; ++ ++ imxpriv->ahci_pdev = pdev; ++ imxpriv->no_device = false; ++ imxpriv->first_time = true; ++ imxpriv->type = (enum ahci_imx_type)of_id->data; ++ ++ imxpriv->sata_clk = devm_clk_get(dev, "sata"); ++ if (IS_ERR(imxpriv->sata_clk)) { ++ dev_err(dev, "can't get sata clock.\n"); ++ return PTR_ERR(imxpriv->sata_clk); ++ } ++ ++ imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); ++ if (IS_ERR(imxpriv->sata_ref_clk)) { ++ dev_err(dev, "can't get sata_ref clock.\n"); ++ return PTR_ERR(imxpriv->sata_ref_clk); ++ } ++ ++ imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); ++ if (IS_ERR(imxpriv->ahb_clk)) { ++ dev_err(dev, "can't get ahb clock.\n"); ++ return PTR_ERR(imxpriv->ahb_clk); ++ } ++ ++ if (imxpriv->type == AHCI_IMX6Q) { ++ u32 reg_value; ++ ++ imxpriv->gpr = syscon_regmap_lookup_by_compatible( ++ "fsl,imx6q-iomuxc-gpr"); ++ if (IS_ERR(imxpriv->gpr)) { ++ dev_err(dev, ++ "failed to find fsl,imx6q-iomux-gpr regmap\n"); ++ 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 | ++ IMX6Q_GPR13_SATA_MPLL_SS_EN | ++ reg_value; ++ } ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ hpriv->plat_data = imxpriv; ++ ++ ret = clk_prepare_enable(imxpriv->sata_clk); ++ if (ret) ++ return ret; ++ ++ ret = imx_sata_enable(hpriv); ++ if (ret) ++ goto disable_clk; ++ ++ /* ++ * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, ++ * and IP vendor specific register IMX_TIMER1MS. ++ * Configure CAP_SSS (support stagered spin up). ++ * Implement the port0. ++ * Get the ahb clock rate, and configure the TIMER1MS register. ++ */ ++ reg_val = readl(hpriv->mmio + HOST_CAP); ++ if (!(reg_val & HOST_CAP_SSS)) { ++ reg_val |= HOST_CAP_SSS; ++ writel(reg_val, hpriv->mmio + HOST_CAP); ++ } ++ reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL); ++ if (!(reg_val & 0x1)) { ++ reg_val |= 0x1; ++ writel(reg_val, hpriv->mmio + HOST_PORTS_IMPL); ++ } ++ ++ reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; ++ writel(reg_val, hpriv->mmio + IMX_TIMER1MS); ++ ++ ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, ++ 0, 0, 0); ++ if (ret) ++ goto disable_sata; ++ ++ return 0; ++ ++disable_sata: ++ imx_sata_disable(hpriv); ++disable_clk: ++ clk_disable_unprepare(imxpriv->sata_clk); ++ return ret; ++} ++ ++static void ahci_imx_host_stop(struct ata_host *host) ++{ ++ struct ahci_host_priv *hpriv = host->private_data; ++ struct imx_ahci_priv *imxpriv = hpriv->plat_data; ++ ++ imx_sata_disable(hpriv); ++ clk_disable_unprepare(imxpriv->sata_clk); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int imx_ahci_suspend(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int ret; ++ ++ ret = ahci_platform_suspend_host(dev); ++ if (ret) ++ return ret; ++ ++ imx_sata_disable(hpriv); ++ ++ return 0; ++} ++ ++static int imx_ahci_resume(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int ret; ++ ++ ret = imx_sata_enable(hpriv); ++ if (ret) ++ return ret; ++ ++ return ahci_platform_resume_host(dev); ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(ahci_imx_pm_ops, imx_ahci_suspend, imx_ahci_resume); ++ ++static struct platform_driver imx_ahci_driver = { ++ .probe = imx_ahci_probe, ++ .remove = ata_platform_remove_one, ++ .driver = { ++ .name = "ahci-imx", ++ .owner = THIS_MODULE, ++ .of_match_table = imx_ahci_of_match, ++ .pm = &ahci_imx_pm_ops, ++ }, ++}; ++module_platform_driver(imx_ahci_driver); ++ ++MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver"); ++MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ahci:imx"); +diff -Nur linux-3.16.6.orig/drivers/cec/cec-dev.c linux-3.16.6/drivers/cec/cec-dev.c +--- linux-3.16.6.orig/drivers/cec/cec-dev.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/cec/cec-dev.c 2014-10-23 12:37:18.374219998 -0500 +@@ -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 -Nur linux-3.16.6.orig/drivers/cec/Kconfig linux-3.16.6/drivers/cec/Kconfig +--- linux-3.16.6.orig/drivers/cec/Kconfig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/cec/Kconfig 2014-10-23 12:37:18.350220009 -0500 +@@ -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 -Nur linux-3.16.6.orig/drivers/cec/Makefile linux-3.16.6/drivers/cec/Makefile +--- linux-3.16.6.orig/drivers/cec/Makefile 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/cec/Makefile 2014-10-23 12:37:18.374219998 -0500 +@@ -0,0 +1 @@ ++obj-$(CONFIG_HDMI_CEC_CORE) += cec-dev.o +diff -Nur linux-3.16.6.orig/drivers/dma/imx-sdma.c linux-3.16.6/drivers/dma/imx-sdma.c +--- linux-3.16.6.orig/drivers/dma/imx-sdma.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/dma/imx-sdma.c 2014-10-23 12:35:52.990220019 -0500 +@@ -255,7 +255,6 @@ + enum dma_slave_buswidth word_size; + unsigned int buf_tail; + unsigned int num_bd; +- unsigned int period_len; + struct sdma_buffer_descriptor *bd; + dma_addr_t bd_phys; + unsigned int pc_from_device, pc_to_device; +@@ -594,12 +593,6 @@ + + static void sdma_handle_channel_loop(struct sdma_channel *sdmac) + { +- if (sdmac->desc.callback) +- sdmac->desc.callback(sdmac->desc.callback_param); +-} +- +-static void sdma_update_channel_loop(struct sdma_channel *sdmac) +-{ + struct sdma_buffer_descriptor *bd; + + /* +@@ -618,6 +611,9 @@ + bd->mode.status |= BD_DONE; + sdmac->buf_tail++; + sdmac->buf_tail %= sdmac->num_bd; ++ ++ if (sdmac->desc.callback) ++ sdmac->desc.callback(sdmac->desc.callback_param); + } + } + +@@ -673,9 +669,6 @@ + int channel = fls(stat) - 1; + struct sdma_channel *sdmac = &sdma->channel[channel]; + +- if (sdmac->flags & IMX_DMA_SG_LOOP) +- sdma_update_channel_loop(sdmac); +- + tasklet_schedule(&sdmac->tasklet); + + __clear_bit(channel, &stat); +@@ -1136,7 +1129,6 @@ + sdmac->status = DMA_IN_PROGRESS; + + sdmac->buf_tail = 0; +- sdmac->period_len = period_len; + + sdmac->flags |= IMX_DMA_SG_LOOP; + sdmac->direction = direction; +@@ -1233,15 +1225,9 @@ + struct dma_tx_state *txstate) + { + struct sdma_channel *sdmac = to_sdma_chan(chan); +- u32 residue; +- +- if (sdmac->flags & IMX_DMA_SG_LOOP) +- residue = (sdmac->num_bd - sdmac->buf_tail) * sdmac->period_len; +- else +- residue = sdmac->chn_count - sdmac->chn_real_count; + + dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, +- residue); ++ sdmac->chn_count - sdmac->chn_real_count); + + return sdmac->status; + } +diff -Nur linux-3.16.6.orig/drivers/dma/imx-sdma.c.orig linux-3.16.6/drivers/dma/imx-sdma.c.orig +--- linux-3.16.6.orig/drivers/dma/imx-sdma.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/dma/imx-sdma.c.orig 2014-10-15 05:05:43.000000000 -0500 +@@ -0,0 +1,1656 @@ ++/* ++ * drivers/dma/imx-sdma.c ++ * ++ * This file contains a driver for the Freescale Smart DMA engine ++ * ++ * Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> ++ * ++ * Based on code from Freescale: ++ * ++ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/bitops.h> ++#include <linux/mm.h> ++#include <linux/interrupt.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/sched.h> ++#include <linux/semaphore.h> ++#include <linux/spinlock.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/firmware.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++#include <linux/dmaengine.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/of_dma.h> ++ ++#include <asm/irq.h> ++#include <linux/platform_data/dma-imx-sdma.h> ++#include <linux/platform_data/dma-imx.h> ++ ++#include "dmaengine.h" ++ ++/* SDMA registers */ ++#define SDMA_H_C0PTR 0x000 ++#define SDMA_H_INTR 0x004 ++#define SDMA_H_STATSTOP 0x008 ++#define SDMA_H_START 0x00c ++#define SDMA_H_EVTOVR 0x010 ++#define SDMA_H_DSPOVR 0x014 ++#define SDMA_H_HOSTOVR 0x018 ++#define SDMA_H_EVTPEND 0x01c ++#define SDMA_H_DSPENBL 0x020 ++#define SDMA_H_RESET 0x024 ++#define SDMA_H_EVTERR 0x028 ++#define SDMA_H_INTRMSK 0x02c ++#define SDMA_H_PSW 0x030 ++#define SDMA_H_EVTERRDBG 0x034 ++#define SDMA_H_CONFIG 0x038 ++#define SDMA_ONCE_ENB 0x040 ++#define SDMA_ONCE_DATA 0x044 ++#define SDMA_ONCE_INSTR 0x048 ++#define SDMA_ONCE_STAT 0x04c ++#define SDMA_ONCE_CMD 0x050 ++#define SDMA_EVT_MIRROR 0x054 ++#define SDMA_ILLINSTADDR 0x058 ++#define SDMA_CHN0ADDR 0x05c ++#define SDMA_ONCE_RTB 0x060 ++#define SDMA_XTRIG_CONF1 0x070 ++#define SDMA_XTRIG_CONF2 0x074 ++#define SDMA_CHNENBL0_IMX35 0x200 ++#define SDMA_CHNENBL0_IMX31 0x080 ++#define SDMA_CHNPRI_0 0x100 ++ ++/* ++ * Buffer descriptor status values. ++ */ ++#define BD_DONE 0x01 ++#define BD_WRAP 0x02 ++#define BD_CONT 0x04 ++#define BD_INTR 0x08 ++#define BD_RROR 0x10 ++#define BD_LAST 0x20 ++#define BD_EXTD 0x80 ++ ++/* ++ * Data Node descriptor status values. ++ */ ++#define DND_END_OF_FRAME 0x80 ++#define DND_END_OF_XFER 0x40 ++#define DND_DONE 0x20 ++#define DND_UNUSED 0x01 ++ ++/* ++ * IPCV2 descriptor status values. ++ */ ++#define BD_IPCV2_END_OF_FRAME 0x40 ++ ++#define IPCV2_MAX_NODES 50 ++/* ++ * Error bit set in the CCB status field by the SDMA, ++ * in setbd routine, in case of a transfer error ++ */ ++#define DATA_ERROR 0x10000000 ++ ++/* ++ * Buffer descriptor commands. ++ */ ++#define C0_ADDR 0x01 ++#define C0_LOAD 0x02 ++#define C0_DUMP 0x03 ++#define C0_SETCTX 0x07 ++#define C0_GETCTX 0x03 ++#define C0_SETDM 0x01 ++#define C0_SETPM 0x04 ++#define C0_GETDM 0x02 ++#define C0_GETPM 0x08 ++/* ++ * Change endianness indicator in the BD command field ++ */ ++#define CHANGE_ENDIANNESS 0x80 ++ ++/* ++ * Mode/Count of data node descriptors - IPCv2 ++ */ ++struct sdma_mode_count { ++ u32 count : 16; /* size of the buffer pointed by this BD */ ++ u32 status : 8; /* E,R,I,C,W,D status bits stored here */ ++ u32 command : 8; /* command mostlky used for channel 0 */ ++}; ++ ++/* ++ * Buffer descriptor ++ */ ++struct sdma_buffer_descriptor { ++ struct sdma_mode_count mode; ++ u32 buffer_addr; /* address of the buffer described */ ++ u32 ext_buffer_addr; /* extended buffer address */ ++} __attribute__ ((packed)); ++ ++/** ++ * struct sdma_channel_control - Channel control Block ++ * ++ * @current_bd_ptr current buffer descriptor processed ++ * @base_bd_ptr first element of buffer descriptor array ++ * @unused padding. The SDMA engine expects an array of 128 byte ++ * control blocks ++ */ ++struct sdma_channel_control { ++ u32 current_bd_ptr; ++ u32 base_bd_ptr; ++ u32 unused[2]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct sdma_state_registers - SDMA context for a channel ++ * ++ * @pc: program counter ++ * @t: test bit: status of arithmetic & test instruction ++ * @rpc: return program counter ++ * @sf: source fault while loading data ++ * @spc: loop start program counter ++ * @df: destination fault while storing data ++ * @epc: loop end program counter ++ * @lm: loop mode ++ */ ++struct sdma_state_registers { ++ u32 pc :14; ++ u32 unused1: 1; ++ u32 t : 1; ++ u32 rpc :14; ++ u32 unused0: 1; ++ u32 sf : 1; ++ u32 spc :14; ++ u32 unused2: 1; ++ u32 df : 1; ++ u32 epc :14; ++ u32 lm : 2; ++} __attribute__ ((packed)); ++ ++/** ++ * struct sdma_context_data - sdma context specific to a channel ++ * ++ * @channel_state: channel state bits ++ * @gReg: general registers ++ * @mda: burst dma destination address register ++ * @msa: burst dma source address register ++ * @ms: burst dma status register ++ * @md: burst dma data register ++ * @pda: peripheral dma destination address register ++ * @psa: peripheral dma source address register ++ * @ps: peripheral dma status register ++ * @pd: peripheral dma data register ++ * @ca: CRC polynomial register ++ * @cs: CRC accumulator register ++ * @dda: dedicated core destination address register ++ * @dsa: dedicated core source address register ++ * @ds: dedicated core status register ++ * @dd: dedicated core data register ++ */ ++struct sdma_context_data { ++ struct sdma_state_registers channel_state; ++ u32 gReg[8]; ++ u32 mda; ++ u32 msa; ++ u32 ms; ++ u32 md; ++ u32 pda; ++ u32 psa; ++ u32 ps; ++ u32 pd; ++ u32 ca; ++ u32 cs; ++ u32 dda; ++ u32 dsa; ++ u32 ds; ++ u32 dd; ++ u32 scratch0; ++ u32 scratch1; ++ u32 scratch2; ++ u32 scratch3; ++ u32 scratch4; ++ u32 scratch5; ++ u32 scratch6; ++ u32 scratch7; ++} __attribute__ ((packed)); ++ ++#define NUM_BD (int)(PAGE_SIZE / sizeof(struct sdma_buffer_descriptor)) ++ ++struct sdma_engine; ++ ++/** ++ * struct sdma_channel - housekeeping for a SDMA channel ++ * ++ * @sdma pointer to the SDMA engine for this channel ++ * @channel the channel number, matches dmaengine chan_id + 1 ++ * @direction transfer type. Needed for setting SDMA script ++ * @peripheral_type Peripheral type. Needed for setting SDMA script ++ * @event_id0 aka dma request line ++ * @event_id1 for channels that use 2 events ++ * @word_size peripheral access size ++ * @buf_tail ID of the buffer that was processed ++ * @num_bd max NUM_BD. number of descriptors currently handling ++ */ ++struct sdma_channel { ++ struct sdma_engine *sdma; ++ unsigned int channel; ++ enum dma_transfer_direction direction; ++ enum sdma_peripheral_type peripheral_type; ++ unsigned int event_id0; ++ unsigned int event_id1; ++ enum dma_slave_buswidth word_size; ++ unsigned int buf_tail; ++ unsigned int num_bd; ++ unsigned int period_len; ++ struct sdma_buffer_descriptor *bd; ++ dma_addr_t bd_phys; ++ unsigned int pc_from_device, pc_to_device; ++ unsigned long flags; ++ dma_addr_t per_address; ++ unsigned long event_mask[2]; ++ unsigned long watermark_level; ++ u32 shp_addr, per_addr; ++ struct dma_chan chan; ++ spinlock_t lock; ++ struct dma_async_tx_descriptor desc; ++ enum dma_status status; ++ unsigned int chn_count; ++ unsigned int chn_real_count; ++ struct tasklet_struct tasklet; ++}; ++ ++#define IMX_DMA_SG_LOOP BIT(0) ++ ++#define MAX_DMA_CHANNELS 32 ++#define MXC_SDMA_DEFAULT_PRIORITY 1 ++#define MXC_SDMA_MIN_PRIORITY 1 ++#define MXC_SDMA_MAX_PRIORITY 7 ++ ++#define SDMA_FIRMWARE_MAGIC 0x414d4453 ++ ++/** ++ * struct sdma_firmware_header - Layout of the firmware image ++ * ++ * @magic "SDMA" ++ * @version_major increased whenever layout of struct sdma_script_start_addrs ++ * changes. ++ * @version_minor firmware minor version (for binary compatible changes) ++ * @script_addrs_start offset of struct sdma_script_start_addrs in this image ++ * @num_script_addrs Number of script addresses in this image ++ * @ram_code_start offset of SDMA ram image in this firmware image ++ * @ram_code_size size of SDMA ram image ++ * @script_addrs Stores the start address of the SDMA scripts ++ * (in SDMA memory space) ++ */ ++struct sdma_firmware_header { ++ u32 magic; ++ u32 version_major; ++ u32 version_minor; ++ u32 script_addrs_start; ++ u32 num_script_addrs; ++ u32 ram_code_start; ++ u32 ram_code_size; ++}; ++ ++struct sdma_driver_data { ++ int chnenbl0; ++ int num_events; ++ struct sdma_script_start_addrs *script_addrs; ++}; ++ ++struct sdma_engine { ++ struct device *dev; ++ struct device_dma_parameters dma_parms; ++ struct sdma_channel channel[MAX_DMA_CHANNELS]; ++ struct sdma_channel_control *channel_control; ++ void __iomem *regs; ++ struct sdma_context_data *context; ++ dma_addr_t context_phys; ++ struct dma_device dma_device; ++ struct clk *clk_ipg; ++ struct clk *clk_ahb; ++ spinlock_t channel_0_lock; ++ u32 script_number; ++ struct sdma_script_start_addrs *script_addrs; ++ const struct sdma_driver_data *drvdata; ++}; ++ ++static struct sdma_driver_data sdma_imx31 = { ++ .chnenbl0 = SDMA_CHNENBL0_IMX31, ++ .num_events = 32, ++}; ++ ++static struct sdma_script_start_addrs sdma_script_imx25 = { ++ .ap_2_ap_addr = 729, ++ .uart_2_mcu_addr = 904, ++ .per_2_app_addr = 1255, ++ .mcu_2_app_addr = 834, ++ .uartsh_2_mcu_addr = 1120, ++ .per_2_shp_addr = 1329, ++ .mcu_2_shp_addr = 1048, ++ .ata_2_mcu_addr = 1560, ++ .mcu_2_ata_addr = 1479, ++ .app_2_per_addr = 1189, ++ .app_2_mcu_addr = 770, ++ .shp_2_per_addr = 1407, ++ .shp_2_mcu_addr = 979, ++}; ++ ++static struct sdma_driver_data sdma_imx25 = { ++ .chnenbl0 = SDMA_CHNENBL0_IMX35, ++ .num_events = 48, ++ .script_addrs = &sdma_script_imx25, ++}; ++ ++static struct sdma_driver_data sdma_imx35 = { ++ .chnenbl0 = SDMA_CHNENBL0_IMX35, ++ .num_events = 48, ++}; ++ ++static struct sdma_script_start_addrs sdma_script_imx51 = { ++ .ap_2_ap_addr = 642, ++ .uart_2_mcu_addr = 817, ++ .mcu_2_app_addr = 747, ++ .mcu_2_shp_addr = 961, ++ .ata_2_mcu_addr = 1473, ++ .mcu_2_ata_addr = 1392, ++ .app_2_per_addr = 1033, ++ .app_2_mcu_addr = 683, ++ .shp_2_per_addr = 1251, ++ .shp_2_mcu_addr = 892, ++}; ++ ++static struct sdma_driver_data sdma_imx51 = { ++ .chnenbl0 = SDMA_CHNENBL0_IMX35, ++ .num_events = 48, ++ .script_addrs = &sdma_script_imx51, ++}; ++ ++static struct sdma_script_start_addrs sdma_script_imx53 = { ++ .ap_2_ap_addr = 642, ++ .app_2_mcu_addr = 683, ++ .mcu_2_app_addr = 747, ++ .uart_2_mcu_addr = 817, ++ .shp_2_mcu_addr = 891, ++ .mcu_2_shp_addr = 960, ++ .uartsh_2_mcu_addr = 1032, ++ .spdif_2_mcu_addr = 1100, ++ .mcu_2_spdif_addr = 1134, ++ .firi_2_mcu_addr = 1193, ++ .mcu_2_firi_addr = 1290, ++}; ++ ++static struct sdma_driver_data sdma_imx53 = { ++ .chnenbl0 = SDMA_CHNENBL0_IMX35, ++ .num_events = 48, ++ .script_addrs = &sdma_script_imx53, ++}; ++ ++static struct sdma_script_start_addrs sdma_script_imx6q = { ++ .ap_2_ap_addr = 642, ++ .uart_2_mcu_addr = 817, ++ .mcu_2_app_addr = 747, ++ .per_2_per_addr = 6331, ++ .uartsh_2_mcu_addr = 1032, ++ .mcu_2_shp_addr = 960, ++ .app_2_mcu_addr = 683, ++ .shp_2_mcu_addr = 891, ++ .spdif_2_mcu_addr = 1100, ++ .mcu_2_spdif_addr = 1134, ++}; ++ ++static struct sdma_driver_data sdma_imx6q = { ++ .chnenbl0 = SDMA_CHNENBL0_IMX35, ++ .num_events = 48, ++ .script_addrs = &sdma_script_imx6q, ++}; ++ ++static struct platform_device_id sdma_devtypes[] = { ++ { ++ .name = "imx25-sdma", ++ .driver_data = (unsigned long)&sdma_imx25, ++ }, { ++ .name = "imx31-sdma", ++ .driver_data = (unsigned long)&sdma_imx31, ++ }, { ++ .name = "imx35-sdma", ++ .driver_data = (unsigned long)&sdma_imx35, ++ }, { ++ .name = "imx51-sdma", ++ .driver_data = (unsigned long)&sdma_imx51, ++ }, { ++ .name = "imx53-sdma", ++ .driver_data = (unsigned long)&sdma_imx53, ++ }, { ++ .name = "imx6q-sdma", ++ .driver_data = (unsigned long)&sdma_imx6q, ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(platform, sdma_devtypes); ++ ++static const struct of_device_id sdma_dt_ids[] = { ++ { .compatible = "fsl,imx6q-sdma", .data = &sdma_imx6q, }, ++ { .compatible = "fsl,imx53-sdma", .data = &sdma_imx53, }, ++ { .compatible = "fsl,imx51-sdma", .data = &sdma_imx51, }, ++ { .compatible = "fsl,imx35-sdma", .data = &sdma_imx35, }, ++ { .compatible = "fsl,imx31-sdma", .data = &sdma_imx31, }, ++ { .compatible = "fsl,imx25-sdma", .data = &sdma_imx25, }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, sdma_dt_ids); ++ ++#define SDMA_H_CONFIG_DSPDMA BIT(12) /* indicates if the DSPDMA is used */ ++#define SDMA_H_CONFIG_RTD_PINS BIT(11) /* indicates if Real-Time Debug pins are enabled */ ++#define SDMA_H_CONFIG_ACR BIT(4) /* indicates if AHB freq /core freq = 2 or 1 */ ++#define SDMA_H_CONFIG_CSM (3) /* indicates which context switch mode is selected*/ ++ ++static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event) ++{ ++ u32 chnenbl0 = sdma->drvdata->chnenbl0; ++ return chnenbl0 + event * 4; ++} ++ ++static int sdma_config_ownership(struct sdma_channel *sdmac, ++ bool event_override, bool mcu_override, bool dsp_override) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int channel = sdmac->channel; ++ unsigned long evt, mcu, dsp; ++ ++ if (event_override && mcu_override && dsp_override) ++ return -EINVAL; ++ ++ evt = readl_relaxed(sdma->regs + SDMA_H_EVTOVR); ++ mcu = readl_relaxed(sdma->regs + SDMA_H_HOSTOVR); ++ dsp = readl_relaxed(sdma->regs + SDMA_H_DSPOVR); ++ ++ if (dsp_override) ++ __clear_bit(channel, &dsp); ++ else ++ __set_bit(channel, &dsp); ++ ++ if (event_override) ++ __clear_bit(channel, &evt); ++ else ++ __set_bit(channel, &evt); ++ ++ if (mcu_override) ++ __clear_bit(channel, &mcu); ++ else ++ __set_bit(channel, &mcu); ++ ++ writel_relaxed(evt, sdma->regs + SDMA_H_EVTOVR); ++ writel_relaxed(mcu, sdma->regs + SDMA_H_HOSTOVR); ++ writel_relaxed(dsp, sdma->regs + SDMA_H_DSPOVR); ++ ++ return 0; ++} ++ ++static void sdma_enable_channel(struct sdma_engine *sdma, int channel) ++{ ++ writel(BIT(channel), sdma->regs + SDMA_H_START); ++} ++ ++/* ++ * sdma_run_channel0 - run a channel and wait till it's done ++ */ ++static int sdma_run_channel0(struct sdma_engine *sdma) ++{ ++ int ret; ++ unsigned long timeout = 500; ++ ++ sdma_enable_channel(sdma, 0); ++ ++ while (!(ret = readl_relaxed(sdma->regs + SDMA_H_INTR) & 1)) { ++ if (timeout-- <= 0) ++ break; ++ udelay(1); ++ } ++ ++ if (ret) { ++ /* Clear the interrupt status */ ++ writel_relaxed(ret, sdma->regs + SDMA_H_INTR); ++ } else { ++ dev_err(sdma->dev, "Timeout waiting for CH0 ready\n"); ++ } ++ ++ return ret ? 0 : -ETIMEDOUT; ++} ++ ++static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, ++ u32 address) ++{ ++ struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd; ++ void *buf_virt; ++ dma_addr_t buf_phys; ++ int ret; ++ unsigned long flags; ++ ++ buf_virt = dma_alloc_coherent(NULL, ++ size, ++ &buf_phys, GFP_KERNEL); ++ if (!buf_virt) { ++ return -ENOMEM; ++ } ++ ++ spin_lock_irqsave(&sdma->channel_0_lock, flags); ++ ++ bd0->mode.command = C0_SETPM; ++ bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; ++ bd0->mode.count = size / 2; ++ bd0->buffer_addr = buf_phys; ++ bd0->ext_buffer_addr = address; ++ ++ memcpy(buf_virt, buf, size); ++ ++ ret = sdma_run_channel0(sdma); ++ ++ spin_unlock_irqrestore(&sdma->channel_0_lock, flags); ++ ++ dma_free_coherent(NULL, size, buf_virt, buf_phys); ++ ++ return ret; ++} ++ ++static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int channel = sdmac->channel; ++ unsigned long val; ++ u32 chnenbl = chnenbl_ofs(sdma, event); ++ ++ val = readl_relaxed(sdma->regs + chnenbl); ++ __set_bit(channel, &val); ++ writel_relaxed(val, sdma->regs + chnenbl); ++} ++ ++static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int channel = sdmac->channel; ++ u32 chnenbl = chnenbl_ofs(sdma, event); ++ unsigned long val; ++ ++ val = readl_relaxed(sdma->regs + chnenbl); ++ __clear_bit(channel, &val); ++ writel_relaxed(val, sdma->regs + chnenbl); ++} ++ ++static void sdma_handle_channel_loop(struct sdma_channel *sdmac) ++{ ++ if (sdmac->desc.callback) ++ sdmac->desc.callback(sdmac->desc.callback_param); ++} ++ ++static void sdma_update_channel_loop(struct sdma_channel *sdmac) ++{ ++ struct sdma_buffer_descriptor *bd; ++ ++ /* ++ * loop mode. Iterate over descriptors, re-setup them and ++ * call callback function. ++ */ ++ while (1) { ++ bd = &sdmac->bd[sdmac->buf_tail]; ++ ++ if (bd->mode.status & BD_DONE) ++ break; ++ ++ if (bd->mode.status & BD_RROR) ++ sdmac->status = DMA_ERROR; ++ ++ bd->mode.status |= BD_DONE; ++ sdmac->buf_tail++; ++ sdmac->buf_tail %= sdmac->num_bd; ++ } ++} ++ ++static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) ++{ ++ struct sdma_buffer_descriptor *bd; ++ int i, error = 0; ++ ++ sdmac->chn_real_count = 0; ++ /* ++ * non loop mode. Iterate over all descriptors, collect ++ * errors and call callback function ++ */ ++ for (i = 0; i < sdmac->num_bd; i++) { ++ bd = &sdmac->bd[i]; ++ ++ if (bd->mode.status & (BD_DONE | BD_RROR)) ++ error = -EIO; ++ sdmac->chn_real_count += bd->mode.count; ++ } ++ ++ if (error) ++ sdmac->status = DMA_ERROR; ++ else ++ sdmac->status = DMA_COMPLETE; ++ ++ dma_cookie_complete(&sdmac->desc); ++ if (sdmac->desc.callback) ++ sdmac->desc.callback(sdmac->desc.callback_param); ++} ++ ++static void sdma_tasklet(unsigned long data) ++{ ++ struct sdma_channel *sdmac = (struct sdma_channel *) data; ++ ++ if (sdmac->flags & IMX_DMA_SG_LOOP) ++ sdma_handle_channel_loop(sdmac); ++ else ++ mxc_sdma_handle_channel_normal(sdmac); ++} ++ ++static irqreturn_t sdma_int_handler(int irq, void *dev_id) ++{ ++ struct sdma_engine *sdma = dev_id; ++ unsigned long stat; ++ ++ stat = readl_relaxed(sdma->regs + SDMA_H_INTR); ++ /* not interested in channel 0 interrupts */ ++ stat &= ~1; ++ writel_relaxed(stat, sdma->regs + SDMA_H_INTR); ++ ++ while (stat) { ++ int channel = fls(stat) - 1; ++ struct sdma_channel *sdmac = &sdma->channel[channel]; ++ ++ if (sdmac->flags & IMX_DMA_SG_LOOP) ++ sdma_update_channel_loop(sdmac); ++ ++ tasklet_schedule(&sdmac->tasklet); ++ ++ __clear_bit(channel, &stat); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * sets the pc of SDMA script according to the peripheral type ++ */ ++static void sdma_get_pc(struct sdma_channel *sdmac, ++ enum sdma_peripheral_type peripheral_type) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int per_2_emi = 0, emi_2_per = 0; ++ /* ++ * These are needed once we start to support transfers between ++ * two peripherals or memory-to-memory transfers ++ */ ++ int per_2_per = 0, emi_2_emi = 0; ++ ++ sdmac->pc_from_device = 0; ++ sdmac->pc_to_device = 0; ++ ++ switch (peripheral_type) { ++ case IMX_DMATYPE_MEMORY: ++ emi_2_emi = sdma->script_addrs->ap_2_ap_addr; ++ break; ++ case IMX_DMATYPE_DSP: ++ emi_2_per = sdma->script_addrs->bp_2_ap_addr; ++ per_2_emi = sdma->script_addrs->ap_2_bp_addr; ++ break; ++ case IMX_DMATYPE_FIRI: ++ per_2_emi = sdma->script_addrs->firi_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_firi_addr; ++ break; ++ case IMX_DMATYPE_UART: ++ per_2_emi = sdma->script_addrs->uart_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_app_addr; ++ break; ++ case IMX_DMATYPE_UART_SP: ++ per_2_emi = sdma->script_addrs->uartsh_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_shp_addr; ++ break; ++ case IMX_DMATYPE_ATA: ++ per_2_emi = sdma->script_addrs->ata_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_ata_addr; ++ break; ++ case IMX_DMATYPE_CSPI: ++ case IMX_DMATYPE_EXT: ++ case IMX_DMATYPE_SSI: ++ per_2_emi = sdma->script_addrs->app_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_app_addr; ++ break; ++ case IMX_DMATYPE_SSI_DUAL: ++ per_2_emi = sdma->script_addrs->ssish_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_ssish_addr; ++ break; ++ case IMX_DMATYPE_SSI_SP: ++ case IMX_DMATYPE_MMC: ++ case IMX_DMATYPE_SDHC: ++ case IMX_DMATYPE_CSPI_SP: ++ case IMX_DMATYPE_ESAI: ++ case IMX_DMATYPE_MSHC_SP: ++ per_2_emi = sdma->script_addrs->shp_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_shp_addr; ++ break; ++ case IMX_DMATYPE_ASRC: ++ per_2_emi = sdma->script_addrs->asrc_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->asrc_2_mcu_addr; ++ per_2_per = sdma->script_addrs->per_2_per_addr; ++ break; ++ case IMX_DMATYPE_MSHC: ++ per_2_emi = sdma->script_addrs->mshc_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_mshc_addr; ++ break; ++ case IMX_DMATYPE_CCM: ++ per_2_emi = sdma->script_addrs->dptc_dvfs_addr; ++ break; ++ case IMX_DMATYPE_SPDIF: ++ per_2_emi = sdma->script_addrs->spdif_2_mcu_addr; ++ emi_2_per = sdma->script_addrs->mcu_2_spdif_addr; ++ break; ++ case IMX_DMATYPE_IPU_MEMORY: ++ emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr; ++ break; ++ default: ++ break; ++ } ++ ++ sdmac->pc_from_device = per_2_emi; ++ sdmac->pc_to_device = emi_2_per; ++} ++ ++static int sdma_load_context(struct sdma_channel *sdmac) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int channel = sdmac->channel; ++ int load_address; ++ struct sdma_context_data *context = sdma->context; ++ struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd; ++ int ret; ++ unsigned long flags; ++ ++ if (sdmac->direction == DMA_DEV_TO_MEM) { ++ load_address = sdmac->pc_from_device; ++ } else { ++ load_address = sdmac->pc_to_device; ++ } ++ ++ if (load_address < 0) ++ return load_address; ++ ++ dev_dbg(sdma->dev, "load_address = %d\n", load_address); ++ dev_dbg(sdma->dev, "wml = 0x%08x\n", (u32)sdmac->watermark_level); ++ dev_dbg(sdma->dev, "shp_addr = 0x%08x\n", sdmac->shp_addr); ++ dev_dbg(sdma->dev, "per_addr = 0x%08x\n", sdmac->per_addr); ++ dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", (u32)sdmac->event_mask[0]); ++ dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", (u32)sdmac->event_mask[1]); ++ ++ spin_lock_irqsave(&sdma->channel_0_lock, flags); ++ ++ memset(context, 0, sizeof(*context)); ++ context->channel_state.pc = load_address; ++ ++ /* Send by context the event mask,base address for peripheral ++ * and watermark level ++ */ ++ context->gReg[0] = sdmac->event_mask[1]; ++ context->gReg[1] = sdmac->event_mask[0]; ++ context->gReg[2] = sdmac->per_addr; ++ context->gReg[6] = sdmac->shp_addr; ++ context->gReg[7] = sdmac->watermark_level; ++ ++ bd0->mode.command = C0_SETDM; ++ bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; ++ bd0->mode.count = sizeof(*context) / 4; ++ bd0->buffer_addr = sdma->context_phys; ++ bd0->ext_buffer_addr = 2048 + (sizeof(*context) / 4) * channel; ++ ret = sdma_run_channel0(sdma); ++ ++ spin_unlock_irqrestore(&sdma->channel_0_lock, flags); ++ ++ return ret; ++} ++ ++static void sdma_disable_channel(struct sdma_channel *sdmac) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int channel = sdmac->channel; ++ ++ writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP); ++ sdmac->status = DMA_ERROR; ++} ++ ++static int sdma_config_channel(struct sdma_channel *sdmac) ++{ ++ int ret; ++ ++ sdma_disable_channel(sdmac); ++ ++ sdmac->event_mask[0] = 0; ++ sdmac->event_mask[1] = 0; ++ sdmac->shp_addr = 0; ++ sdmac->per_addr = 0; ++ ++ if (sdmac->event_id0) { ++ if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events) ++ return -EINVAL; ++ sdma_event_enable(sdmac, sdmac->event_id0); ++ } ++ ++ switch (sdmac->peripheral_type) { ++ case IMX_DMATYPE_DSP: ++ sdma_config_ownership(sdmac, false, true, true); ++ break; ++ case IMX_DMATYPE_MEMORY: ++ sdma_config_ownership(sdmac, false, true, false); ++ break; ++ default: ++ sdma_config_ownership(sdmac, true, true, false); ++ break; ++ } ++ ++ sdma_get_pc(sdmac, sdmac->peripheral_type); ++ ++ if ((sdmac->peripheral_type != IMX_DMATYPE_MEMORY) && ++ (sdmac->peripheral_type != IMX_DMATYPE_DSP)) { ++ /* Handle multiple event channels differently */ ++ if (sdmac->event_id1) { ++ sdmac->event_mask[1] = BIT(sdmac->event_id1 % 32); ++ if (sdmac->event_id1 > 31) ++ __set_bit(31, &sdmac->watermark_level); ++ sdmac->event_mask[0] = BIT(sdmac->event_id0 % 32); ++ if (sdmac->event_id0 > 31) ++ __set_bit(30, &sdmac->watermark_level); ++ } else { ++ __set_bit(sdmac->event_id0, sdmac->event_mask); ++ } ++ /* Watermark Level */ ++ sdmac->watermark_level |= sdmac->watermark_level; ++ /* Address */ ++ sdmac->shp_addr = sdmac->per_address; ++ } else { ++ sdmac->watermark_level = 0; /* FIXME: M3_BASE_ADDRESS */ ++ } ++ ++ ret = sdma_load_context(sdmac); ++ ++ return ret; ++} ++ ++static int sdma_set_channel_priority(struct sdma_channel *sdmac, ++ unsigned int priority) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int channel = sdmac->channel; ++ ++ if (priority < MXC_SDMA_MIN_PRIORITY ++ || priority > MXC_SDMA_MAX_PRIORITY) { ++ return -EINVAL; ++ } ++ ++ writel_relaxed(priority, sdma->regs + SDMA_CHNPRI_0 + 4 * channel); ++ ++ return 0; ++} ++ ++static int sdma_request_channel(struct sdma_channel *sdmac) ++{ ++ struct sdma_engine *sdma = sdmac->sdma; ++ int channel = sdmac->channel; ++ int ret = -EBUSY; ++ ++ sdmac->bd = dma_alloc_coherent(NULL, PAGE_SIZE, &sdmac->bd_phys, GFP_KERNEL); ++ if (!sdmac->bd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ memset(sdmac->bd, 0, PAGE_SIZE); ++ ++ sdma->channel_control[channel].base_bd_ptr = sdmac->bd_phys; ++ sdma->channel_control[channel].current_bd_ptr = sdmac->bd_phys; ++ ++ sdma_set_channel_priority(sdmac, MXC_SDMA_DEFAULT_PRIORITY); ++ return 0; ++out: ++ ++ return ret; ++} ++ ++static struct sdma_channel *to_sdma_chan(struct dma_chan *chan) ++{ ++ return container_of(chan, struct sdma_channel, chan); ++} ++ ++static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx) ++{ ++ unsigned long flags; ++ struct sdma_channel *sdmac = to_sdma_chan(tx->chan); ++ dma_cookie_t cookie; ++ ++ spin_lock_irqsave(&sdmac->lock, flags); ++ ++ cookie = dma_cookie_assign(tx); ++ ++ spin_unlock_irqrestore(&sdmac->lock, flags); ++ ++ return cookie; ++} ++ ++static int sdma_alloc_chan_resources(struct dma_chan *chan) ++{ ++ struct sdma_channel *sdmac = to_sdma_chan(chan); ++ struct imx_dma_data *data = chan->private; ++ int prio, ret; ++ ++ if (!data) ++ return -EINVAL; ++ ++ switch (data->priority) { ++ case DMA_PRIO_HIGH: ++ prio = 3; ++ break; ++ case DMA_PRIO_MEDIUM: ++ prio = 2; ++ break; ++ case DMA_PRIO_LOW: ++ default: ++ prio = 1; ++ break; ++ } ++ ++ sdmac->peripheral_type = data->peripheral_type; ++ sdmac->event_id0 = data->dma_request; ++ ++ clk_enable(sdmac->sdma->clk_ipg); ++ clk_enable(sdmac->sdma->clk_ahb); ++ ++ ret = sdma_request_channel(sdmac); ++ if (ret) ++ return ret; ++ ++ ret = sdma_set_channel_priority(sdmac, prio); ++ if (ret) ++ return ret; ++ ++ dma_async_tx_descriptor_init(&sdmac->desc, chan); ++ sdmac->desc.tx_submit = sdma_tx_submit; ++ /* txd.flags will be overwritten in prep funcs */ ++ sdmac->desc.flags = DMA_CTRL_ACK; ++ ++ return 0; ++} ++ ++static void sdma_free_chan_resources(struct dma_chan *chan) ++{ ++ struct sdma_channel *sdmac = to_sdma_chan(chan); ++ struct sdma_engine *sdma = sdmac->sdma; ++ ++ sdma_disable_channel(sdmac); ++ ++ if (sdmac->event_id0) ++ sdma_event_disable(sdmac, sdmac->event_id0); ++ if (sdmac->event_id1) ++ sdma_event_disable(sdmac, sdmac->event_id1); ++ ++ sdmac->event_id0 = 0; ++ sdmac->event_id1 = 0; ++ ++ sdma_set_channel_priority(sdmac, 0); ++ ++ dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); ++ ++ clk_disable(sdma->clk_ipg); ++ clk_disable(sdma->clk_ahb); ++} ++ ++static struct dma_async_tx_descriptor *sdma_prep_slave_sg( ++ struct dma_chan *chan, struct scatterlist *sgl, ++ unsigned int sg_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) ++{ ++ struct sdma_channel *sdmac = to_sdma_chan(chan); ++ struct sdma_engine *sdma = sdmac->sdma; ++ int ret, i, count; ++ int channel = sdmac->channel; ++ struct scatterlist *sg; ++ ++ if (sdmac->status == DMA_IN_PROGRESS) ++ return NULL; ++ sdmac->status = DMA_IN_PROGRESS; ++ ++ sdmac->flags = 0; ++ ++ sdmac->buf_tail = 0; ++ ++ dev_dbg(sdma->dev, "setting up %d entries for channel %d.\n", ++ sg_len, channel); ++ ++ sdmac->direction = direction; ++ ret = sdma_load_context(sdmac); ++ if (ret) ++ goto err_out; ++ ++ if (sg_len > NUM_BD) { ++ dev_err(sdma->dev, "SDMA channel %d: maximum number of sg exceeded: %d > %d\n", ++ channel, sg_len, NUM_BD); ++ ret = -EINVAL; ++ goto err_out; ++ } ++ ++ sdmac->chn_count = 0; ++ for_each_sg(sgl, sg, sg_len, i) { ++ struct sdma_buffer_descriptor *bd = &sdmac->bd[i]; ++ int param; ++ ++ bd->buffer_addr = sg->dma_address; ++ ++ count = sg_dma_len(sg); ++ ++ if (count > 0xffff) { ++ dev_err(sdma->dev, "SDMA channel %d: maximum bytes for sg entry exceeded: %d > %d\n", ++ channel, count, 0xffff); ++ ret = -EINVAL; ++ goto err_out; ++ } ++ ++ bd->mode.count = count; ++ sdmac->chn_count += count; ++ ++ if (sdmac->word_size > DMA_SLAVE_BUSWIDTH_4_BYTES) { ++ ret = -EINVAL; ++ goto err_out; ++ } ++ ++ switch (sdmac->word_size) { ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ bd->mode.command = 0; ++ if (count & 3 || sg->dma_address & 3) ++ return NULL; ++ break; ++ case DMA_SLAVE_BUSWIDTH_2_BYTES: ++ bd->mode.command = 2; ++ if (count & 1 || sg->dma_address & 1) ++ return NULL; ++ break; ++ case DMA_SLAVE_BUSWIDTH_1_BYTE: ++ bd->mode.command = 1; ++ break; ++ default: ++ return NULL; ++ } ++ ++ param = BD_DONE | BD_EXTD | BD_CONT; ++ ++ if (i + 1 == sg_len) { ++ param |= BD_INTR; ++ param |= BD_LAST; ++ param &= ~BD_CONT; ++ } ++ ++ dev_dbg(sdma->dev, "entry %d: count: %d dma: %#llx %s%s\n", ++ i, count, (u64)sg->dma_address, ++ param & BD_WRAP ? "wrap" : "", ++ param & BD_INTR ? " intr" : ""); ++ ++ bd->mode.status = param; ++ } ++ ++ sdmac->num_bd = sg_len; ++ sdma->channel_control[channel].current_bd_ptr = sdmac->bd_phys; ++ ++ return &sdmac->desc; ++err_out: ++ sdmac->status = DMA_ERROR; ++ return NULL; ++} ++ ++static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( ++ struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, ++ size_t period_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) ++{ ++ struct sdma_channel *sdmac = to_sdma_chan(chan); ++ struct sdma_engine *sdma = sdmac->sdma; ++ int num_periods = buf_len / period_len; ++ int channel = sdmac->channel; ++ int ret, i = 0, buf = 0; ++ ++ dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); ++ ++ if (sdmac->status == DMA_IN_PROGRESS) ++ return NULL; ++ ++ sdmac->status = DMA_IN_PROGRESS; ++ ++ sdmac->buf_tail = 0; ++ sdmac->period_len = period_len; ++ ++ sdmac->flags |= IMX_DMA_SG_LOOP; ++ sdmac->direction = direction; ++ ret = sdma_load_context(sdmac); ++ if (ret) ++ goto err_out; ++ ++ if (num_periods > NUM_BD) { ++ dev_err(sdma->dev, "SDMA channel %d: maximum number of sg exceeded: %d > %d\n", ++ channel, num_periods, NUM_BD); ++ goto err_out; ++ } ++ ++ if (period_len > 0xffff) { ++ dev_err(sdma->dev, "SDMA channel %d: maximum period size exceeded: %d > %d\n", ++ channel, period_len, 0xffff); ++ goto err_out; ++ } ++ ++ while (buf < buf_len) { ++ struct sdma_buffer_descriptor *bd = &sdmac->bd[i]; ++ int param; ++ ++ bd->buffer_addr = dma_addr; ++ ++ bd->mode.count = period_len; ++ ++ if (sdmac->word_size > DMA_SLAVE_BUSWIDTH_4_BYTES) ++ goto err_out; ++ if (sdmac->word_size == DMA_SLAVE_BUSWIDTH_4_BYTES) ++ bd->mode.command = 0; ++ else ++ bd->mode.command = sdmac->word_size; ++ ++ param = BD_DONE | BD_EXTD | BD_CONT | BD_INTR; ++ if (i + 1 == num_periods) ++ param |= BD_WRAP; ++ ++ dev_dbg(sdma->dev, "entry %d: count: %d dma: %#llx %s%s\n", ++ i, period_len, (u64)dma_addr, ++ param & BD_WRAP ? "wrap" : "", ++ param & BD_INTR ? " intr" : ""); ++ ++ bd->mode.status = param; ++ ++ dma_addr += period_len; ++ buf += period_len; ++ ++ i++; ++ } ++ ++ sdmac->num_bd = num_periods; ++ sdma->channel_control[channel].current_bd_ptr = sdmac->bd_phys; ++ ++ return &sdmac->desc; ++err_out: ++ sdmac->status = DMA_ERROR; ++ return NULL; ++} ++ ++static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct sdma_channel *sdmac = to_sdma_chan(chan); ++ struct dma_slave_config *dmaengine_cfg = (void *)arg; ++ ++ switch (cmd) { ++ case DMA_TERMINATE_ALL: ++ sdma_disable_channel(sdmac); ++ return 0; ++ case DMA_SLAVE_CONFIG: ++ if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { ++ sdmac->per_address = dmaengine_cfg->src_addr; ++ sdmac->watermark_level = dmaengine_cfg->src_maxburst * ++ dmaengine_cfg->src_addr_width; ++ sdmac->word_size = dmaengine_cfg->src_addr_width; ++ } else { ++ sdmac->per_address = dmaengine_cfg->dst_addr; ++ sdmac->watermark_level = dmaengine_cfg->dst_maxburst * ++ dmaengine_cfg->dst_addr_width; ++ sdmac->word_size = dmaengine_cfg->dst_addr_width; ++ } ++ sdmac->direction = dmaengine_cfg->direction; ++ return sdma_config_channel(sdmac); ++ default: ++ return -ENOSYS; ++ } ++ ++ return -EINVAL; ++} ++ ++static enum dma_status sdma_tx_status(struct dma_chan *chan, ++ dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct sdma_channel *sdmac = to_sdma_chan(chan); ++ u32 residue; ++ ++ if (sdmac->flags & IMX_DMA_SG_LOOP) ++ residue = (sdmac->num_bd - sdmac->buf_tail) * sdmac->period_len; ++ else ++ residue = sdmac->chn_count - sdmac->chn_real_count; ++ ++ dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, ++ residue); ++ ++ return sdmac->status; ++} ++ ++static void sdma_issue_pending(struct dma_chan *chan) ++{ ++ struct sdma_channel *sdmac = to_sdma_chan(chan); ++ struct sdma_engine *sdma = sdmac->sdma; ++ ++ if (sdmac->status == DMA_IN_PROGRESS) ++ sdma_enable_channel(sdma, sdmac->channel); ++} ++ ++#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 ++#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2 38 ++ ++static void sdma_add_scripts(struct sdma_engine *sdma, ++ const struct sdma_script_start_addrs *addr) ++{ ++ s32 *addr_arr = (u32 *)addr; ++ s32 *saddr_arr = (u32 *)sdma->script_addrs; ++ int i; ++ ++ /* use the default firmware in ROM if missing external firmware */ ++ if (!sdma->script_number) ++ sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; ++ ++ for (i = 0; i < sdma->script_number; i++) ++ if (addr_arr[i] > 0) ++ saddr_arr[i] = addr_arr[i]; ++} ++ ++static void sdma_load_firmware(const struct firmware *fw, void *context) ++{ ++ struct sdma_engine *sdma = context; ++ const struct sdma_firmware_header *header; ++ const struct sdma_script_start_addrs *addr; ++ unsigned short *ram_code; ++ ++ if (!fw) { ++ dev_err(sdma->dev, "firmware not found\n"); ++ return; ++ } ++ ++ if (fw->size < sizeof(*header)) ++ goto err_firmware; ++ ++ header = (struct sdma_firmware_header *)fw->data; ++ ++ if (header->magic != SDMA_FIRMWARE_MAGIC) ++ goto err_firmware; ++ if (header->ram_code_start + header->ram_code_size > fw->size) ++ goto err_firmware; ++ switch (header->version_major) { ++ case 1: ++ sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; ++ break; ++ case 2: ++ sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2; ++ break; ++ default: ++ dev_err(sdma->dev, "unknown firmware version\n"); ++ goto err_firmware; ++ } ++ ++ addr = (void *)header + header->script_addrs_start; ++ ram_code = (void *)header + header->ram_code_start; ++ ++ clk_enable(sdma->clk_ipg); ++ clk_enable(sdma->clk_ahb); ++ /* download the RAM image for SDMA */ ++ sdma_load_script(sdma, ram_code, ++ header->ram_code_size, ++ addr->ram_code_start_addr); ++ clk_disable(sdma->clk_ipg); ++ clk_disable(sdma->clk_ahb); ++ ++ sdma_add_scripts(sdma, addr); ++ ++ dev_info(sdma->dev, "loaded firmware %d.%d\n", ++ header->version_major, ++ header->version_minor); ++ ++err_firmware: ++ release_firmware(fw); ++} ++ ++static int __init sdma_get_firmware(struct sdma_engine *sdma, ++ const char *fw_name) ++{ ++ int ret; ++ ++ ret = request_firmware_nowait(THIS_MODULE, ++ FW_ACTION_HOTPLUG, fw_name, sdma->dev, ++ GFP_KERNEL, sdma, sdma_load_firmware); ++ ++ return ret; ++} ++ ++static int __init sdma_init(struct sdma_engine *sdma) ++{ ++ int i, ret; ++ dma_addr_t ccb_phys; ++ ++ clk_enable(sdma->clk_ipg); ++ clk_enable(sdma->clk_ahb); ++ ++ /* Be sure SDMA has not started yet */ ++ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); ++ ++ sdma->channel_control = dma_alloc_coherent(NULL, ++ MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + ++ sizeof(struct sdma_context_data), ++ &ccb_phys, GFP_KERNEL); ++ ++ if (!sdma->channel_control) { ++ ret = -ENOMEM; ++ goto err_dma_alloc; ++ } ++ ++ sdma->context = (void *)sdma->channel_control + ++ MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control); ++ sdma->context_phys = ccb_phys + ++ MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control); ++ ++ /* Zero-out the CCB structures array just allocated */ ++ memset(sdma->channel_control, 0, ++ MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control)); ++ ++ /* disable all channels */ ++ for (i = 0; i < sdma->drvdata->num_events; i++) ++ writel_relaxed(0, sdma->regs + chnenbl_ofs(sdma, i)); ++ ++ /* All channels have priority 0 */ ++ for (i = 0; i < MAX_DMA_CHANNELS; i++) ++ writel_relaxed(0, sdma->regs + SDMA_CHNPRI_0 + i * 4); ++ ++ ret = sdma_request_channel(&sdma->channel[0]); ++ if (ret) ++ goto err_dma_alloc; ++ ++ sdma_config_ownership(&sdma->channel[0], false, true, false); ++ ++ /* Set Command Channel (Channel Zero) */ ++ writel_relaxed(0x4050, sdma->regs + SDMA_CHN0ADDR); ++ ++ /* Set bits of CONFIG register but with static context switching */ ++ /* FIXME: Check whether to set ACR bit depending on clock ratios */ ++ writel_relaxed(0, sdma->regs + SDMA_H_CONFIG); ++ ++ writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR); ++ ++ /* Set bits of CONFIG register with given context switching mode */ ++ writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); ++ ++ /* Initializes channel's priorities */ ++ sdma_set_channel_priority(&sdma->channel[0], 7); ++ ++ clk_disable(sdma->clk_ipg); ++ clk_disable(sdma->clk_ahb); ++ ++ return 0; ++ ++err_dma_alloc: ++ clk_disable(sdma->clk_ipg); ++ clk_disable(sdma->clk_ahb); ++ dev_err(sdma->dev, "initialisation failed with %d\n", ret); ++ return ret; ++} ++ ++static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param) ++{ ++ struct imx_dma_data *data = fn_param; ++ ++ if (!imx_dma_is_general_purpose(chan)) ++ return false; ++ ++ chan->private = data; ++ ++ return true; ++} ++ ++static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec, ++ struct of_dma *ofdma) ++{ ++ struct sdma_engine *sdma = ofdma->of_dma_data; ++ dma_cap_mask_t mask = sdma->dma_device.cap_mask; ++ struct imx_dma_data data; ++ ++ if (dma_spec->args_count != 3) ++ return NULL; ++ ++ data.dma_request = dma_spec->args[0]; ++ data.peripheral_type = dma_spec->args[1]; ++ data.priority = dma_spec->args[2]; ++ ++ return dma_request_channel(mask, sdma_filter_fn, &data); ++} ++ ++static int __init sdma_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *of_id = ++ of_match_device(sdma_dt_ids, &pdev->dev); ++ struct device_node *np = pdev->dev.of_node; ++ const char *fw_name; ++ int ret; ++ int irq; ++ struct resource *iores; ++ struct sdma_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ int i; ++ struct sdma_engine *sdma; ++ s32 *saddr_arr; ++ const struct sdma_driver_data *drvdata = NULL; ++ ++ if (of_id) ++ drvdata = of_id->data; ++ else if (pdev->id_entry) ++ drvdata = (void *)pdev->id_entry->driver_data; ++ ++ if (!drvdata) { ++ dev_err(&pdev->dev, "unable to find driver data\n"); ++ return -EINVAL; ++ } ++ ++ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); ++ if (ret) ++ return ret; ++ ++ sdma = kzalloc(sizeof(*sdma), GFP_KERNEL); ++ if (!sdma) ++ return -ENOMEM; ++ ++ spin_lock_init(&sdma->channel_0_lock); ++ ++ sdma->dev = &pdev->dev; ++ sdma->drvdata = drvdata; ++ ++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ if (!iores || irq < 0) { ++ ret = -EINVAL; ++ goto err_irq; ++ } ++ ++ if (!request_mem_region(iores->start, resource_size(iores), pdev->name)) { ++ ret = -EBUSY; ++ goto err_request_region; ++ } ++ ++ sdma->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); ++ if (IS_ERR(sdma->clk_ipg)) { ++ ret = PTR_ERR(sdma->clk_ipg); ++ goto err_clk; ++ } ++ ++ sdma->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(sdma->clk_ahb)) { ++ ret = PTR_ERR(sdma->clk_ahb); ++ goto err_clk; ++ } ++ ++ clk_prepare(sdma->clk_ipg); ++ clk_prepare(sdma->clk_ahb); ++ ++ sdma->regs = ioremap(iores->start, resource_size(iores)); ++ if (!sdma->regs) { ++ ret = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ ret = request_irq(irq, sdma_int_handler, 0, "sdma", sdma); ++ if (ret) ++ goto err_request_irq; ++ ++ sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL); ++ if (!sdma->script_addrs) { ++ ret = -ENOMEM; ++ goto err_alloc; ++ } ++ ++ /* initially no scripts available */ ++ saddr_arr = (s32 *)sdma->script_addrs; ++ for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++) ++ saddr_arr[i] = -EINVAL; ++ ++ dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask); ++ dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask); ++ ++ INIT_LIST_HEAD(&sdma->dma_device.channels); ++ /* Initialize channel parameters */ ++ for (i = 0; i < MAX_DMA_CHANNELS; i++) { ++ struct sdma_channel *sdmac = &sdma->channel[i]; ++ ++ sdmac->sdma = sdma; ++ spin_lock_init(&sdmac->lock); ++ ++ sdmac->chan.device = &sdma->dma_device; ++ dma_cookie_init(&sdmac->chan); ++ sdmac->channel = i; ++ ++ tasklet_init(&sdmac->tasklet, sdma_tasklet, ++ (unsigned long) sdmac); ++ /* ++ * Add the channel to the DMAC list. Do not add channel 0 though ++ * because we need it internally in the SDMA driver. This also means ++ * that channel 0 in dmaengine counting matches sdma channel 1. ++ */ ++ if (i) ++ list_add_tail(&sdmac->chan.device_node, ++ &sdma->dma_device.channels); ++ } ++ ++ ret = sdma_init(sdma); ++ if (ret) ++ goto err_init; ++ ++ if (sdma->drvdata->script_addrs) ++ sdma_add_scripts(sdma, sdma->drvdata->script_addrs); ++ if (pdata && pdata->script_addrs) ++ sdma_add_scripts(sdma, pdata->script_addrs); ++ ++ if (pdata) { ++ ret = sdma_get_firmware(sdma, pdata->fw_name); ++ if (ret) ++ dev_warn(&pdev->dev, "failed to get firmware from platform data\n"); ++ } else { ++ /* ++ * Because that device tree does not encode ROM script address, ++ * the RAM script in firmware is mandatory for device tree ++ * probe, otherwise it fails. ++ */ ++ ret = of_property_read_string(np, "fsl,sdma-ram-script-name", ++ &fw_name); ++ if (ret) ++ dev_warn(&pdev->dev, "failed to get firmware name\n"); ++ else { ++ ret = sdma_get_firmware(sdma, fw_name); ++ if (ret) ++ dev_warn(&pdev->dev, "failed to get firmware from device tree\n"); ++ } ++ } ++ ++ sdma->dma_device.dev = &pdev->dev; ++ ++ sdma->dma_device.device_alloc_chan_resources = sdma_alloc_chan_resources; ++ sdma->dma_device.device_free_chan_resources = sdma_free_chan_resources; ++ sdma->dma_device.device_tx_status = sdma_tx_status; ++ sdma->dma_device.device_prep_slave_sg = sdma_prep_slave_sg; ++ sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic; ++ sdma->dma_device.device_control = sdma_control; ++ sdma->dma_device.device_issue_pending = sdma_issue_pending; ++ sdma->dma_device.dev->dma_parms = &sdma->dma_parms; ++ dma_set_max_seg_size(sdma->dma_device.dev, 65535); ++ ++ ret = dma_async_device_register(&sdma->dma_device); ++ if (ret) { ++ dev_err(&pdev->dev, "unable to register\n"); ++ goto err_init; ++ } ++ ++ if (np) { ++ ret = of_dma_controller_register(np, sdma_xlate, sdma); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to register controller\n"); ++ goto err_register; ++ } ++ } ++ ++ dev_info(sdma->dev, "initialized\n"); ++ ++ return 0; ++ ++err_register: ++ dma_async_device_unregister(&sdma->dma_device); ++err_init: ++ kfree(sdma->script_addrs); ++err_alloc: ++ free_irq(irq, sdma); ++err_request_irq: ++ iounmap(sdma->regs); ++err_ioremap: ++err_clk: ++ release_mem_region(iores->start, resource_size(iores)); ++err_request_region: ++err_irq: ++ kfree(sdma); ++ return ret; ++} ++ ++static int sdma_remove(struct platform_device *pdev) ++{ ++ return -EBUSY; ++} ++ ++static struct platform_driver sdma_driver = { ++ .driver = { ++ .name = "imx-sdma", ++ .of_match_table = sdma_dt_ids, ++ }, ++ .id_table = sdma_devtypes, ++ .remove = sdma_remove, ++}; ++ ++static int __init sdma_module_init(void) ++{ ++ return platform_driver_probe(&sdma_driver, sdma_probe); ++} ++module_init(sdma_module_init); ++ ++MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>"); ++MODULE_DESCRIPTION("i.MX SDMA driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.16.6.orig/drivers/gpu/ipu-v3/ipu-dc.c linux-3.16.6/drivers/gpu/ipu-v3/ipu-dc.c +--- linux-3.16.6.orig/drivers/gpu/ipu-v3/ipu-dc.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/gpu/ipu-v3/ipu-dc.c 2014-10-23 12:35:30.966220009 -0500 +@@ -93,6 +93,7 @@ + IPU_DC_MAP_BGR666, + IPU_DC_MAP_LVDS666, + IPU_DC_MAP_BGR24, ++ IPU_DC_MAP_RGB666, + }; + + struct ipu_dc { +@@ -161,6 +162,8 @@ + return IPU_DC_MAP_LVDS666; + case V4L2_PIX_FMT_BGR24: + return IPU_DC_MAP_BGR24; ++ case V4L2_PIX_FMT_RGB666: ++ return IPU_DC_MAP_RGB666; + default: + return -EINVAL; + } +@@ -452,6 +455,12 @@ + 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 -Nur linux-3.16.6.orig/drivers/gpu/ipu-v3/ipu-di.c linux-3.16.6/drivers/gpu/ipu-v3/ipu-di.c +--- linux-3.16.6.orig/drivers/gpu/ipu-v3/ipu-di.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/gpu/ipu-v3/ipu-di.c 2014-10-23 12:35:38.078220007 -0500 +@@ -595,7 +595,7 @@ + } + } + +- if (sig->clk_pol) ++ if (sig->clk_pol == CLK_POL_POSEDGE) + di_gen |= DI_GEN_POLARITY_DISP_CLK; + + ipu_di_write(di, di_gen, DI_GENERAL); +@@ -606,7 +606,7 @@ + reg = ipu_di_read(di, DI_POL); + reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); + +- if (sig->enable_pol) ++ if (sig->enable_pol == ENABLE_POL_HIGH) + reg |= DI_POL_DRDY_POLARITY_15; + if (sig->data_pol) + reg |= DI_POL_DRDY_DATA_POLARITY; +diff -Nur linux-3.16.6.orig/drivers/Kconfig linux-3.16.6/drivers/Kconfig +--- linux-3.16.6.orig/drivers/Kconfig 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/Kconfig 2014-10-23 12:37:18.314220004 -0500 +@@ -176,4 +176,6 @@ + + source "drivers/mcb/Kconfig" + ++source "drivers/cec/Kconfig" ++ + endmenu +diff -Nur linux-3.16.6.orig/drivers/Makefile linux-3.16.6/drivers/Makefile +--- linux-3.16.6.orig/drivers/Makefile 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/Makefile 2014-10-23 12:37:18.350220009 -0500 +@@ -158,3 +158,4 @@ + obj-$(CONFIG_FMC) += fmc/ + obj-$(CONFIG_POWERCAP) += powercap/ + obj-$(CONFIG_MCB) += mcb/ ++obj-$(CONFIG_CEC) += cec/ +diff -Nur linux-3.16.6.orig/drivers/mmc/core/core.c linux-3.16.6/drivers/mmc/core/core.c +--- linux-3.16.6.orig/drivers/mmc/core/core.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/mmc/core/core.c 2014-10-23 12:34:18.710219997 -0500 +@@ -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/consumer.h> + #include <linux/leds.h> + #include <linux/scatterlist.h> + #include <linux/log2.h> +@@ -1515,6 +1517,43 @@ + 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. +@@ -1531,6 +1570,9 @@ + 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 -Nur linux-3.16.6.orig/drivers/mmc/core/host.c linux-3.16.6/drivers/mmc/core/host.c +--- linux-3.16.6.orig/drivers/mmc/core/host.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/mmc/core/host.c 2014-10-23 12:34:34.134220000 -0500 +@@ -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> + +@@ -461,6 +465,66 @@ + + 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 +@@ -540,6 +604,10 @@ + { + 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 -Nur linux-3.16.6.orig/drivers/mmc/host/dw_mmc.c linux-3.16.6/drivers/mmc/host/dw_mmc.c +--- linux-3.16.6.orig/drivers/mmc/host/dw_mmc.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/mmc/host/dw_mmc.c 2014-10-23 12:34:26.238219996 -0500 +@@ -2049,6 +2049,8 @@ + if (!mmc) + return -ENOMEM; + ++ mmc_of_parse(mmc); ++ + slot = mmc_priv(mmc); + slot->id = id; + slot->mmc = mmc; +diff -Nur linux-3.16.6.orig/drivers/mmc/host/Kconfig linux-3.16.6/drivers/mmc/host/Kconfig +--- linux-3.16.6.orig/drivers/mmc/host/Kconfig 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/mmc/host/Kconfig 2014-10-23 12:34:04.318220041 -0500 +@@ -25,8 +25,7 @@ + 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_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_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 @@ + 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_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_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_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_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_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_DOVE + tristate "SDHCI support on Marvell's Dove SoC" +- depends on ARCH_DOVE || MACH_DOVE +- depends on MMC_SDHCI_PLTFM ++ depends on (ARCH_DOVE || MACH_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_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_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_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,8 @@ + + config MMC_SDHCI_PXAV3 + tristate "Marvell MMP2 SD Host Controller support (PXAV3)" +- depends on CLKDEV_LOOKUP +- depends on MMC_SDHCI_PLTFM ++ depends on CLKDEV_LOOKUP && HAS_DMA ++ select MMC_SDHCI_PLTFM + default CPU_MMP2 + help + This selects the Marvell(R) PXAV3 SD Host Controller. +@@ -227,8 +229,8 @@ + + config MMC_SDHCI_PXAV2 + tristate "Marvell PXA9XX SD Host Controller support (PXAV2)" +- depends on CLKDEV_LOOKUP +- depends on MMC_SDHCI_PLTFM ++ depends on CLKDEV_LOOKUP && HAS_DMA ++ select MMC_SDHCI_PLTFM + default CPU_PXA910 + help + This selects the Marvell(R) PXAV2 SD Host Controller. +@@ -239,7 +241,8 @@ + + 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 +@@ -261,8 +264,8 @@ + + config MMC_SDHCI_BCM_KONA + tristate "SDHCI support on Broadcom KONA platform" +- depends on ARCH_BCM_MOBILE +- depends on MMC_SDHCI_PLTFM ++ depends on ARCH_BCM_MOBILE && HAS_DMA ++ select MMC_SDHCI_PLTFM + help + This selects the Broadcom Kona Secure Digital Host Controller + Interface(SDHCI) support. +@@ -272,9 +275,9 @@ + + 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 -Nur linux-3.16.6.orig/drivers/mmc/host/sdhci.c linux-3.16.6/drivers/mmc/host/sdhci.c +--- linux-3.16.6.orig/drivers/mmc/host/sdhci.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/mmc/host/sdhci.c 2014-10-23 12:34:10.650220104 -0500 +@@ -1530,7 +1530,6 @@ + host->ops->set_clock(host, host->clock); + } + +- + /* Reset SD Clock Enable */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; +@@ -1763,9 +1762,6 @@ + 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) +diff -Nur linux-3.16.6.orig/drivers/regulator/anatop-regulator.c linux-3.16.6/drivers/regulator/anatop-regulator.c +--- linux-3.16.6.orig/drivers/regulator/anatop-regulator.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/regulator/anatop-regulator.c 2014-10-23 12:36:22.798219997 -0500 +@@ -267,6 +267,7 @@ + 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 -Nur linux-3.16.6.orig/drivers/regulator/core.c linux-3.16.6/drivers/regulator/core.c +--- linux-3.16.6.orig/drivers/regulator/core.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/regulator/core.c 2014-10-23 12:36:22.802220004 -0500 +@@ -24,6 +24,7 @@ + #include <linux/suspend.h> + #include <linux/delay.h> + #include <linux/gpio.h> ++#include <linux/gpio/consumer.h> + #include <linux/of.h> + #include <linux/regmap.h> + #include <linux/regulator/of_regulator.h> +@@ -77,7 +78,7 @@ + */ + struct regulator_enable_gpio { + struct list_head list; +- int gpio; ++ struct gpio_desc *gpiod; + u32 enable_count; /* a number of enabled shared GPIO */ + u32 request_count; /* a number of requested shared GPIO */ + unsigned int ena_gpio_invert:1; +@@ -1660,10 +1661,13 @@ + const struct regulator_config *config) + { + struct regulator_enable_gpio *pin; ++ struct gpio_desc *gpiod; + int ret; + ++ gpiod = gpio_to_desc(config->ena_gpio); ++ + list_for_each_entry(pin, ®ulator_ena_gpio_list, list) { +- if (pin->gpio == config->ena_gpio) { ++ if (pin->gpiod == gpiod) { + rdev_dbg(rdev, "GPIO %d is already used\n", + config->ena_gpio); + goto update_ena_gpio_to_rdev; +@@ -1682,7 +1686,7 @@ + return -ENOMEM; + } + +- pin->gpio = config->ena_gpio; ++ pin->gpiod = gpiod; + pin->ena_gpio_invert = config->ena_gpio_invert; + list_add(&pin->list, ®ulator_ena_gpio_list); + +@@ -1701,10 +1705,10 @@ + + /* Free the GPIO only in case of no use */ + list_for_each_entry_safe(pin, n, ®ulator_ena_gpio_list, list) { +- if (pin->gpio == rdev->ena_pin->gpio) { ++ if (pin->gpiod == rdev->ena_pin->gpiod) { + if (pin->request_count <= 1) { + pin->request_count = 0; +- gpio_free(pin->gpio); ++ gpiod_put(pin->gpiod); + list_del(&pin->list); + kfree(pin); + } else { +@@ -1732,8 +1736,8 @@ + if (enable) { + /* Enable GPIO at initial use */ + if (pin->enable_count == 0) +- gpio_set_value_cansleep(pin->gpio, +- !pin->ena_gpio_invert); ++ gpiod_set_value_cansleep(pin->gpiod, ++ !pin->ena_gpio_invert); + + pin->enable_count++; + } else { +@@ -1744,8 +1748,8 @@ + + /* Disable GPIO if not used */ + if (pin->enable_count <= 1) { +- gpio_set_value_cansleep(pin->gpio, +- pin->ena_gpio_invert); ++ gpiod_set_value_cansleep(pin->gpiod, ++ pin->ena_gpio_invert); + pin->enable_count = 0; + } + } +@@ -3470,7 +3474,7 @@ + + 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 -Nur linux-3.16.6.orig/drivers/regulator/dummy.c linux-3.16.6/drivers/regulator/dummy.c +--- linux-3.16.6.orig/drivers/regulator/dummy.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/regulator/dummy.c 2014-10-23 12:36:22.810220006 -0500 +@@ -48,6 +48,7 @@ + + 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 -Nur linux-3.16.6.orig/drivers/regulator/fixed.c linux-3.16.6/drivers/regulator/fixed.c +--- linux-3.16.6.orig/drivers/regulator/fixed.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/regulator/fixed.c 2014-10-23 12:36:22.810220006 -0500 +@@ -156,9 +156,7 @@ + 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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/drm-ddc-connector.c linux-3.16.6/drivers/staging/imx-drm/drm-ddc-connector.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/drm-ddc-connector.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/drm-ddc-connector.c 2014-10-23 12:37:30.178219970 -0500 +@@ -0,0 +1,88 @@ ++#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" ++ ++enum drm_connector_status ++drm_ddc_connector_always_connected(struct drm_connector *connector, bool force) ++{ ++ return connector_status_connected; ++} ++EXPORT_SYMBOL_GPL(drm_ddc_connector_always_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); ++ ++void drm_ddc_connector_destroy(struct drm_connector *connector) ++{ ++ struct drm_ddc_connector *ddc_conn = to_ddc_conn(connector); ++ ++ pr_info("%s: %p\n", __func__, ddc_conn); ++ ++ drm_sysfs_connector_remove(connector); ++ drm_connector_cleanup(connector); ++ if (ddc_conn->ddc) ++ i2c_put_adapter(ddc_conn->ddc); ++ kfree(ddc_conn); ++} ++EXPORT_SYMBOL_GPL(drm_ddc_connector_destroy); ++ ++void drm_ddc_connector_add(struct drm_device *drm, ++ struct drm_ddc_connector *ddc_conn, ++ struct drm_connector_funcs *funcs, int connector_type) ++{ ++ drm_connector_init(drm, &ddc_conn->connector, funcs, connector_type); ++} ++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 = kzalloc(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) { ++ kfree(ddc_conn); ++ 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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/drm-ddc-connector.h linux-3.16.6/drivers/staging/imx-drm/drm-ddc-connector.h +--- linux-3.16.6.orig/drivers/staging/imx-drm/drm-ddc-connector.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/drm-ddc-connector.h 2014-10-23 12:37:30.178219970 -0500 +@@ -0,0 +1,31 @@ ++#ifndef DRM_DDC_CONNECTOR_H ++#define DRM_DDC_CONNECTOR_H ++ ++#include <drm/drm_crtc.h> ++ ++struct drm_ddc_connector { ++ struct i2c_adapter *ddc; ++ struct drm_connector connector; ++ void *private; ++}; ++ ++#define to_ddc_conn(c) container_of(c, struct drm_ddc_connector, connector) ++ ++enum drm_connector_status drm_ddc_connector_always_connected( ++ struct drm_connector *connector, bool force); ++int drm_ddc_connector_get_modes(struct drm_connector *connector); ++void drm_ddc_connector_add(struct drm_device *drm, ++ struct drm_ddc_connector *ddc_conn, ++ struct drm_connector_funcs *funcs, int connector_type); ++void drm_ddc_connector_destroy(struct drm_connector *connector); ++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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-audio.c linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-audio.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-audio.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-audio.c 2014-10-23 12:37:11.394219951 -0500 +@@ -0,0 +1,654 @@ ++/* ++ * 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); ++ synchronize_irq(dw->data.irq); ++} ++ ++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); ++ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); ++ ++ 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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-audio.h linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-audio.h +--- linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-audio.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-audio.h 2014-10-23 12:36:44.258220010 -0500 +@@ -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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-cec.c linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-cec.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-cec.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-cec.c 2014-10-23 12:37:23.890220362 -0500 +@@ -0,0 +1,207 @@ ++/* 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); ++ ++ platform_set_drvdata(pdev, cec); ++ ++ /* ++ * 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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-cec.h linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-cec.h +--- linux-3.16.6.orig/drivers/staging/imx-drm/dw-hdmi-cec.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/dw-hdmi-cec.h 2014-10-23 12:37:23.890220362 -0500 +@@ -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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/imx-drm-core.c linux-3.16.6/drivers/staging/imx-drm/imx-drm-core.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/imx-drm-core.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/imx-drm-core.c 2014-10-23 12:37:37.690220197 -0500 +@@ -115,8 +115,7 @@ + helper = &imx_crtc->imx_drm_helper_funcs; + if (helper->set_interface_pix_fmt) + return helper->set_interface_pix_fmt(encoder->crtc, +- encoder->encoder_type, interface_pix_fmt, +- hsync_pin, vsync_pin); ++ interface_pix_fmt, hsync_pin, vsync_pin); + return 0; + } + EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/imx-drm.h linux-3.16.6/drivers/staging/imx-drm/imx-drm.h +--- linux-3.16.6.orig/drivers/staging/imx-drm/imx-drm.h 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/imx-drm.h 2014-10-23 12:37:37.690220197 -0500 +@@ -17,7 +17,7 @@ + struct imx_drm_crtc_helper_funcs { + int (*enable_vblank)(struct drm_crtc *crtc); + void (*disable_vblank)(struct drm_crtc *crtc); +- int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 encoder_type, ++ int (*set_interface_pix_fmt)(struct drm_crtc *crtc, + u32 pix_fmt, int hsync_pin, int vsync_pin); + const struct drm_crtc_helper_funcs *crtc_helper_funcs; + const struct drm_crtc_funcs *crtc_funcs; +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/imx-hdmi.c linux-3.16.6/drivers/staging/imx-drm/imx-hdmi.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/imx-hdmi.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/imx-hdmi.c 2014-10-23 12:37:30.178219970 -0500 +@@ -29,6 +29,9 @@ + #include <drm/drm_encoder_slave.h> + #include <video/imx-ipu-v3.h> + ++#include "drm-ddc-connector.h" ++#include "dw-hdmi-audio.h" ++#include "dw-hdmi-cec.h" + #include "imx-hdmi.h" + #include "imx-drm.h" + +@@ -112,9 +115,11 @@ + }; + + 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; +@@ -124,13 +129,13 @@ + 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; +@@ -361,6 +366,12 @@ + 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 +@@ -1144,8 +1155,6 @@ + /* 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); +@@ -1157,23 +1166,28 @@ + 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 */ +@@ -1376,43 +1390,16 @@ + 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); ++ struct imx_hdmi *hdmi = drm_ddc_private(connector); + + return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? + connector_status_connected : connector_status_disconnected; + } + +-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"); +- } +- +- return 0; +-} +- + 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; + } +@@ -1485,11 +1472,11 @@ + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = imx_hdmi_connector_detect, +- .destroy = imx_drm_connector_destroy, ++ .destroy = drm_ddc_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, + .best_encoder = imx_hdmi_connector_best_encoder, + }; + +@@ -1530,7 +1517,7 @@ + + 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); +@@ -1548,24 +1535,43 @@ + 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); ++ drm_ddc_connector_add(drm, hdmi->ddc_conn, &imx_hdmi_connector_funcs, ++ DRM_MODE_CONNECTOR_HDMIA); + +- hdmi->connector.encoder = &hdmi->encoder; +- +- 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", +@@ -1587,11 +1593,13 @@ + 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; +@@ -1600,9 +1608,14 @@ + 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->dev = dev; + hdmi->sample_rate = 48000; + hdmi->ratio = 100; ++ hdmi->mc_clkdis = 0x7f; + + if (of_id) { + const struct platform_device_id *device_id = of_id->data; +@@ -1610,17 +1623,6 @@ + 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 irq; +@@ -1706,6 +1708,35 @@ + /* 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; +@@ -1723,15 +1754,18 @@ + { + 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->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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/imx-ldb.c linux-3.16.6/drivers/staging/imx-drm/imx-ldb.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/imx-ldb.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/imx-ldb.c 2014-10-23 12:35:16.922220006 -0500 +@@ -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 *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 @@ + 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 @@ + } + + 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 @@ + (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]) +@@ -378,6 +391,9 @@ + 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); + +@@ -492,6 +508,7 @@ + + 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) +@@ -555,6 +572,10 @@ + 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; +@@ -574,9 +595,6 @@ + for (i = 0; i < 2; i++) { + struct imx_ldb_channel *channel = &imx_ldb->channel[i]; + +- if (!channel->connector.funcs) +- continue; +- + channel->connector.funcs->destroy(&channel->connector); + channel->encoder.funcs->destroy(&channel->encoder); + } +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/imx-ldb.c.orig linux-3.16.6/drivers/staging/imx-drm/imx-ldb.c.orig +--- linux-3.16.6.orig/drivers/staging/imx-drm/imx-ldb.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/imx-ldb.c.orig 2014-10-15 05:05:43.000000000 -0500 +@@ -0,0 +1,616 @@ ++/* ++ * i.MX drm driver - LVDS display bridge ++ * ++ * Copyright (C) 2012 Sascha Hauer, Pengutronix ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++ * MA 02110-1301, USA. ++ */ ++ ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/component.h> ++#include <drm/drmP.h> ++#include <drm/drm_fb_helper.h> ++#include <drm/drm_crtc_helper.h> ++#include <linux/mfd/syscon.h> ++#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> ++#include <linux/of_address.h> ++#include <linux/of_device.h> ++#include <video/of_videomode.h> ++#include <linux/regmap.h> ++#include <linux/videodev2.h> ++ ++#include "imx-drm.h" ++ ++#define DRIVER_NAME "imx-ldb" ++ ++#define LDB_CH0_MODE_EN_TO_DI0 (1 << 0) ++#define LDB_CH0_MODE_EN_TO_DI1 (3 << 0) ++#define LDB_CH0_MODE_EN_MASK (3 << 0) ++#define LDB_CH1_MODE_EN_TO_DI0 (1 << 2) ++#define LDB_CH1_MODE_EN_TO_DI1 (3 << 2) ++#define LDB_CH1_MODE_EN_MASK (3 << 2) ++#define LDB_SPLIT_MODE_EN (1 << 4) ++#define LDB_DATA_WIDTH_CH0_24 (1 << 5) ++#define LDB_BIT_MAP_CH0_JEIDA (1 << 6) ++#define LDB_DATA_WIDTH_CH1_24 (1 << 7) ++#define LDB_BIT_MAP_CH1_JEIDA (1 << 8) ++#define LDB_DI0_VS_POL_ACT_LOW (1 << 9) ++#define LDB_DI1_VS_POL_ACT_LOW (1 << 10) ++#define LDB_BGREF_RMODE_INT (1 << 15) ++ ++#define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector) ++#define enc_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, encoder) ++ ++struct imx_ldb; ++ ++struct imx_ldb_channel { ++ struct imx_ldb *ldb; ++ struct drm_connector connector; ++ struct drm_encoder encoder; ++ struct device_node *child; ++ int chno; ++ void *edid; ++ int edid_len; ++ struct drm_display_mode mode; ++ int mode_valid; ++}; ++ ++struct bus_mux { ++ int reg; ++ int shift; ++ int mask; ++}; ++ ++struct imx_ldb { ++ struct regmap *regmap; ++ struct device *dev; ++ struct imx_ldb_channel channel[2]; ++ struct clk *clk[2]; /* our own clock */ ++ struct clk *clk_sel[4]; /* parent of display clock */ ++ struct clk *clk_pll[2]; /* upstream clock we can adjust */ ++ u32 ldb_ctrl; ++ const struct bus_mux *lvds_mux; ++}; ++ ++static enum drm_connector_status imx_ldb_connector_detect( ++ struct drm_connector *connector, bool force) ++{ ++ return connector_status_connected; ++} ++ ++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->edid) { ++ drm_mode_connector_update_edid_property(connector, ++ imx_ldb_ch->edid); ++ num_modes = drm_add_edid_modes(connector, imx_ldb_ch->edid); ++ } ++ ++ if (imx_ldb_ch->mode_valid) { ++ struct drm_display_mode *mode; ++ ++ mode = drm_mode_create(connector->dev); ++ if (!mode) ++ return -EINVAL; ++ drm_mode_copy(mode, &imx_ldb_ch->mode); ++ mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; ++ drm_mode_probed_add(connector, mode); ++ num_modes++; ++ } ++ ++ return num_modes; ++} ++ ++static struct drm_encoder *imx_ldb_connector_best_encoder( ++ struct drm_connector *connector) ++{ ++ struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); ++ ++ return &imx_ldb_ch->encoder; ++} ++ ++static void imx_ldb_encoder_dpms(struct drm_encoder *encoder, int mode) ++{ ++} ++ ++static bool imx_ldb_encoder_mode_fixup(struct drm_encoder *encoder, ++ const struct drm_display_mode *mode, ++ struct drm_display_mode *adjusted_mode) ++{ ++ return true; ++} ++ ++static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno, ++ unsigned long serial_clk, unsigned long di_clk) ++{ ++ int ret; ++ ++ dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__, ++ clk_get_rate(ldb->clk_pll[chno]), serial_clk); ++ clk_set_rate(ldb->clk_pll[chno], serial_clk); ++ ++ dev_dbg(ldb->dev, "%s after: %ld\n", __func__, ++ clk_get_rate(ldb->clk_pll[chno])); ++ ++ dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__, ++ clk_get_rate(ldb->clk[chno]), ++ (long int)di_clk); ++ clk_set_rate(ldb->clk[chno], di_clk); ++ ++ dev_dbg(ldb->dev, "%s after: %ld\n", __func__, ++ clk_get_rate(ldb->clk[chno])); ++ ++ /* set display clock mux to LDB input clock */ ++ ret = clk_set_parent(ldb->clk_sel[mux], ldb->clk[chno]); ++ if (ret) ++ dev_err(ldb->dev, ++ "unable to set di%d parent clock to ldb_di%d\n", mux, ++ chno); ++} ++ ++static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) ++{ ++ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); ++ struct imx_ldb *ldb = imx_ldb_ch->ldb; ++ struct drm_display_mode *mode = &encoder->crtc->mode; ++ u32 pixel_fmt; ++ unsigned long serial_clk; ++ unsigned long di_clk = mode->clock * 1000; ++ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); ++ ++ if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { ++ /* dual channel LVDS mode */ ++ serial_clk = 3500UL * mode->clock; ++ imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk); ++ imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk); ++ } else { ++ serial_clk = 7000UL * mode->clock; ++ imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, ++ di_clk); ++ } ++ ++ switch (imx_ldb_ch->chno) { ++ case 0: ++ pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH0_24) ? ++ V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666; ++ break; ++ case 1: ++ pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH1_24) ? ++ V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666; ++ break; ++ default: ++ dev_err(ldb->dev, "unable to config di%d panel format\n", ++ imx_ldb_ch->chno); ++ pixel_fmt = V4L2_PIX_FMT_RGB24; ++ } ++ ++ imx_drm_panel_format(encoder, pixel_fmt); ++} ++ ++static void imx_ldb_encoder_commit(struct drm_encoder *encoder) ++{ ++ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); ++ struct imx_ldb *ldb = imx_ldb_ch->ldb; ++ int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; ++ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); ++ ++ if (dual) { ++ clk_prepare_enable(ldb->clk[0]); ++ clk_prepare_enable(ldb->clk[1]); ++ } ++ ++ if (imx_ldb_ch == &ldb->channel[0] || dual) { ++ ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; ++ if (mux == 0 || ldb->lvds_mux) ++ ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0; ++ else if (mux == 1) ++ ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI1; ++ } ++ if (imx_ldb_ch == &ldb->channel[1] || dual) { ++ ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; ++ if (mux == 1 || ldb->lvds_mux) ++ ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI1; ++ else if (mux == 0) ++ ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0; ++ } ++ ++ if (ldb->lvds_mux) { ++ const struct bus_mux *lvds_mux = NULL; ++ ++ if (imx_ldb_ch == &ldb->channel[0]) ++ lvds_mux = &ldb->lvds_mux[0]; ++ else if (imx_ldb_ch == &ldb->channel[1]) ++ lvds_mux = &ldb->lvds_mux[1]; ++ ++ regmap_update_bits(ldb->regmap, lvds_mux->reg, lvds_mux->mask, ++ mux << lvds_mux->shift); ++ } ++ ++ regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); ++} ++ ++static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, ++ struct drm_display_mode *mode, ++ struct drm_display_mode *adjusted_mode) ++{ ++ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); ++ struct imx_ldb *ldb = imx_ldb_ch->ldb; ++ int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; ++ ++ if (mode->clock > 170000) { ++ dev_warn(ldb->dev, ++ "%s: mode exceeds 170 MHz pixel clock\n", __func__); ++ } ++ if (mode->clock > 85000 && !dual) { ++ dev_warn(ldb->dev, ++ "%s: mode exceeds 85 MHz pixel clock\n", __func__); ++ } ++ ++ /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */ ++ if (imx_ldb_ch == &ldb->channel[0]) { ++ if (mode->flags & DRM_MODE_FLAG_NVSYNC) ++ ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW; ++ else if (mode->flags & DRM_MODE_FLAG_PVSYNC) ++ ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW; ++ } ++ if (imx_ldb_ch == &ldb->channel[1]) { ++ if (mode->flags & DRM_MODE_FLAG_NVSYNC) ++ ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW; ++ else if (mode->flags & DRM_MODE_FLAG_PVSYNC) ++ ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW; ++ } ++} ++ ++static void imx_ldb_encoder_disable(struct drm_encoder *encoder) ++{ ++ struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); ++ struct imx_ldb *ldb = imx_ldb_ch->ldb; ++ ++ /* ++ * imx_ldb_encoder_disable is called by ++ * drm_helper_disable_unused_functions without ++ * the encoder being enabled before. ++ */ ++ if (imx_ldb_ch == &ldb->channel[0] && ++ (ldb->ldb_ctrl & LDB_CH0_MODE_EN_MASK) == 0) ++ return; ++ else if (imx_ldb_ch == &ldb->channel[1] && ++ (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0) ++ return; ++ ++ if (imx_ldb_ch == &ldb->channel[0]) ++ ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; ++ else if (imx_ldb_ch == &ldb->channel[1]) ++ ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; ++ ++ regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); ++ ++ if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { ++ clk_disable_unprepare(ldb->clk[0]); ++ clk_disable_unprepare(ldb->clk[1]); ++ } ++} ++ ++static struct drm_connector_funcs imx_ldb_connector_funcs = { ++ .dpms = drm_helper_connector_dpms, ++ .fill_modes = drm_helper_probe_single_connector_modes, ++ .detect = imx_ldb_connector_detect, ++ .destroy = imx_drm_connector_destroy, ++}; ++ ++static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { ++ .get_modes = imx_ldb_connector_get_modes, ++ .best_encoder = imx_ldb_connector_best_encoder, ++}; ++ ++static struct drm_encoder_funcs imx_ldb_encoder_funcs = { ++ .destroy = imx_drm_encoder_destroy, ++}; ++ ++static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { ++ .dpms = imx_ldb_encoder_dpms, ++ .mode_fixup = imx_ldb_encoder_mode_fixup, ++ .prepare = imx_ldb_encoder_prepare, ++ .commit = imx_ldb_encoder_commit, ++ .mode_set = imx_ldb_encoder_mode_set, ++ .disable = imx_ldb_encoder_disable, ++}; ++ ++static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno) ++{ ++ char clkname[16]; ++ ++ snprintf(clkname, sizeof(clkname), "di%d", chno); ++ ldb->clk[chno] = devm_clk_get(ldb->dev, clkname); ++ if (IS_ERR(ldb->clk[chno])) ++ return PTR_ERR(ldb->clk[chno]); ++ ++ snprintf(clkname, sizeof(clkname), "di%d_pll", chno); ++ ldb->clk_pll[chno] = devm_clk_get(ldb->dev, clkname); ++ ++ return PTR_ERR_OR_ZERO(ldb->clk_pll[chno]); ++} ++ ++static int imx_ldb_register(struct drm_device *drm, ++ struct imx_ldb_channel *imx_ldb_ch) ++{ ++ struct imx_ldb *ldb = imx_ldb_ch->ldb; ++ int ret; ++ ++ ret = imx_drm_encoder_parse_of(drm, &imx_ldb_ch->encoder, ++ imx_ldb_ch->child); ++ if (ret) ++ return ret; ++ ++ ret = imx_ldb_get_clk(ldb, imx_ldb_ch->chno); ++ if (ret) ++ return ret; ++ ++ if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { ++ ret = imx_ldb_get_clk(ldb, 1); ++ if (ret) ++ return ret; ++ } ++ ++ drm_encoder_helper_add(&imx_ldb_ch->encoder, ++ &imx_ldb_encoder_helper_funcs); ++ drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs, ++ DRM_MODE_ENCODER_LVDS); ++ ++ drm_connector_helper_add(&imx_ldb_ch->connector, ++ &imx_ldb_connector_helper_funcs); ++ drm_connector_init(drm, &imx_ldb_ch->connector, ++ &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS); ++ ++ drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, ++ &imx_ldb_ch->encoder); ++ ++ return 0; ++} ++ ++enum { ++ LVDS_BIT_MAP_SPWG, ++ LVDS_BIT_MAP_JEIDA ++}; ++ ++static const char * const imx_ldb_bit_mappings[] = { ++ [LVDS_BIT_MAP_SPWG] = "spwg", ++ [LVDS_BIT_MAP_JEIDA] = "jeida", ++}; ++ ++static const int of_get_data_mapping(struct device_node *np) ++{ ++ const char *bm; ++ int ret, i; ++ ++ ret = of_property_read_string(np, "fsl,data-mapping", &bm); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < ARRAY_SIZE(imx_ldb_bit_mappings); i++) ++ if (!strcasecmp(bm, imx_ldb_bit_mappings[i])) ++ return i; ++ ++ return -EINVAL; ++} ++ ++static struct bus_mux imx6q_lvds_mux[2] = { ++ { ++ .reg = IOMUXC_GPR3, ++ .shift = 6, ++ .mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK, ++ }, { ++ .reg = IOMUXC_GPR3, ++ .shift = 8, ++ .mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK, ++ } ++}; ++ ++/* ++ * For a device declaring compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb", ++ * of_match_device will walk through this list and take the first entry ++ * matching any of its compatible values. Therefore, the more generic ++ * entries (in this case fsl,imx53-ldb) need to be ordered last. ++ */ ++static const struct of_device_id imx_ldb_dt_ids[] = { ++ { .compatible = "fsl,imx6q-ldb", .data = imx6q_lvds_mux, }, ++ { .compatible = "fsl,imx53-ldb", .data = NULL, }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids); ++ ++static int imx_ldb_bind(struct device *dev, struct device *master, void *data) ++{ ++ struct drm_device *drm = data; ++ struct device_node *np = dev->of_node; ++ const struct of_device_id *of_id = ++ of_match_device(imx_ldb_dt_ids, dev); ++ struct device_node *child; ++ const u8 *edidp; ++ struct imx_ldb *imx_ldb; ++ int datawidth; ++ int mapping; ++ int dual; ++ int ret; ++ int i; ++ ++ imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL); ++ if (!imx_ldb) ++ return -ENOMEM; ++ ++ imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); ++ if (IS_ERR(imx_ldb->regmap)) { ++ dev_err(dev, "failed to get parent regmap\n"); ++ return PTR_ERR(imx_ldb->regmap); ++ } ++ ++ imx_ldb->dev = dev; ++ ++ if (of_id) ++ imx_ldb->lvds_mux = of_id->data; ++ ++ dual = of_property_read_bool(np, "fsl,dual-channel"); ++ if (dual) ++ imx_ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN; ++ ++ /* ++ * There are three different possible clock mux configurations: ++ * i.MX53: ipu1_di0_sel, ipu1_di1_sel ++ * i.MX6q: ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, ipu2_di1_sel ++ * i.MX6dl: ipu1_di0_sel, ipu1_di1_sel, lcdif_sel ++ * Map them all to di0_sel...di3_sel. ++ */ ++ for (i = 0; i < 4; i++) { ++ char clkname[16]; ++ ++ sprintf(clkname, "di%d_sel", i); ++ imx_ldb->clk_sel[i] = devm_clk_get(imx_ldb->dev, clkname); ++ if (IS_ERR(imx_ldb->clk_sel[i])) { ++ ret = PTR_ERR(imx_ldb->clk_sel[i]); ++ imx_ldb->clk_sel[i] = NULL; ++ break; ++ } ++ } ++ if (i == 0) ++ return ret; ++ ++ for_each_child_of_node(np, child) { ++ struct imx_ldb_channel *channel; ++ ++ ret = of_property_read_u32(child, "reg", &i); ++ if (ret || i < 0 || i > 1) ++ return -EINVAL; ++ ++ if (dual && i > 0) { ++ dev_warn(dev, "dual-channel mode, ignoring second output\n"); ++ continue; ++ } ++ ++ if (!of_device_is_available(child)) ++ continue; ++ ++ channel = &imx_ldb->channel[i]; ++ channel->ldb = imx_ldb; ++ channel->chno = i; ++ channel->child = child; ++ ++ edidp = of_get_property(child, "edid", &channel->edid_len); ++ if (edidp) { ++ channel->edid = kmemdup(edidp, channel->edid_len, ++ GFP_KERNEL); ++ } else { ++ ret = of_get_drm_display_mode(child, &channel->mode, 0); ++ if (!ret) ++ channel->mode_valid = 1; ++ } ++ ++ ret = of_property_read_u32(child, "fsl,data-width", &datawidth); ++ if (ret) ++ datawidth = 0; ++ else if (datawidth != 18 && datawidth != 24) ++ return -EINVAL; ++ ++ mapping = of_get_data_mapping(child); ++ switch (mapping) { ++ case LVDS_BIT_MAP_SPWG: ++ if (datawidth == 24) { ++ if (i == 0 || dual) ++ imx_ldb->ldb_ctrl |= ++ LDB_DATA_WIDTH_CH0_24; ++ if (i == 1 || dual) ++ imx_ldb->ldb_ctrl |= ++ LDB_DATA_WIDTH_CH1_24; ++ } ++ break; ++ case LVDS_BIT_MAP_JEIDA: ++ if (datawidth == 18) { ++ dev_err(dev, "JEIDA standard only supported in 24 bit\n"); ++ return -EINVAL; ++ } ++ if (i == 0 || dual) ++ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | ++ LDB_BIT_MAP_CH0_JEIDA; ++ if (i == 1 || dual) ++ imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | ++ LDB_BIT_MAP_CH1_JEIDA; ++ break; ++ default: ++ dev_err(dev, "data mapping not specified or invalid\n"); ++ return -EINVAL; ++ } ++ ++ ret = imx_ldb_register(drm, channel); ++ if (ret) ++ return ret; ++ } ++ ++ dev_set_drvdata(dev, imx_ldb); ++ ++ return 0; ++} ++ ++static void imx_ldb_unbind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct imx_ldb *imx_ldb = dev_get_drvdata(dev); ++ int i; ++ ++ for (i = 0; i < 2; i++) { ++ struct imx_ldb_channel *channel = &imx_ldb->channel[i]; ++ ++ if (!channel->connector.funcs) ++ continue; ++ ++ channel->connector.funcs->destroy(&channel->connector); ++ channel->encoder.funcs->destroy(&channel->encoder); ++ } ++} ++ ++static const struct component_ops imx_ldb_ops = { ++ .bind = imx_ldb_bind, ++ .unbind = imx_ldb_unbind, ++}; ++ ++static int imx_ldb_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &imx_ldb_ops); ++} ++ ++static int imx_ldb_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &imx_ldb_ops); ++ return 0; ++} ++ ++static struct platform_driver imx_ldb_driver = { ++ .probe = imx_ldb_probe, ++ .remove = imx_ldb_remove, ++ .driver = { ++ .of_match_table = imx_ldb_dt_ids, ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(imx_ldb_driver); ++ ++MODULE_DESCRIPTION("i.MX LVDS driver"); ++MODULE_AUTHOR("Sascha Hauer, Pengutronix"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:" DRIVER_NAME); +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/imx-tve.c linux-3.16.6/drivers/staging/imx-drm/imx-tve.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/imx-tve.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/imx-tve.c 2014-10-23 12:37:30.182220005 -0500 +@@ -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> +@@ -32,6 +31,7 @@ + #include <drm/drm_crtc_helper.h> + #include <video/imx-ipu-v3.h> + ++#include "drm-ddc-connector.h" + #include "imx-drm.h" + + #define TVE_COM_CONF_REG 0x00 +@@ -111,7 +111,7 @@ + }; + + 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 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 @@ + 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; + + /* pixel clock with 2x oversampling */ +@@ -269,7 +243,7 @@ + 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; + } +@@ -360,12 +334,12 @@ + 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, ++ .detect = drm_ddc_connector_always_connected, ++ .destroy = drm_ddc_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, + }; +@@ -508,12 +482,13 @@ + 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, &imx_tve_connector_funcs, ++ 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; + } +@@ -562,7 +537,6 @@ + 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; +@@ -574,15 +548,13 @@ + 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"); +@@ -689,7 +661,6 @@ + { + struct imx_tve *tve = dev_get_drvdata(dev); + +- tve->connector.funcs->destroy(&tve->connector); + tve->encoder.funcs->destroy(&tve->encoder); + + if (!IS_ERR(tve->dac_reg)) +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/ipuv3-crtc.c linux-3.16.6/drivers/staging/imx-drm/ipuv3-crtc.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/ipuv3-crtc.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/ipuv3-crtc.c 2014-10-23 12:37:37.690220197 -0500 +@@ -51,7 +51,6 @@ + struct drm_framebuffer *newfb; + int irq; + u32 interface_pix_fmt; +- unsigned long di_clkflags; + int di_hsync_pin; + int di_vsync_pin; + }; +@@ -146,10 +145,13 @@ + int x, int y, + struct drm_framebuffer *old_fb) + { ++ struct drm_device *dev = crtc->dev; ++ struct drm_encoder *encoder; + struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); +- int ret; + struct ipu_di_signal_cfg sig_cfg = {}; ++ unsigned long encoder_types = 0; + u32 out_pixel_fmt; ++ int ret; + + dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, + mode->hdisplay); +@@ -165,8 +167,26 @@ + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + sig_cfg.Vsync_pol = 1; + +- sig_cfg.enable_pol = 1; +- sig_cfg.clk_pol = 0; ++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) ++ if (encoder->crtc == crtc) ++ encoder_types |= BIT(encoder->encoder_type); ++ ++ dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n", ++ __func__, encoder_types); ++ ++ /* ++ * If we have DAC, TVDAC or LDB, then we need the IPU DI clock ++ * to be the same as the LDB DI clock. ++ */ ++ if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) | ++ BIT(DRM_MODE_ENCODER_TVDAC) | ++ BIT(DRM_MODE_ENCODER_LVDS))) ++ sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT; ++ else ++ sig_cfg.clkflags = 0; ++ ++ sig_cfg.enable_pol = ENABLE_POL_HIGH; ++ sig_cfg.clk_pol = CLK_POL_NEGEDGE; + sig_cfg.width = mode->hdisplay; + sig_cfg.height = mode->vdisplay; + sig_cfg.pixel_fmt = out_pixel_fmt; +@@ -178,7 +198,6 @@ + sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start; + sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay; + sig_cfg.pixelclock = mode->clock * 1000; +- sig_cfg.clkflags = ipu_crtc->di_clkflags; + + sig_cfg.v_to_h_sync = 0; + +@@ -277,7 +296,7 @@ + ipu_crtc->newfb = NULL; + } + +-static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, ++static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, + u32 pixfmt, int hsync_pin, int vsync_pin) + { + struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); +@@ -286,19 +305,6 @@ + ipu_crtc->di_hsync_pin = hsync_pin; + ipu_crtc->di_vsync_pin = vsync_pin; + +- switch (encoder_type) { +- case DRM_MODE_ENCODER_DAC: +- case DRM_MODE_ENCODER_TVDAC: +- case DRM_MODE_ENCODER_LVDS: +- ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | +- IPU_DI_CLKMODE_EXT; +- break; +- case DRM_MODE_ENCODER_TMDS: +- case DRM_MODE_ENCODER_NONE: +- ipu_crtc->di_clkflags = 0; +- break; +- } +- + return 0; + } + +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/ipuv3-plane.c linux-3.16.6/drivers/staging/imx-drm/ipuv3-plane.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/ipuv3-plane.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/ipuv3-plane.c 2014-10-23 12:35:00.842220709 -0500 +@@ -281,8 +281,7 @@ + + ipu_idmac_put(ipu_plane->ipu_ch); + ipu_dmfc_put(ipu_plane->dmfc); +- if (ipu_plane->dp) +- ipu_dp_put(ipu_plane->dp); ++ ipu_dp_put(ipu_plane->dp); + } + } + +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/ipuv3-plane.c.orig linux-3.16.6/drivers/staging/imx-drm/ipuv3-plane.c.orig +--- linux-3.16.6.orig/drivers/staging/imx-drm/ipuv3-plane.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/ipuv3-plane.c.orig 2014-10-15 05:05:43.000000000 -0500 +@@ -0,0 +1,388 @@ ++/* ++ * i.MX IPUv3 DP Overlay Planes ++ * ++ * Copyright (C) 2013 Philipp Zabel, Pengutronix ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <drm/drmP.h> ++#include <drm/drm_fb_cma_helper.h> ++#include <drm/drm_gem_cma_helper.h> ++ ++#include "video/imx-ipu-v3.h" ++#include "ipuv3-plane.h" ++ ++#define to_ipu_plane(x) container_of(x, struct ipu_plane, base) ++ ++static const uint32_t ipu_plane_formats[] = { ++ DRM_FORMAT_XRGB1555, ++ DRM_FORMAT_XBGR1555, ++ DRM_FORMAT_ARGB8888, ++ DRM_FORMAT_XRGB8888, ++ DRM_FORMAT_ABGR8888, ++ DRM_FORMAT_XBGR8888, ++ DRM_FORMAT_YUYV, ++ DRM_FORMAT_YVYU, ++ DRM_FORMAT_YUV420, ++ DRM_FORMAT_YVU420, ++}; ++ ++int ipu_plane_irq(struct ipu_plane *ipu_plane) ++{ ++ return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch, ++ IPU_IRQ_EOF); ++} ++ ++static int calc_vref(struct drm_display_mode *mode) ++{ ++ unsigned long htotal, vtotal; ++ ++ htotal = mode->htotal; ++ vtotal = mode->vtotal; ++ ++ if (!htotal || !vtotal) ++ return 60; ++ ++ return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal); ++} ++ ++static inline int calc_bandwidth(int width, int height, unsigned int vref) ++{ ++ return width * height * vref; ++} ++ ++int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb, ++ int x, int y) ++{ ++ struct ipu_ch_param __iomem *cpmem; ++ struct drm_gem_cma_object *cma_obj; ++ unsigned long eba; ++ ++ cma_obj = drm_fb_cma_get_gem_obj(fb, 0); ++ if (!cma_obj) { ++ DRM_DEBUG_KMS("entry is null.\n"); ++ return -EFAULT; ++ } ++ ++ dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d", ++ &cma_obj->paddr, x, y); ++ ++ cpmem = ipu_get_cpmem(ipu_plane->ipu_ch); ++ ipu_cpmem_set_stride(cpmem, fb->pitches[0]); ++ ++ eba = cma_obj->paddr + fb->offsets[0] + ++ fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x; ++ ipu_cpmem_set_buffer(cpmem, 0, eba); ++ ipu_cpmem_set_buffer(cpmem, 1, eba); ++ ++ /* cache offsets for subsequent pageflips */ ++ ipu_plane->x = x; ++ ipu_plane->y = y; ++ ++ return 0; ++} ++ ++int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc, ++ struct drm_display_mode *mode, ++ struct drm_framebuffer *fb, int crtc_x, int crtc_y, ++ unsigned int crtc_w, unsigned int crtc_h, ++ uint32_t src_x, uint32_t src_y, ++ uint32_t src_w, uint32_t src_h) ++{ ++ struct ipu_ch_param __iomem *cpmem; ++ struct device *dev = ipu_plane->base.dev->dev; ++ int ret; ++ ++ /* no scaling */ ++ if (src_w != crtc_w || src_h != crtc_h) ++ return -EINVAL; ++ ++ /* clip to crtc bounds */ ++ if (crtc_x < 0) { ++ if (-crtc_x > crtc_w) ++ return -EINVAL; ++ src_x += -crtc_x; ++ src_w -= -crtc_x; ++ crtc_w -= -crtc_x; ++ crtc_x = 0; ++ } ++ if (crtc_y < 0) { ++ if (-crtc_y > crtc_h) ++ return -EINVAL; ++ src_y += -crtc_y; ++ src_h -= -crtc_y; ++ crtc_h -= -crtc_y; ++ crtc_y = 0; ++ } ++ if (crtc_x + crtc_w > mode->hdisplay) { ++ if (crtc_x > mode->hdisplay) ++ return -EINVAL; ++ crtc_w = mode->hdisplay - crtc_x; ++ src_w = crtc_w; ++ } ++ if (crtc_y + crtc_h > mode->vdisplay) { ++ if (crtc_y > mode->vdisplay) ++ return -EINVAL; ++ crtc_h = mode->vdisplay - crtc_y; ++ src_h = crtc_h; ++ } ++ /* full plane minimum width is 13 pixels */ ++ if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG)) ++ return -EINVAL; ++ if (crtc_h < 2) ++ return -EINVAL; ++ ++ switch (ipu_plane->dp_flow) { ++ case IPU_DP_FLOW_SYNC_BG: ++ ret = ipu_dp_setup_channel(ipu_plane->dp, ++ IPUV3_COLORSPACE_RGB, ++ IPUV3_COLORSPACE_RGB); ++ if (ret) { ++ dev_err(dev, ++ "initializing display processor failed with %d\n", ++ ret); ++ return ret; ++ } ++ ipu_dp_set_global_alpha(ipu_plane->dp, 1, 0, 1); ++ break; ++ case IPU_DP_FLOW_SYNC_FG: ++ ipu_dp_setup_channel(ipu_plane->dp, ++ ipu_drm_fourcc_to_colorspace(fb->pixel_format), ++ IPUV3_COLORSPACE_UNKNOWN); ++ ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y); ++ break; ++ } ++ ++ ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w); ++ if (ret) { ++ dev_err(dev, "initializing dmfc channel failed with %d\n", ret); ++ return ret; ++ } ++ ++ ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc, ++ calc_bandwidth(crtc_w, crtc_h, ++ calc_vref(mode)), 64); ++ if (ret) { ++ dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret); ++ return ret; ++ } ++ ++ cpmem = ipu_get_cpmem(ipu_plane->ipu_ch); ++ ipu_ch_param_zero(cpmem); ++ ipu_cpmem_set_resolution(cpmem, src_w, src_h); ++ ret = ipu_cpmem_set_fmt(cpmem, fb->pixel_format); ++ if (ret < 0) { ++ dev_err(dev, "unsupported pixel format 0x%08x\n", ++ fb->pixel_format); ++ return ret; ++ } ++ ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); ++ ++ ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++void ipu_plane_put_resources(struct ipu_plane *ipu_plane) ++{ ++ if (!IS_ERR_OR_NULL(ipu_plane->dp)) ++ ipu_dp_put(ipu_plane->dp); ++ if (!IS_ERR_OR_NULL(ipu_plane->dmfc)) ++ ipu_dmfc_put(ipu_plane->dmfc); ++ if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch)) ++ ipu_idmac_put(ipu_plane->ipu_ch); ++} ++ ++int ipu_plane_get_resources(struct ipu_plane *ipu_plane) ++{ ++ int ret; ++ ++ ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma); ++ if (IS_ERR(ipu_plane->ipu_ch)) { ++ ret = PTR_ERR(ipu_plane->ipu_ch); ++ DRM_ERROR("failed to get idmac channel: %d\n", ret); ++ return ret; ++ } ++ ++ ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma); ++ if (IS_ERR(ipu_plane->dmfc)) { ++ ret = PTR_ERR(ipu_plane->dmfc); ++ DRM_ERROR("failed to get dmfc: ret %d\n", ret); ++ goto err_out; ++ } ++ ++ if (ipu_plane->dp_flow >= 0) { ++ ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow); ++ if (IS_ERR(ipu_plane->dp)) { ++ ret = PTR_ERR(ipu_plane->dp); ++ DRM_ERROR("failed to get dp flow: %d\n", ret); ++ goto err_out; ++ } ++ } ++ ++ return 0; ++err_out: ++ ipu_plane_put_resources(ipu_plane); ++ ++ return ret; ++} ++ ++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) ++ ipu_dp_enable_channel(ipu_plane->dp); ++ ++ ipu_plane->enabled = true; ++} ++ ++void ipu_plane_disable(struct ipu_plane *ipu_plane) ++{ ++ ipu_plane->enabled = false; ++ ++ ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); ++ ++ if (ipu_plane->dp) ++ 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) ++{ ++ bool enable; ++ ++ DRM_DEBUG_KMS("mode = %d", mode); ++ ++ enable = (mode == DRM_MODE_DPMS_ON); ++ ++ if (enable == ipu_plane->enabled) ++ return; ++ ++ if (enable) { ++ ipu_plane_enable(ipu_plane); ++ } else { ++ ipu_plane_disable(ipu_plane); ++ ++ ipu_idmac_put(ipu_plane->ipu_ch); ++ ipu_dmfc_put(ipu_plane->dmfc); ++ if (ipu_plane->dp) ++ ipu_dp_put(ipu_plane->dp); ++ } ++} ++ ++/* ++ * drm_plane API ++ */ ++ ++static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, ++ struct drm_framebuffer *fb, int crtc_x, int crtc_y, ++ unsigned int crtc_w, unsigned int crtc_h, ++ uint32_t src_x, uint32_t src_y, ++ uint32_t src_w, uint32_t src_h) ++{ ++ struct ipu_plane *ipu_plane = to_ipu_plane(plane); ++ int ret = 0; ++ ++ DRM_DEBUG_KMS("plane - %p\n", plane); ++ ++ if (!ipu_plane->enabled) ++ ret = ipu_plane_get_resources(ipu_plane); ++ if (ret < 0) ++ return ret; ++ ++ ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb, ++ crtc_x, crtc_y, crtc_w, crtc_h, ++ src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16); ++ if (ret < 0) { ++ ipu_plane_put_resources(ipu_plane); ++ return ret; ++ } ++ ++ if (crtc != plane->crtc) ++ dev_info(plane->dev->dev, "crtc change: %p -> %p\n", ++ plane->crtc, crtc); ++ plane->crtc = crtc; ++ ++ ipu_plane_dpms(ipu_plane, DRM_MODE_DPMS_ON); ++ ++ return 0; ++} ++ ++static int ipu_disable_plane(struct drm_plane *plane) ++{ ++ struct ipu_plane *ipu_plane = to_ipu_plane(plane); ++ ++ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); ++ ++ ipu_plane_dpms(ipu_plane, DRM_MODE_DPMS_OFF); ++ ++ ipu_plane_put_resources(ipu_plane); ++ ++ return 0; ++} ++ ++static void ipu_plane_destroy(struct drm_plane *plane) ++{ ++ struct ipu_plane *ipu_plane = to_ipu_plane(plane); ++ ++ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); ++ ++ ipu_disable_plane(plane); ++ drm_plane_cleanup(plane); ++ kfree(ipu_plane); ++} ++ ++static struct drm_plane_funcs ipu_plane_funcs = { ++ .update_plane = ipu_update_plane, ++ .disable_plane = ipu_disable_plane, ++ .destroy = ipu_plane_destroy, ++}; ++ ++struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, ++ int dma, int dp, unsigned int possible_crtcs, ++ bool priv) ++{ ++ struct ipu_plane *ipu_plane; ++ int ret; ++ ++ DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n", ++ dma, dp, possible_crtcs); ++ ++ ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL); ++ if (!ipu_plane) { ++ DRM_ERROR("failed to allocate plane\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ ipu_plane->ipu = ipu; ++ ipu_plane->dma = dma; ++ ipu_plane->dp_flow = dp; ++ ++ ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs, ++ &ipu_plane_funcs, ipu_plane_formats, ++ ARRAY_SIZE(ipu_plane_formats), ++ priv); ++ if (ret) { ++ DRM_ERROR("failed to initialize plane\n"); ++ kfree(ipu_plane); ++ return ERR_PTR(ret); ++ } ++ ++ return ipu_plane; ++} +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/Kconfig linux-3.16.6/drivers/staging/imx-drm/Kconfig +--- linux-3.16.6.orig/drivers/staging/imx-drm/Kconfig 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/Kconfig 2014-10-23 12:37:23.890220362 -0500 +@@ -35,6 +35,7 @@ + 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. +@@ -51,3 +52,20 @@ + 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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/Kconfig.orig linux-3.16.6/drivers/staging/imx-drm/Kconfig.orig +--- linux-3.16.6.orig/drivers/staging/imx-drm/Kconfig.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/drivers/staging/imx-drm/Kconfig.orig 2014-10-23 12:35:45.310219999 -0500 +@@ -0,0 +1,62 @@ ++config DRM_IMX ++ tristate "DRM Support for Freescale i.MX" ++ select DRM_KMS_HELPER ++ select DRM_KMS_FB_HELPER ++ select VIDEOMODE_HELPERS ++ select DRM_GEM_CMA_HELPER ++ select DRM_KMS_CMA_HELPER ++ depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM) ++ help ++ enable i.MX graphics support ++ ++config DRM_IMX_FB_HELPER ++ tristate "provide legacy framebuffer /dev/fb0" ++ select DRM_KMS_CMA_HELPER ++ depends on DRM_IMX ++ help ++ The DRM framework can provide a legacy /dev/fb0 framebuffer ++ for your device. This is necessary to get a framebuffer console ++ and also for applications using the legacy framebuffer API ++ ++config DRM_IMX_PARALLEL_DISPLAY ++ tristate "Support for parallel displays" ++ select DRM_PANEL ++ depends on DRM_IMX ++ select VIDEOMODE_HELPERS ++ ++config DRM_IMX_TVE ++ tristate "Support for TV and VGA displays" ++ depends on DRM_IMX ++ select REGMAP_MMIO ++ help ++ Choose this to enable the internal Television Encoder (TVe) ++ found on i.MX53 processors. ++ ++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. ++ ++config DRM_IMX_IPUV3 ++ tristate "DRM Support for i.MX IPUv3" ++ depends on DRM_IMX ++ depends on IMX_IPUV3_CORE ++ help ++ Choose this if you have a i.MX5 or i.MX6 processor. ++ ++config DRM_IMX_HDMI ++ tristate "Freescale i.MX DRM 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. +diff -Nur linux-3.16.6.orig/drivers/staging/imx-drm/Makefile linux-3.16.6/drivers/staging/imx-drm/Makefile +--- linux-3.16.6.orig/drivers/staging/imx-drm/Makefile 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/Makefile 2014-10-23 12:37:30.178219970 -0500 +@@ -3,6 +3,7 @@ + + 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 +@@ -10,3 +11,5 @@ + 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 -Nur linux-3.16.6.orig/drivers/staging/imx-drm/parallel-display.c linux-3.16.6/drivers/staging/imx-drm/parallel-display.c +--- linux-3.16.6.orig/drivers/staging/imx-drm/parallel-display.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/drivers/staging/imx-drm/parallel-display.c 2014-10-23 12:35:30.986219995 -0500 +@@ -225,6 +225,8 @@ + 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; + else if (!strcmp(fmt, "lvds666")) + imxpd->interface_pix_fmt = v4l2_fourcc('L', 'V', 'D', '6'); + } +diff -Nur linux-3.16.6.orig/include/linux/cec-dev.h linux-3.16.6/include/linux/cec-dev.h +--- linux-3.16.6.orig/include/linux/cec-dev.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/include/linux/cec-dev.h 2014-10-23 12:37:18.374219998 -0500 +@@ -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 -Nur linux-3.16.6.orig/include/linux/mmc/host.h linux-3.16.6/include/linux/mmc/host.h +--- linux-3.16.6.orig/include/linux/mmc/host.h 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/include/linux/mmc/host.h 2014-10-23 12:34:18.742220000 -0500 +@@ -298,6 +298,11 @@ + 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 */ +diff -Nur linux-3.16.6.orig/include/uapi/linux/cec-dev.h linux-3.16.6/include/uapi/linux/cec-dev.h +--- linux-3.16.6.orig/include/uapi/linux/cec-dev.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/include/uapi/linux/cec-dev.h 2014-10-23 12:37:18.374219998 -0500 +@@ -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 -Nur linux-3.16.6.orig/include/uapi/linux/videodev2.h linux-3.16.6/include/uapi/linux/videodev2.h +--- linux-3.16.6.orig/include/uapi/linux/videodev2.h 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/include/uapi/linux/videodev2.h 2014-10-23 12:35:23.722220002 -0500 +@@ -299,6 +299,7 @@ + #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 -Nur linux-3.16.6.orig/include/video/imx-ipu-v3.h linux-3.16.6/include/video/imx-ipu-v3.h +--- linux-3.16.6.orig/include/video/imx-ipu-v3.h 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/include/video/imx-ipu-v3.h 2014-10-23 12:35:38.090219994 -0500 +@@ -27,6 +27,12 @@ + + #define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3') + ++#define CLK_POL_NEGEDGE 0 ++#define CLK_POL_POSEDGE 1 ++ ++#define ENABLE_POL_LOW 0 ++#define ENABLE_POL_HIGH 1 ++ + /* + * Bitfield of Display Interface signal polarities. + */ +@@ -37,7 +43,7 @@ + unsigned clksel_en:1; + unsigned clkidle_en:1; + unsigned data_pol:1; /* true = inverted */ +- unsigned clk_pol:1; /* true = rising edge */ ++ unsigned clk_pol:1; + unsigned enable_pol:1; + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; +diff -Nur linux-3.16.6.orig/sound/soc/codecs/sgtl5000.c linux-3.16.6/sound/soc/codecs/sgtl5000.c +--- linux-3.16.6.orig/sound/soc/codecs/sgtl5000.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/sound/soc/codecs/sgtl5000.c 2014-10-23 12:37:45.114220003 -0500 +@@ -773,7 +773,7 @@ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; + int reg; +- ++dev_info(codec->dev, "%s(): enabled %u\n", __func__, ldo->enabled); + if (ldo_regulator_is_enabled(dev)) + return 0; + +@@ -805,10 +805,16 @@ + { + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; ++dev_info(codec->dev, "%s(): enabled %u\n", __func__, ldo->enabled); ++ ++ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, ++ SGTL5000_LINREG_SIMPLE_POWERUP, ++ SGTL5000_LINREG_SIMPLE_POWERUP); + + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINEREG_D_POWERUP, + 0); ++dev_info(codec->dev, "%s: ANA_POWER = 0x%04x\n", __func__, snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER)); + + /* clear voltage info */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, +@@ -866,6 +872,7 @@ + config.dev = codec->dev; + config.driver_data = ldo; + config.init_data = init_data; ++ config.ena_gpio = -EINVAL; + + ldo->dev = regulator_register(&ldo->desc, &config); + if (IS_ERR(ldo->dev)) { +@@ -1159,8 +1166,11 @@ + * if vddio and vddd > 3.1v, + * charge pump should be clean before set ana_pwr + */ +- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, +- SGTL5000_VDDC_CHRGPMP_POWERUP, 0); ++// FIXME: this is total crap - we have read this register above into ++// ana_pwr, which we then modify (above), and then write back to the ++// register below. This modification just gets completely overwritten. ++// snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, ++// SGTL5000_VDDC_CHRGPMP_POWERUP, 0); + + /* VDDC use VDDIO rail */ + lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; +@@ -1304,6 +1314,9 @@ + int ret; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + ++ if (!devres_open_group(codec->dev, NULL, GFP_KERNEL)) ++ return -ENOMEM; ++ + ret = sgtl5000_enable_regulators(codec); + if (ret) + return ret; +@@ -1361,6 +1374,9 @@ + err: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); ++ ++ devres_release_group(codec->dev, NULL); ++ + ldo_regulator_remove(codec); + + return ret; +@@ -1374,6 +1390,9 @@ + + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); ++ ++ devres_release_group(codec->dev, NULL); ++ + ldo_regulator_remove(codec); + + return 0; +diff -Nur linux-3.16.6.orig/sound/soc/fsl/imx-pcm-dma.c linux-3.16.6/sound/soc/fsl/imx-pcm-dma.c +--- linux-3.16.6.orig/sound/soc/fsl/imx-pcm-dma.c 2014-10-15 05:05:43.000000000 -0500 ++++ linux-3.16.6/sound/soc/fsl/imx-pcm-dma.c 2014-10-23 12:35:54.206220260 -0500 +@@ -43,7 +43,7 @@ + .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, + }; +@@ -59,6 +59,7 @@ + { + return devm_snd_dmaengine_pcm_register(&pdev->dev, + &imx_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_COMPAT); + } + EXPORT_SYMBOL_GPL(imx_pcm_dma_init); +diff -Nur linux-3.16.6.orig/sound/soc/fsl/imx-pcm-dma.c.orig linux-3.16.6/sound/soc/fsl/imx-pcm-dma.c.orig +--- linux-3.16.6.orig/sound/soc/fsl/imx-pcm-dma.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.16.6/sound/soc/fsl/imx-pcm-dma.c.orig 2014-10-23 12:26:49.542220041 -0500 +@@ -0,0 +1,66 @@ ++/* ++ * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer ++ * ++ * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> ++ * ++ * This code is based on code copyrighted by Freescale, ++ * Liam Girdwood, Javier Martin and probably others. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++#include <linux/platform_device.h> ++#include <linux/dmaengine.h> ++#include <linux/types.h> ++#include <linux/module.h> ++ ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/soc.h> ++#include <sound/dmaengine_pcm.h> ++ ++#include "imx-pcm.h" ++ ++static bool filter(struct dma_chan *chan, void *param) ++{ ++ if (!imx_dma_is_general_purpose(chan)) ++ return false; ++ ++ chan->private = param; ++ ++ return true; ++} ++ ++static const struct snd_pcm_hardware imx_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_PAUSE | ++ SNDRV_PCM_INFO_RESUME, ++ .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, ++ .period_bytes_min = 128, ++ .period_bytes_max = 65535, /* Limited by SDMA engine */ ++ .periods_min = 4, ++ .periods_max = 255, ++ .fifo_size = 0, ++}; ++ ++static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = { ++ .pcm_hardware = &imx_pcm_hardware, ++ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, ++ .compat_filter_fn = filter, ++ .prealloc_buffer_size = IMX_SSI_DMABUF_SIZE, ++}; ++ ++int imx_pcm_dma_init(struct platform_device *pdev) ++{ ++ return devm_snd_dmaengine_pcm_register(&pdev->dev, ++ &imx_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_COMPAT); ++} ++EXPORT_SYMBOL_GPL(imx_pcm_dma_init); ++ ++MODULE_LICENSE("GPL"); |