diff --git a/patch.sh b/patch.sh index f6ea1894e94b87816f37ae2a064b355d803b7a23..775ed5abf5f235c030e018b789f8314020b950e2 100644 --- a/patch.sh +++ b/patch.sh @@ -372,6 +372,58 @@ dts () { ${git} "${DIR}/patches/dts/0009-arm-dts-am335x-bone-common-setup-default-pinmux-http.patch" ${git} "${DIR}/patches/dts/0010-ARM-dts-omap3-beagle-xm-spidev.patch" + ${git} "${DIR}/patches/dts/0011-hack-wand-enable-hdmi.patch" +} + +imx_drm () { + echo "dir: imx_drm" + ${git} "${DIR}/patches/imx_drm/0001-imx-drm-imx-hdmi-convert-HDMI-clock-settings-to-tabu.patch" + ${git} "${DIR}/patches/imx_drm/0002-imx-drm-imx-hdmi-clean-up-setting-CSC-registers.patch" + ${git} "${DIR}/patches/imx_drm/0003-imx-drm-imx-hdmi-provide-register-modification-funct.patch" + ${git} "${DIR}/patches/imx_drm/0004-imx-drm-imx-hdmi-clean-up-setting-of-vp_conf.patch" + ${git} "${DIR}/patches/imx_drm/0005-imx-drm-imx-hdmi-fix-CTS-N-setup-at-init-time.patch" + ${git} "${DIR}/patches/imx_drm/0006-imx-drm-ipu-v3-more-inteligent-DI-clock-selection.patch" + ${git} "${DIR}/patches/imx_drm/0007-imx-drm-ipu-v3-don-t-use-clk_round_rate-before-clk_s.patch" + ${git} "${DIR}/patches/imx_drm/0008-imx-drm-ipu-v3-more-clocking-fixes.patch" + ${git} "${DIR}/patches/imx_drm/0009-imx-drm-add-imx6-DT-configuration-for-HDMI.patch" + ${git} "${DIR}/patches/imx_drm/0010-imx-drm-update-and-fix-imx6-DT-descriptions-for-v3-H.patch" + ${git} "${DIR}/patches/imx_drm/0011-imx-drm-imx-drm-core-sanitise-imx_drm_encoder_get_mu.patch" + ${git} "${DIR}/patches/imx_drm/0012-imx-drm-imx-drm-core-use-array-instead-of-list-for-C.patch" + ${git} "${DIR}/patches/imx_drm/0013-imx-drm-provide-common-connector-mode-validation-fun.patch" + ${git} "${DIR}/patches/imx_drm/0014-imx-drm-simplify-setup-of-panel-format.patch" + ${git} "${DIR}/patches/imx_drm/0015-imx-drm-convert-to-componentised-device-support.patch" + ${git} "${DIR}/patches/imx_drm/0016-imx-drm-imx-hdmi-convert-to-a-component-device.patch" + ${git} "${DIR}/patches/imx_drm/0017-imx-drm-delay-publishing-sysfs-connector-entries.patch" + ${git} "${DIR}/patches/imx_drm/0018-imx-drm-remove-separate-imx-fbdev.patch" + ${git} "${DIR}/patches/imx_drm/0019-imx-drm-remove-imx-fb.c.patch" + ${git} "${DIR}/patches/imx_drm/0020-imx-drm-use-supplied-drm_device-where-possible.patch" + ${git} "${DIR}/patches/imx_drm/0021-imx-drm-imx-drm-core-provide-helper-function-to-pars.patch" + ${git} "${DIR}/patches/imx_drm/0022-imx-drm-imx-drm-core-provide-common-connector-and-en.patch" + ${git} "${DIR}/patches/imx_drm/0023-imx-drm-parallel-display-imx-tve-imx-ldb-initialise-.patch" + ${git} "${DIR}/patches/imx_drm/0024-imx-drm-imx-hdmi-initialise-drm-components-directly.patch" + ${git} "${DIR}/patches/imx_drm/0025-imx-drm-imx-drm-core-remove-imx_drm_connector-and-im.patch" + ${git} "${DIR}/patches/imx_drm/0026-imx-drm-imx-drm-core-get-rid-of-drm_mode_group_init_.patch" + ${git} "${DIR}/patches/imx_drm/0027-imx-drm-imx-drm-core-kill-off-mutex.patch" + ${git} "${DIR}/patches/imx_drm/0028-imx-drm-imx-drm-core-move-allocation-of-imxdrm-devic.patch" + ${git} "${DIR}/patches/imx_drm/0029-imx-drm-imx-drm-core-various-cleanups.patch" + ${git} "${DIR}/patches/imx_drm/0030-imx-drm-imx-drm-core-add-core-hotplug-connector-supp.patch" + ${git} "${DIR}/patches/imx_drm/0031-imx-drm-imx-hdmi-add-hotplug-support-to-HDMI-compone.patch" + ${git} "${DIR}/patches/imx_drm/0032-imx-drm-dw-hdmi-audio-add-audio-driver.patch" + ${git} "${DIR}/patches/imx_drm/0033-imx-drm-dw-hdmi-audio-parse-ELD-from-HDMI-driver.patch" + ${git} "${DIR}/patches/imx_drm/0034-imx-drm-add-CEC-HDMI-driver.patch" +} + +imx_drm_dts () { + echo "dir: imx_drm_dts" + ${git} "${DIR}/patches/imx_drm_dts/0001-staging-imx-drm-core-don-t-request-probe-deferral-in.patch" + ${git} "${DIR}/patches/imx_drm_dts/0002-staging-imx-drm-Add-temporary-copies-of-v4l2-of-pars.patch" + ${git} "${DIR}/patches/imx_drm_dts/0003-staging-imx-drm-core-Use-OF-graph-to-find-components.patch" + ${git} "${DIR}/patches/imx_drm_dts/0004-staging-imx-drm-Document-updated-imx-drm-device-tree.patch" + ${git} "${DIR}/patches/imx_drm_dts/0005-staging-imx-drm-Document-imx-hdmi-device-tree-bindin.patch" + ${git} "${DIR}/patches/imx_drm_dts/0006-ARM-dts-imx51-Add-IPU-ports-and-endpoints-move-imx-d.patch" + ${git} "${DIR}/patches/imx_drm_dts/0007-ARM-dts-imx53-Add-IPU-DI-ports-and-endpoints-move-im.patch" + ${git} "${DIR}/patches/imx_drm_dts/0008-ARM-dts-imx6qdl-Add-IPU-DI-ports-and-endpoints-move-.patch" + ${git} "${DIR}/patches/imx_drm_dts/0009-staging-imx-drm-Update-TODO.patch" } imx_video_staging () { @@ -439,7 +491,9 @@ usb dts -imx_video_staging +imx_drm +imx_drm_dts +#imx_video_staging omap_sprz319_erratum omap3_beagle_xm_rework diff --git a/patches/defconfig b/patches/defconfig index 2e5c13a361099e476d2cd2737874e1a8641d912d..764cb0a8966d99d29e0108c1a010a348e9f6a90c 100644 --- a/patches/defconfig +++ b/patches/defconfig @@ -3583,12 +3583,12 @@ CONFIG_LOGO_LINUX_MONO=y CONFIG_LOGO_LINUX_VGA16=y CONFIG_LOGO_LINUX_CLUT224=y CONFIG_FB_SSD1307=y -CONFIG_SOUND=m +CONFIG_SOUND=y CONFIG_SOUND_OSS_CORE=y CONFIG_SOUND_OSS_CORE_PRECLAIM=y -CONFIG_SND=m -CONFIG_SND_TIMER=m -CONFIG_SND_PCM=m +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y CONFIG_SND_DMAENGINE_PCM=m CONFIG_SND_HWDEP=m CONFIG_SND_RAWMIDI=m @@ -3598,7 +3598,7 @@ CONFIG_SND_SEQUENCER=m CONFIG_SND_SEQ_DUMMY=m CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=m -CONFIG_SND_PCM_OSS=m +CONFIG_SND_PCM_OSS=y CONFIG_SND_PCM_OSS_PLUGINS=y # CONFIG_SND_SEQUENCER_OSS is not set CONFIG_SND_HRTIMER=m @@ -4423,6 +4423,7 @@ CONFIG_DRM_IMX_LDB=y CONFIG_DRM_IMX_IPUV3_CORE=y CONFIG_DRM_IMX_IPUV3=y CONFIG_DRM_IMX_HDMI=y +CONFIG_DRM_DW_HDMI_CEC=y # CONFIG_DGRP is not set # CONFIG_LUSTRE_FS is not set # CONFIG_XILLYBUS is not set diff --git a/patches/dts/0002-ARM-dts-imx6qdl-wandboard-Add-support-for-i2c1.patch b/patches/dts/0002-ARM-dts-imx6qdl-wandboard-Add-support-for-i2c1.patch index 703e5c9bd56d80ef8753bc51319f2bd98c85c6c7..a5d9771d2600a023a6bd5a063667cf8369264c83 100644 --- a/patches/dts/0002-ARM-dts-imx6qdl-wandboard-Add-support-for-i2c1.patch +++ b/patches/dts/0002-ARM-dts-imx6qdl-wandboard-Add-support-for-i2c1.patch @@ -7,11 +7,11 @@ This patch adds support for i2c1 to the wandboard common dtsi file. Signed-off-by: Michael Panetta <panetta.mike@gmail.com> --- - arch/arm/boot/dts/imx6qdl-wandboard.dtsi | 7 +++++++ - 1 file changed, 7 insertions(+) + arch/arm/boot/dts/imx6qdl-wandboard.dtsi | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi -index cc64556..6706b36 100644 +index 7f30ff7..291c527 100644 --- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi @@ -62,6 +62,13 @@ @@ -21,13 +21,27 @@ index cc64556..6706b36 100644 +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_i2c1_1>; ++ pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; +}; + &i2c2 { clock-frequency = <100000>; pinctrl-names = "default"; +@@ -127,6 +134,13 @@ + >; + }; + ++ pinctrl_i2c1: i2c1grp { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 ++ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 ++ >; ++ }; ++ + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 -- 1.8.5.3 diff --git a/patches/dts/0011-hack-wand-enable-hdmi.patch b/patches/dts/0011-hack-wand-enable-hdmi.patch new file mode 100644 index 0000000000000000000000000000000000000000..316fe64f15b4128d1496d117b84c105c8cc19824 --- /dev/null +++ b/patches/dts/0011-hack-wand-enable-hdmi.patch @@ -0,0 +1,29 @@ +From 8b059ea786fb922c869443d90436b69ee3df6b63 Mon Sep 17 00:00:00 2001 +From: Robert Nelson <robertcnelson@gmail.com> +Date: Thu, 20 Feb 2014 13:46:50 -0600 +Subject: [PATCH 11/11] hack: wand: enable hdmi + +Signed-off-by: Robert Nelson <robertcnelson@gmail.com> +--- + arch/arm/boot/dts/imx6qdl-wandboard.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi +index 9afec84..c0c6960 100644 +--- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi ++++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi +@@ -78,6 +78,11 @@ + status = "okay"; + }; + ++&hdmi { ++ ddc = <&i2c1>; ++ status = "okay"; ++}; ++ + &i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0001-imx-drm-imx-hdmi-convert-HDMI-clock-settings-to-tabu.patch b/patches/imx_drm/0001-imx-drm-imx-hdmi-convert-HDMI-clock-settings-to-tabu.patch new file mode 100644 index 0000000000000000000000000000000000000000..7e86a24d268f5cdbe60b382df7a68979108dd418 --- /dev/null +++ b/patches/imx_drm/0001-imx-drm-imx-hdmi-convert-HDMI-clock-settings-to-tabu.patch @@ -0,0 +1,305 @@ +From 42b78b01aab43d0df555a9055e03db9a8fe70fd2 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:09:41 +0000 +Subject: [PATCH 01/34] imx-drm: imx-hdmi: convert HDMI clock settings to + tabular form + +Rather than having large if() and switch() statements, provide a table +to look up the register settings for various clock rates. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 250 ++++++++++++++----------------------- + 1 file changed, 95 insertions(+), 155 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 62ce0e8..cb316bf 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -806,19 +806,94 @@ static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable) + HDMI_PHY_CONF0_SELDIPIF_MASK); + } + ++enum { ++ RES_8, ++ RES_10, ++ RES_12, ++ RES_MAX, ++}; ++ ++struct mpll_config { ++ unsigned long mpixelclock; ++ struct { ++ u16 cpce; ++ u16 gmp; ++ } res[RES_MAX]; ++}; ++ ++static const struct mpll_config mpll_config[] = { ++ { ++ 45250000, { ++ { 0x01e0, 0x0000 }, ++ { 0x21e1, 0x0000 }, ++ { 0x41e2, 0x0000 } ++ }, ++ }, { ++ 92500000, { ++ { 0x0140, 0x0005 }, ++ { 0x2141, 0x0005 }, ++ { 0x4142, 0x0005 }, ++ }, ++ }, { ++ 148500000, { ++ { 0x00a0, 0x000a }, ++ { 0x20a1, 0x000a }, ++ { 0x40a2, 0x000a }, ++ }, ++ }, { ++ ~0UL, { ++ { 0x00a0, 0x000a }, ++ { 0x2001, 0x000f }, ++ { 0x4002, 0x000f }, ++ }, ++ } ++}; ++ ++struct curr_ctrl { ++ unsigned long mpixelclock; ++ u16 curr[RES_MAX]; ++}; ++ ++static const struct curr_ctrl curr_ctrl[] = { ++ /* pixelclk bpp8 bpp10 bpp12 */ ++ { ++ 54000000, { 0x091c, 0x091c, 0x06dc }, ++ }, { ++ 58400000, { 0x091c, 0x06dc, 0x06dc }, ++ }, { ++ 72000000, { 0x06dc, 0x06dc, 0x091c }, ++ }, { ++ 74250000, { 0x06dc, 0x0b5c, 0x091c }, ++ }, { ++ 118800000, { 0x091c, 0x091c, 0x06dc }, ++ }, { ++ 216000000, { 0x06dc, 0x0b5c, 0x091c }, ++ } ++}; ++ + static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, + unsigned char res, int cscon) + { ++ unsigned res_idx, i; + u8 val, msec; + +- /* color resolution 0 is 8 bit colour depth */ +- if (!res) +- res = 8; +- + if (prep) + return -EINVAL; +- else if (res != 8 && res != 12) ++ ++ switch (res) { ++ case 0: /* color resolution 0 is 8 bit colour depth */ ++ case 8: ++ res_idx = RES_8; ++ break; ++ case 10: ++ res_idx = RES_10; ++ break; ++ case 12: ++ res_idx = RES_12; ++ break; ++ default: + return -EINVAL; ++ } + + /* Enable csc path */ + if (cscon) +@@ -845,165 +920,30 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, + HDMI_PHY_I2CM_SLAVE_ADDR); + hdmi_phy_test_clear(hdmi, 0); + +- if (hdmi->hdmi_data.video_mode.mpixelclock <= 45250000) { +- switch (res) { +- case 8: +- /* PLL/MPLL Cfg */ +- hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); /* GMPCTRL */ +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x21e1, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x41e2, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); +- break; +- default: +- return -EINVAL; +- } +- } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 92500000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x0140, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x2141, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x4142, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); +- default: +- return -EINVAL; +- } +- } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 148500000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x20a1, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x40a2, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); +- default: +- return -EINVAL; +- } +- } else { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); ++ /* PLL/MPLL Cfg - always match on final entry */ ++ for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++) ++ if (hdmi->hdmi_data.video_mode.mpixelclock <= ++ mpll_config[i].mpixelclock) + break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x2001, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x4002, 0x06); +- hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); +- default: +- return -EINVAL; +- } +- } + +- if (hdmi->hdmi_data.video_mode.mpixelclock <= 54000000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); /* CURRCTRL */ +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- default: +- return -EINVAL; +- } +- } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 58400000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- default: +- return -EINVAL; +- } +- } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 72000000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); +- break; +- default: +- return -EINVAL; +- } +- } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 74250000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); +- break; +- default: +- return -EINVAL; +- } +- } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 118800000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- default: +- return -EINVAL; +- } +- } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 216000000) { +- switch (res) { +- case 8: +- hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); +- break; +- case 10: +- hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10); +- break; +- case 12: +- hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); ++ hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06); ++ hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15); ++ ++ for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++) ++ if (hdmi->hdmi_data.video_mode.mpixelclock <= ++ curr_ctrl[i].mpixelclock) + break; +- default: +- return -EINVAL; +- } +- } else { ++ ++ if (i >= ARRAY_SIZE(curr_ctrl)) { + dev_err(hdmi->dev, + "Pixel clock %d - unsupported by HDMI\n", + hdmi->hdmi_data.video_mode.mpixelclock); + return -EINVAL; + } + ++ /* CURRCTRL */ ++ hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10); ++ + hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ + hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); + /* RESISTANCE TERM 133Ohm Cfg */ +-- +1.8.5.3 + diff --git a/patches/imx_drm/0002-imx-drm-imx-hdmi-clean-up-setting-CSC-registers.patch b/patches/imx_drm/0002-imx-drm-imx-hdmi-clean-up-setting-CSC-registers.patch new file mode 100644 index 0000000000000000000000000000000000000000..c14fa07b3b208a809d72a983ed49628c4c93d043 --- /dev/null +++ b/patches/imx_drm/0002-imx-drm-imx-hdmi-clean-up-setting-CSC-registers.patch @@ -0,0 +1,77 @@ +From 128b27441f134f568fc164309624c35e34930a9a Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:09:46 +0000 +Subject: [PATCH 02/34] imx-drm: imx-hdmi: clean up setting CSC registers + +Rather than manually writing each register sequentially, we can use a +loop to reduce the amount of code. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 40 +++++++++++++------------------------- + 1 file changed, 14 insertions(+), 26 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index cb316bf..18de310 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -480,6 +480,7 @@ static int is_color_space_interpolation(struct imx_hdmi *hdmi) + static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) + { + const u16 (*csc_coeff)[3][4] = &csc_coeff_default; ++ unsigned i; + u32 csc_scale = 1; + u8 val; + +@@ -498,32 +499,19 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) + } + } + +- hdmi_writeb(hdmi, ((*csc_coeff)[0][0] & 0xff), HDMI_CSC_COEF_A1_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[0][0] >> 8), HDMI_CSC_COEF_A1_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[0][1] & 0xff), HDMI_CSC_COEF_A2_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[0][1] >> 8), HDMI_CSC_COEF_A2_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[0][2] & 0xff), HDMI_CSC_COEF_A3_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[0][2] >> 8), HDMI_CSC_COEF_A3_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[0][3] & 0xff), HDMI_CSC_COEF_A4_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[0][3] >> 8), HDMI_CSC_COEF_A4_MSB); +- +- hdmi_writeb(hdmi, ((*csc_coeff)[1][0] & 0xff), HDMI_CSC_COEF_B1_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[1][0] >> 8), HDMI_CSC_COEF_B1_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[1][1] & 0xff), HDMI_CSC_COEF_B2_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[1][1] >> 8), HDMI_CSC_COEF_B2_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[1][2] & 0xff), HDMI_CSC_COEF_B3_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[1][2] >> 8), HDMI_CSC_COEF_B3_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[1][3] & 0xff), HDMI_CSC_COEF_B4_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[1][3] >> 8), HDMI_CSC_COEF_B4_MSB); +- +- hdmi_writeb(hdmi, ((*csc_coeff)[2][0] & 0xff), HDMI_CSC_COEF_C1_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[2][0] >> 8), HDMI_CSC_COEF_C1_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[2][1] & 0xff), HDMI_CSC_COEF_C2_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[2][1] >> 8), HDMI_CSC_COEF_C2_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[2][2] & 0xff), HDMI_CSC_COEF_C3_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[2][2] >> 8), HDMI_CSC_COEF_C3_MSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[2][3] & 0xff), HDMI_CSC_COEF_C4_LSB); +- hdmi_writeb(hdmi, ((*csc_coeff)[2][3] >> 8), HDMI_CSC_COEF_C4_MSB); ++ /* The CSC registers are sequential, alternating MSB then LSB */ ++ for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) { ++ u16 coeff_a = (*csc_coeff)[0][i]; ++ u16 coeff_b = (*csc_coeff)[1][i]; ++ u16 coeff_c = (*csc_coeff)[2][i]; ++ ++ hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2); ++ hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2); ++ hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2); ++ hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2); ++ hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2); ++ hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); ++ } + + val = hdmi_readb(hdmi, HDMI_CSC_SCALE); + val &= ~HDMI_CSC_SCALE_CSCSCALE_MASK; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0003-imx-drm-imx-hdmi-provide-register-modification-funct.patch b/patches/imx_drm/0003-imx-drm-imx-hdmi-provide-register-modification-funct.patch new file mode 100644 index 0000000000000000000000000000000000000000..03ac738c10eca6d2488cf14f118258244401ae39 --- /dev/null +++ b/patches/imx_drm/0003-imx-drm-imx-hdmi-provide-register-modification-funct.patch @@ -0,0 +1,341 @@ +From 69c15c5787170e756ee06e764d5903bb70e7f540 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:09:51 +0000 +Subject: [PATCH 03/34] imx-drm: imx-hdmi: provide register modification + function + +There are a load of read-modify-write patterns to change bitfields in +various registers in this driver; provide a helper to perform this +manipulation. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 182 +++++++++++++------------------------ + 1 file changed, 65 insertions(+), 117 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 18de310..2fa8658 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -156,37 +156,34 @@ static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset) + return readb(hdmi->regs + offset); + } + ++static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg) ++{ ++ u8 val = hdmi_readb(hdmi, reg) & ~mask; ++ val |= data & mask; ++ hdmi_writeb(hdmi, val, reg); ++} ++ + static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg, + u8 shift, u8 mask) + { +- u8 value = hdmi_readb(hdmi, reg) & ~mask; +- value |= (data << shift) & mask; +- hdmi_writeb(hdmi, value, reg); ++ hdmi_modb(hdmi, data << shift, mask, reg); + } + + static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi, + unsigned int value) + { +- u8 val; +- + hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1); + hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2); + hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3); + + /* nshift factor = 0 */ +- val = hdmi_readb(hdmi, HDMI_AUD_CTS3); +- val &= ~HDMI_AUD_CTS3_N_SHIFT_MASK; +- hdmi_writeb(hdmi, val, HDMI_AUD_CTS3); ++ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); + } + + static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts) + { +- u8 val; +- + /* Must be set/cleared first */ +- val = hdmi_readb(hdmi, HDMI_AUD_CTS3); +- val &= ~HDMI_AUD_CTS3_CTS_MANUAL; +- hdmi_writeb(hdmi, val, HDMI_AUD_CTS3); ++ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + + hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); + hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); +@@ -482,7 +479,6 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) + const u16 (*csc_coeff)[3][4] = &csc_coeff_default; + unsigned i; + u32 csc_scale = 1; +- u8 val; + + if (is_color_space_conversion(hdmi)) { + if (hdmi->hdmi_data.enc_out_format == RGB) { +@@ -513,10 +509,8 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) + hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); + } + +- val = hdmi_readb(hdmi, HDMI_CSC_SCALE); +- val &= ~HDMI_CSC_SCALE_CSCSCALE_MASK; +- val |= csc_scale & HDMI_CSC_SCALE_CSCSCALE_MASK; +- hdmi_writeb(hdmi, val, HDMI_CSC_SCALE); ++ hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK, ++ HDMI_CSC_SCALE); + } + + static void hdmi_video_csc(struct imx_hdmi *hdmi) +@@ -524,7 +518,6 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi) + int color_depth = 0; + int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; + int decimation = 0; +- u8 val; + + /* YCC422 interpolation to 444 mode */ + if (is_color_space_interpolation(hdmi)) +@@ -545,10 +538,8 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi) + + /* Configure the CSC registers */ + hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG); +- val = hdmi_readb(hdmi, HDMI_CSC_SCALE); +- val &= ~HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK; +- val |= color_depth; +- hdmi_writeb(hdmi, val, HDMI_CSC_SCALE); ++ hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, ++ HDMI_CSC_SCALE); + + imx_hdmi_update_csc_coeffs(hdmi); + } +@@ -603,107 +594,80 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK); + hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); + +- val = hdmi_readb(hdmi, HDMI_VP_STUFF); +- val &= ~HDMI_VP_STUFF_PR_STUFFING_MASK; +- val |= HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE; +- hdmi_writeb(hdmi, val, HDMI_VP_STUFF); ++ hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE, ++ HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); + + /* Data from pixel repeater block */ + if (hdmi_data->pix_repet_factor > 1) { +- val = hdmi_readb(hdmi, HDMI_VP_CONF); +- val &= ~(HDMI_VP_CONF_PR_EN_MASK | +- HDMI_VP_CONF_BYPASS_SELECT_MASK); +- val |= HDMI_VP_CONF_PR_EN_ENABLE | +- HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; +- hdmi_writeb(hdmi, val, HDMI_VP_CONF); ++ hdmi_modb(hdmi, HDMI_VP_CONF_PR_EN_ENABLE | ++ HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER, ++ HDMI_VP_CONF_PR_EN_MASK | ++ HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); + } else { /* data from packetizer block */ +- val = hdmi_readb(hdmi, HDMI_VP_CONF); +- val &= ~(HDMI_VP_CONF_PR_EN_MASK | +- HDMI_VP_CONF_BYPASS_SELECT_MASK); +- val |= HDMI_VP_CONF_PR_EN_DISABLE | +- HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; +- hdmi_writeb(hdmi, val, HDMI_VP_CONF); ++ hdmi_modb(hdmi, HDMI_VP_CONF_PR_EN_DISABLE | ++ HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER, ++ HDMI_VP_CONF_PR_EN_MASK | ++ HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); + } + +- val = hdmi_readb(hdmi, HDMI_VP_STUFF); +- val &= ~HDMI_VP_STUFF_IDEFAULT_PHASE_MASK; +- val |= 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET; +- hdmi_writeb(hdmi, val, HDMI_VP_STUFF); ++ hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, ++ HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); + + hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); + + if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { +- val = hdmi_readb(hdmi, HDMI_VP_CONF); +- val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | +- HDMI_VP_CONF_PP_EN_ENMASK | +- HDMI_VP_CONF_YCC422_EN_MASK); +- val |= HDMI_VP_CONF_BYPASS_EN_DISABLE | +- HDMI_VP_CONF_PP_EN_ENABLE | +- HDMI_VP_CONF_YCC422_EN_DISABLE; +- hdmi_writeb(hdmi, val, HDMI_VP_CONF); ++ hdmi_modb(hdmi, HDMI_VP_CONF_BYPASS_EN_DISABLE | ++ HDMI_VP_CONF_PP_EN_ENABLE | ++ HDMI_VP_CONF_YCC422_EN_DISABLE, ++ HDMI_VP_CONF_BYPASS_EN_MASK | ++ HDMI_VP_CONF_PP_EN_ENMASK | ++ HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { +- val = hdmi_readb(hdmi, HDMI_VP_CONF); +- val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | +- HDMI_VP_CONF_PP_EN_ENMASK | +- HDMI_VP_CONF_YCC422_EN_MASK); +- val |= HDMI_VP_CONF_BYPASS_EN_DISABLE | +- HDMI_VP_CONF_PP_EN_DISABLE | +- HDMI_VP_CONF_YCC422_EN_ENABLE; +- hdmi_writeb(hdmi, val, HDMI_VP_CONF); ++ hdmi_modb(hdmi, HDMI_VP_CONF_BYPASS_EN_DISABLE | ++ HDMI_VP_CONF_PP_EN_DISABLE | ++ HDMI_VP_CONF_YCC422_EN_ENABLE, ++ HDMI_VP_CONF_BYPASS_EN_MASK | ++ HDMI_VP_CONF_PP_EN_ENMASK | ++ HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { +- val = hdmi_readb(hdmi, HDMI_VP_CONF); +- val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | +- HDMI_VP_CONF_PP_EN_ENMASK | +- HDMI_VP_CONF_YCC422_EN_MASK); +- val |= HDMI_VP_CONF_BYPASS_EN_ENABLE | +- HDMI_VP_CONF_PP_EN_DISABLE | +- HDMI_VP_CONF_YCC422_EN_DISABLE; +- hdmi_writeb(hdmi, val, HDMI_VP_CONF); ++ hdmi_modb(hdmi, HDMI_VP_CONF_BYPASS_EN_ENABLE | ++ HDMI_VP_CONF_PP_EN_DISABLE | ++ HDMI_VP_CONF_YCC422_EN_DISABLE, ++ HDMI_VP_CONF_BYPASS_EN_MASK | ++ HDMI_VP_CONF_PP_EN_ENMASK | ++ HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); + } else { + return; + } + +- val = hdmi_readb(hdmi, HDMI_VP_STUFF); +- val &= ~(HDMI_VP_STUFF_PP_STUFFING_MASK | +- HDMI_VP_STUFF_YCC422_STUFFING_MASK); +- val |= HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | +- HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE; +- hdmi_writeb(hdmi, val, HDMI_VP_STUFF); ++ hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | ++ HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE, ++ HDMI_VP_STUFF_PP_STUFFING_MASK | ++ HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF); + +- val = hdmi_readb(hdmi, HDMI_VP_CONF); +- val &= ~HDMI_VP_CONF_OUTPUT_SELECTOR_MASK; +- val |= output_select; +- hdmi_writeb(hdmi, val, HDMI_VP_CONF); ++ hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK, ++ HDMI_VP_CONF); + } + + static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi, + unsigned char bit) + { +- u8 val = hdmi_readb(hdmi, HDMI_PHY_TST0); +- val &= ~HDMI_PHY_TST0_TSTCLR_MASK; +- val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) & +- HDMI_PHY_TST0_TSTCLR_MASK; +- hdmi_writeb(hdmi, val, HDMI_PHY_TST0); ++ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET, ++ HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); + } + + static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi, + unsigned char bit) + { +- u8 val = hdmi_readb(hdmi, HDMI_PHY_TST0); +- val &= ~HDMI_PHY_TST0_TSTEN_MASK; +- val |= (bit << HDMI_PHY_TST0_TSTEN_OFFSET) & +- HDMI_PHY_TST0_TSTEN_MASK; +- hdmi_writeb(hdmi, val, HDMI_PHY_TST0); ++ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, ++ HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); + } + + static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi, + unsigned char bit) + { +- u8 val = hdmi_readb(hdmi, HDMI_PHY_TST0); +- val &= ~HDMI_PHY_TST0_TSTCLK_MASK; +- val |= (bit << HDMI_PHY_TST0_TSTCLK_OFFSET) & +- HDMI_PHY_TST0_TSTCLK_MASK; +- hdmi_writeb(hdmi, val, HDMI_PHY_TST0); ++ hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, ++ HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); + } + + static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi, +@@ -1000,7 +964,7 @@ static int imx_hdmi_phy_init(struct imx_hdmi *hdmi) + + static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi) + { +- u8 de, val; ++ u8 de; + + if (hdmi->hdmi_data.video_mode.mdataenablepolarity) + de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; +@@ -1008,20 +972,13 @@ static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi) + de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; + + /* disable rx detect */ +- val = hdmi_readb(hdmi, HDMI_A_HDCPCFG0); +- val &= HDMI_A_HDCPCFG0_RXDETECT_MASK; +- val |= HDMI_A_HDCPCFG0_RXDETECT_DISABLE; +- hdmi_writeb(hdmi, val, HDMI_A_HDCPCFG0); ++ hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE, ++ HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0); + +- val = hdmi_readb(hdmi, HDMI_A_VIDPOLCFG); +- val &= HDMI_A_VIDPOLCFG_DATAENPOL_MASK; +- val |= de; +- hdmi_writeb(hdmi, val, HDMI_A_VIDPOLCFG); ++ hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG); + +- val = hdmi_readb(hdmi, HDMI_A_HDCPCFG1); +- val &= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK; +- val |= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE; +- hdmi_writeb(hdmi, val, HDMI_A_HDCPCFG1); ++ hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE, ++ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1); + } + + static void hdmi_config_AVI(struct imx_hdmi *hdmi) +@@ -1245,11 +1202,7 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) + + static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi) + { +- u8 clkdis; +- +- clkdis = hdmi_readb(hdmi, HDMI_MC_CLKDIS); +- clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); + } + + /* Workaround to clear the overflow condition */ +@@ -1593,7 +1546,6 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) + struct imx_hdmi *hdmi = dev_id; + u8 intr_stat; + u8 phy_int_pol; +- u8 val; + + intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); + +@@ -1603,17 +1555,13 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) + if (phy_int_pol & HDMI_PHY_HPD) { + dev_dbg(hdmi->dev, "EVENT=plugin\n"); + +- val = hdmi_readb(hdmi, HDMI_PHY_POL0); +- val &= ~HDMI_PHY_HPD; +- hdmi_writeb(hdmi, val, HDMI_PHY_POL0); ++ hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0); + + imx_hdmi_poweron(hdmi); + } else { + dev_dbg(hdmi->dev, "EVENT=plugout\n"); + +- val = hdmi_readb(hdmi, HDMI_PHY_POL0); +- val |= HDMI_PHY_HPD; +- hdmi_writeb(hdmi, val, HDMI_PHY_POL0); ++ hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, HDMI_PHY_POL0); + + imx_hdmi_poweroff(hdmi); + } +-- +1.8.5.3 + diff --git a/patches/imx_drm/0004-imx-drm-imx-hdmi-clean-up-setting-of-vp_conf.patch b/patches/imx_drm/0004-imx-drm-imx-hdmi-clean-up-setting-of-vp_conf.patch new file mode 100644 index 0000000000000000000000000000000000000000..14fd8c6ebb61d0ff52b028fc2ddb4b14973eac7e --- /dev/null +++ b/patches/imx_drm/0004-imx-drm-imx-hdmi-clean-up-setting-of-vp_conf.patch @@ -0,0 +1,98 @@ +From 57e6643d980504c919e34695209a88ff9084cd65 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:09:56 +0000 +Subject: [PATCH 04/34] imx-drm: imx-hdmi: clean up setting of vp_conf + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 49 +++++++++++++++++--------------------- + 1 file changed, 22 insertions(+), 27 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 2fa8658..ec5b5e2 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -555,7 +555,7 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) + unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; + unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; + struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; +- u8 val; ++ u8 val, vp_conf; + + if (hdmi_data->enc_out_format == RGB + || hdmi_data->enc_out_format == YCBCR444) { +@@ -599,47 +599,42 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) + + /* Data from pixel repeater block */ + if (hdmi_data->pix_repet_factor > 1) { +- hdmi_modb(hdmi, HDMI_VP_CONF_PR_EN_ENABLE | +- HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER, +- HDMI_VP_CONF_PR_EN_MASK | +- HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); ++ vp_conf = HDMI_VP_CONF_PR_EN_ENABLE | ++ HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; + } else { /* data from packetizer block */ +- hdmi_modb(hdmi, HDMI_VP_CONF_PR_EN_DISABLE | +- HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER, +- HDMI_VP_CONF_PR_EN_MASK | +- HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); ++ vp_conf = HDMI_VP_CONF_PR_EN_DISABLE | ++ HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; + } + ++ hdmi_modb(hdmi, vp_conf, ++ HDMI_VP_CONF_PR_EN_MASK | ++ HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); ++ + hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, + HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); + + hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); + + if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { +- hdmi_modb(hdmi, HDMI_VP_CONF_BYPASS_EN_DISABLE | +- HDMI_VP_CONF_PP_EN_ENABLE | +- HDMI_VP_CONF_YCC422_EN_DISABLE, +- HDMI_VP_CONF_BYPASS_EN_MASK | +- HDMI_VP_CONF_PP_EN_ENMASK | +- HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); ++ vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | ++ HDMI_VP_CONF_PP_EN_ENABLE | ++ HDMI_VP_CONF_YCC422_EN_DISABLE; + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { +- hdmi_modb(hdmi, HDMI_VP_CONF_BYPASS_EN_DISABLE | +- HDMI_VP_CONF_PP_EN_DISABLE | +- HDMI_VP_CONF_YCC422_EN_ENABLE, +- HDMI_VP_CONF_BYPASS_EN_MASK | +- HDMI_VP_CONF_PP_EN_ENMASK | +- HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); ++ vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | ++ HDMI_VP_CONF_PP_EN_DISABLE | ++ HDMI_VP_CONF_YCC422_EN_ENABLE; + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { +- hdmi_modb(hdmi, HDMI_VP_CONF_BYPASS_EN_ENABLE | +- HDMI_VP_CONF_PP_EN_DISABLE | +- HDMI_VP_CONF_YCC422_EN_DISABLE, +- HDMI_VP_CONF_BYPASS_EN_MASK | +- HDMI_VP_CONF_PP_EN_ENMASK | +- HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); ++ vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE | ++ HDMI_VP_CONF_PP_EN_DISABLE | ++ HDMI_VP_CONF_YCC422_EN_DISABLE; + } else { + return; + } + ++ hdmi_modb(hdmi, vp_conf, ++ HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK | ++ HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); ++ + hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE, + HDMI_VP_STUFF_PP_STUFFING_MASK | +-- +1.8.5.3 + diff --git a/patches/imx_drm/0005-imx-drm-imx-hdmi-fix-CTS-N-setup-at-init-time.patch b/patches/imx_drm/0005-imx-drm-imx-hdmi-fix-CTS-N-setup-at-init-time.patch new file mode 100644 index 0000000000000000000000000000000000000000..10af798eb5e7ecb2fbb61bde18591ca598f0a694 --- /dev/null +++ b/patches/imx_drm/0005-imx-drm-imx-hdmi-fix-CTS-N-setup-at-init-time.patch @@ -0,0 +1,123 @@ +From 38253a34a0b2dd42b84dd708e0ee13df907332e3 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:02 +0000 +Subject: [PATCH 05/34] imx-drm: imx-hdmi: fix CTS/N setup at init time + +Many of the variables for the audio clock regenerator (CTS/N) were not +initialised in any way. The pixel rate which was being used also +wasn't being adjusted at all when the display mode is modified. + +Get rid of the seaprate 'pixel_clk_rate', and use the stored pixel +clock rate instead. Pass this desired pixel clock rate into +hdmi_set_clk_regenerator(). Collapse down hdmi_init_clk_regenerator() +since it is a copy of hdmi_set_clk_regenerator(), and pass a default +pixel clock rate. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 48 ++++++++------------------------------ + 1 file changed, 10 insertions(+), 38 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index ec5b5e2..05cf8a0 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -134,7 +134,6 @@ struct imx_hdmi { + struct i2c_adapter *ddc; + void __iomem *regs; + +- unsigned long pixel_clk_rate; + unsigned int sample_rate; + int ratio; + }; +@@ -328,34 +327,25 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, + return (cts * ratio) / 100; + } + +-static void hdmi_get_pixel_clk(struct imx_hdmi *hdmi) +-{ +- unsigned long rate; +- +- rate = 65000000; /* FIXME */ +- +- if (rate) +- hdmi->pixel_clk_rate = rate; +-} +- +-static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi) ++static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi, ++ unsigned long pixel_clk) + { + unsigned int clk_n, clk_cts; + +- clk_n = hdmi_compute_n(hdmi->sample_rate, hdmi->pixel_clk_rate, ++ clk_n = hdmi_compute_n(hdmi->sample_rate, pixel_clk, + hdmi->ratio); +- clk_cts = hdmi_compute_cts(hdmi->sample_rate, hdmi->pixel_clk_rate, ++ clk_cts = hdmi_compute_cts(hdmi->sample_rate, pixel_clk, + hdmi->ratio); + + if (!clk_cts) { + dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n", +- __func__, hdmi->pixel_clk_rate); ++ __func__, pixel_clk); + return; + } + + dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n", + __func__, hdmi->sample_rate, hdmi->ratio, +- hdmi->pixel_clk_rate, clk_n, clk_cts); ++ pixel_clk, clk_n, clk_cts); + + hdmi_set_clock_regenerator_n(hdmi, clk_n); + hdmi_regenerate_cts(hdmi, clk_cts); +@@ -363,32 +353,12 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi) + + static void hdmi_init_clk_regenerator(struct imx_hdmi *hdmi) + { +- unsigned int clk_n, clk_cts; +- +- clk_n = hdmi_compute_n(hdmi->sample_rate, hdmi->pixel_clk_rate, +- hdmi->ratio); +- clk_cts = hdmi_compute_cts(hdmi->sample_rate, hdmi->pixel_clk_rate, +- hdmi->ratio); +- +- if (!clk_cts) { +- dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n", +- __func__, hdmi->pixel_clk_rate); +- return; +- } +- +- dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n", +- __func__, hdmi->sample_rate, hdmi->ratio, +- hdmi->pixel_clk_rate, clk_n, clk_cts); +- +- hdmi_set_clock_regenerator_n(hdmi, clk_n); +- hdmi_regenerate_cts(hdmi, clk_cts); ++ hdmi_set_clk_regenerator(hdmi, 74250000); + } + + static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi) + { +- /* Get pixel clock from ipu */ +- hdmi_get_pixel_clk(hdmi); +- hdmi_set_clk_regenerator(hdmi); ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); + } + + /* +@@ -1636,6 +1606,8 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) + return -ENOMEM; + + hdmi->dev = &pdev->dev; ++ hdmi->sample_rate = 48000; ++ hdmi->ratio = 100; + + if (of_id) { + const struct platform_device_id *device_id = of_id->data; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0006-imx-drm-ipu-v3-more-inteligent-DI-clock-selection.patch b/patches/imx_drm/0006-imx-drm-ipu-v3-more-inteligent-DI-clock-selection.patch new file mode 100644 index 0000000000000000000000000000000000000000..0282558224512f7857f2b16d7a47a8e58eb550b9 --- /dev/null +++ b/patches/imx_drm/0006-imx-drm-ipu-v3-more-inteligent-DI-clock-selection.patch @@ -0,0 +1,114 @@ +From 41a24912d8ad817925d8a397be124bca23bd1591 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:07 +0000 +Subject: [PATCH 06/34] imx-drm: ipu-v3: more inteligent DI clock selection + +The DI clock selection was very rudimentary: it would statically use +either the IPU internal clock or the DI external clock depending on +which "encoder" was being used. In the case of HDMI, it would always +use the IPU clock. + +Moreover, using the IPU clock resulted in fractional divisors, which +are achieved by skipping clock pulses. This can result in the HDMI +PHY PLL being frequency modulated, and the attached device is then +unable to properly lock on to the TMDS clock. + +We need at least 1% accurate and stable clocks for HDMI. + +Arrange for the DI clock to be sourced from the IPU internal clock +if it can satisfy our requirements, otherwise switch to the DI +external clock and try and set the external clock to our desired +pixel clock rate. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/ipu-v3/ipu-di.c | 54 +++++++++++++++++++++++++++++++-- + 1 file changed, 52 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +index 948a49b..8c7241bb 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +@@ -544,10 +544,48 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) + return -EINVAL; + ++ dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", ++ clk_get_rate(di->clk_ipu), ++ clk_get_rate(di->clk_di), ++ sig->pixelclock); ++ ++ /* ++ * CLKMODE_EXT means we must use the DI clock: this is needed ++ * for things like LVDS which needs to feed the DI and LDB with ++ * the same pixel clock. ++ * ++ * For other interfaces, we can arbitarily select between the DI ++ * specific clock and the internal IPU clock. See DI_GENERAL ++ * bit 20. We select the IPU clock if it can give us a clock ++ * rate within 1% of the requested frequency, otherwise we use ++ * the DI clock. ++ */ + if (sig->clkflags & IPU_DI_CLKMODE_EXT) + parent = di->clk_di; +- else +- parent = di->clk_ipu; ++ else { ++ unsigned long rate, clkrate; ++ unsigned div, error; ++ ++ clkrate = clk_get_rate(di->clk_ipu); ++ div = (clkrate + sig->pixelclock / 2) / sig->pixelclock; ++ rate = clkrate / div; ++ ++ error = rate / (sig->pixelclock / 1000); ++ ++ dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", ++ rate, div, (signed)(error - 1000) / 10, error % 10); ++ ++ /* Allow a 1% error */ ++ if (error < 1010 && error >= 990) { ++ parent = di->clk_ipu; ++ } else { ++ parent = di->clk_di; ++ ++ ret = clk_set_rate(parent, sig->pixelclock); ++ if (ret) ++ dev_err(di->ipu->dev, "Setting of DI clock failed: %d\n", ret); ++ } ++ } + + ret = clk_set_parent(di->clk_di_pixel, parent); + if (ret) { +@@ -557,6 +595,11 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + return ret; + } + ++ /* ++ * CLKMODE_SYNC means that we want the DI to be clocked at the ++ * same rate as the parent clock. This is needed (eg) for LDB ++ * which needs to be fed with the same pixel clock. ++ */ + if (sig->clkflags & IPU_DI_CLKMODE_SYNC) + round = clk_get_rate(parent); + else +@@ -564,6 +607,13 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + + ret = clk_set_rate(di->clk_di_pixel, round); + ++ dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, got %luHz\n", ++ sig->pixelclock, ++ clk_get_rate(di->clk_ipu), ++ clk_get_rate(di->clk_di), ++ parent == di->clk_di ? "DI" : "IPU", ++ clk_get_rate(di->clk_di_pixel)); ++ + h_total = sig->width + sig->h_sync_width + sig->h_start_width + + sig->h_end_width; + v_total = sig->height + sig->v_sync_width + sig->v_start_width + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0007-imx-drm-ipu-v3-don-t-use-clk_round_rate-before-clk_s.patch b/patches/imx_drm/0007-imx-drm-ipu-v3-don-t-use-clk_round_rate-before-clk_s.patch new file mode 100644 index 0000000000000000000000000000000000000000..2e41a91b0f07c03287eb5f2289d0b9800e5332b7 --- /dev/null +++ b/patches/imx_drm/0007-imx-drm-ipu-v3-don-t-use-clk_round_rate-before-clk_s.patch @@ -0,0 +1,62 @@ +From c793cf51b6aff2ef3c9defa75465112694c047b9 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:12 +0000 +Subject: [PATCH 07/34] imx-drm: ipu-v3: don't use clk_round_rate() before + clk_set_rate() + +This is nonsense; clk_round_rate() is just clk_set_rate() without the +side effect of changing the hardware. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/ipu-v3/ipu-di.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +index 8c7241bb..d766e18 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +@@ -560,9 +560,10 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + * rate within 1% of the requested frequency, otherwise we use + * the DI clock. + */ +- if (sig->clkflags & IPU_DI_CLKMODE_EXT) ++ round = sig->pixelclock; ++ if (sig->clkflags & IPU_DI_CLKMODE_EXT) { + parent = di->clk_di; +- else { ++ } else { + unsigned long rate, clkrate; + unsigned div, error; + +@@ -584,6 +585,9 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + ret = clk_set_rate(parent, sig->pixelclock); + if (ret) + dev_err(di->ipu->dev, "Setting of DI clock failed: %d\n", ret); ++ ++ /* Use the integer divisor rate - avoid fractional dividers */ ++ round = rate; + } + } + +@@ -599,11 +603,12 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + * CLKMODE_SYNC means that we want the DI to be clocked at the + * same rate as the parent clock. This is needed (eg) for LDB + * which needs to be fed with the same pixel clock. ++ * ++ * Note: clk_set_rate(clk, clk_round_rate(clk, rate)) is the ++ * same as clk_set_rate(clk, rate); + */ + if (sig->clkflags & IPU_DI_CLKMODE_SYNC) + round = clk_get_rate(parent); +- else +- round = clk_round_rate(di->clk_di_pixel, sig->pixelclock); + + ret = clk_set_rate(di->clk_di_pixel, round); + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0008-imx-drm-ipu-v3-more-clocking-fixes.patch b/patches/imx_drm/0008-imx-drm-ipu-v3-more-clocking-fixes.patch new file mode 100644 index 0000000000000000000000000000000000000000..c574d5705c3eafcb867d2b1a579805a39afcb1e0 --- /dev/null +++ b/patches/imx_drm/0008-imx-drm-ipu-v3-more-clocking-fixes.patch @@ -0,0 +1,453 @@ +From 87659eff31228e0f482b14d0f6f67a46feea8c2c Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:17 +0000 +Subject: [PATCH 08/34] imx-drm: ipu-v3: more clocking fixes + +There's no point in using the clk API for this; we end up having to +violate the layering this provides. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/ipu-v3/ipu-di.c | 328 ++++++++++---------------------- + 1 file changed, 105 insertions(+), 223 deletions(-) + +diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +index d766e18..82a9eba 100644 +--- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c ++++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c +@@ -19,9 +19,6 @@ + #include <linux/io.h> + #include <linux/err.h> + #include <linux/platform_device.h> +-#include <linux/clk.h> +-#include <linux/clk-provider.h> +-#include <linux/clkdev.h> + + #include "imx-ipu-v3.h" + #include "ipu-prv.h" +@@ -33,10 +30,7 @@ struct ipu_di { + struct clk *clk_di; /* display input clock */ + struct clk *clk_ipu; /* IPU bus clock */ + struct clk *clk_di_pixel; /* resulting pixel clock */ +- struct clk_hw clk_hw_out; +- char *clk_name; + bool inuse; +- unsigned long clkflags; + struct ipu_soc *ipu; + }; + +@@ -141,130 +135,6 @@ static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) + writel(value, di->base + offset); + } + +-static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate) +-{ +- u64 tmp = inrate; +- int div; +- +- tmp *= 16; +- +- do_div(tmp, outrate); +- +- div = tmp; +- +- if (div < 0x10) +- div = 0x10; +- +-#ifdef WTF_IS_THIS +- /* +- * Freescale has this in their Kernel. It is neither clear what +- * it does nor why it does it +- */ +- if (div & 0x10) +- div &= ~0x7; +- else { +- /* Round up divider if it gets us closer to desired pix clk */ +- if ((div & 0xC) == 0xC) { +- div += 0x10; +- div &= ~0xF; +- } +- } +-#endif +- return div; +-} +- +-static unsigned long clk_di_recalc_rate(struct clk_hw *hw, +- unsigned long parent_rate) +-{ +- struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out); +- unsigned long outrate; +- u32 div = ipu_di_read(di, DI_BS_CLKGEN0); +- +- if (div < 0x10) +- div = 0x10; +- +- outrate = (parent_rate / div) * 16; +- +- return outrate; +-} +- +-static long clk_di_round_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long *prate) +-{ +- struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out); +- unsigned long outrate; +- int div; +- u32 val; +- +- div = ipu_di_clk_calc_div(*prate, rate); +- +- outrate = (*prate / div) * 16; +- +- val = ipu_di_read(di, DI_GENERAL); +- +- if (!(val & DI_GEN_DI_CLK_EXT) && outrate > *prate / 2) +- outrate = *prate / 2; +- +- dev_dbg(di->ipu->dev, +- "%s: inrate: %ld div: 0x%08x outrate: %ld wanted: %ld\n", +- __func__, *prate, div, outrate, rate); +- +- return outrate; +-} +- +-static int clk_di_set_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long parent_rate) +-{ +- struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out); +- int div; +- u32 clkgen0; +- +- clkgen0 = ipu_di_read(di, DI_BS_CLKGEN0) & ~0xfff; +- +- div = ipu_di_clk_calc_div(parent_rate, rate); +- +- ipu_di_write(di, clkgen0 | div, DI_BS_CLKGEN0); +- +- dev_dbg(di->ipu->dev, "%s: inrate: %ld desired: %ld div: 0x%08x\n", +- __func__, parent_rate, rate, div); +- return 0; +-} +- +-static u8 clk_di_get_parent(struct clk_hw *hw) +-{ +- struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out); +- u32 val; +- +- val = ipu_di_read(di, DI_GENERAL); +- +- return val & DI_GEN_DI_CLK_EXT ? 1 : 0; +-} +- +-static int clk_di_set_parent(struct clk_hw *hw, u8 index) +-{ +- struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out); +- u32 val; +- +- val = ipu_di_read(di, DI_GENERAL); +- +- if (index) +- val |= DI_GEN_DI_CLK_EXT; +- else +- val &= ~DI_GEN_DI_CLK_EXT; +- +- ipu_di_write(di, val, DI_GENERAL); +- +- return 0; +-} +- +-static struct clk_ops clk_di_ops = { +- .round_rate = clk_di_round_rate, +- .set_rate = clk_di_set_rate, +- .recalc_rate = clk_di_recalc_rate, +- .set_parent = clk_di_set_parent, +- .get_parent = clk_di_get_parent, +-}; +- + static void ipu_di_data_wave_config(struct ipu_di *di, + int wave_gen, + int access_size, int component_size) +@@ -528,42 +398,58 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, + ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); + } + +-int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) ++static void ipu_di_config_clock(struct ipu_di *di, ++ const struct ipu_di_signal_cfg *sig) + { +- u32 reg; +- u32 di_gen, vsync_cnt; +- u32 div; +- u32 h_total, v_total; +- int ret; +- unsigned long round; +- struct clk *parent; ++ struct clk *clk; ++ unsigned clkgen0; ++ uint32_t val; + +- dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", +- di->id, sig->width, sig->height); ++ if (sig->clkflags & IPU_DI_CLKMODE_EXT) { ++ /* ++ * CLKMODE_EXT means we must use the DI clock: this is ++ * needed for things like LVDS which needs to feed the ++ * DI and LDB with the same pixel clock. ++ */ ++ clk = di->clk_di; ++ ++ if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { ++ /* ++ * CLKMODE_SYNC means that we want the DI to be ++ * clocked at the same rate as the parent clock. ++ * This is needed (eg) for LDB which needs to be ++ * fed with the same pixel clock. We assume that ++ * the LDB clock has already been set correctly. ++ */ ++ clkgen0 = 1 << 4; ++ } else { ++ /* ++ * We can use the divider. We should really have ++ * a flag here indicating whether the bridge can ++ * cope with a fractional divider or not. For the ++ * time being, let's go for simplicitly and ++ * reliability. ++ */ ++ unsigned long in_rate; ++ unsigned div; + +- if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) +- return -EINVAL; ++ clk_set_rate(clk, sig->pixelclock); + +- dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", +- clk_get_rate(di->clk_ipu), +- clk_get_rate(di->clk_di), +- sig->pixelclock); ++ in_rate = clk_get_rate(clk); ++ div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; ++ if (div == 0) ++ div = 1; + +- /* +- * CLKMODE_EXT means we must use the DI clock: this is needed +- * for things like LVDS which needs to feed the DI and LDB with +- * the same pixel clock. +- * +- * For other interfaces, we can arbitarily select between the DI +- * specific clock and the internal IPU clock. See DI_GENERAL +- * bit 20. We select the IPU clock if it can give us a clock +- * rate within 1% of the requested frequency, otherwise we use +- * the DI clock. +- */ +- round = sig->pixelclock; +- if (sig->clkflags & IPU_DI_CLKMODE_EXT) { +- parent = di->clk_di; ++ clkgen0 = div << 4; ++ } + } else { ++ /* ++ * For other interfaces, we can arbitarily select between ++ * the DI specific clock and the internal IPU clock. See ++ * DI_GENERAL bit 20. We select the IPU clock if it can ++ * give us a clock rate within 1% of the requested frequency, ++ * otherwise we use the DI clock. ++ */ + unsigned long rate, clkrate; + unsigned div, error; + +@@ -578,54 +464,80 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) + + /* Allow a 1% error */ + if (error < 1010 && error >= 990) { +- parent = di->clk_ipu; ++ clk = di->clk_ipu; ++ ++ clkgen0 = div << 4; + } else { +- parent = di->clk_di; ++ unsigned long in_rate; ++ unsigned div; ++ ++ clk = di->clk_di; + +- ret = clk_set_rate(parent, sig->pixelclock); +- if (ret) +- dev_err(di->ipu->dev, "Setting of DI clock failed: %d\n", ret); ++ clk_set_rate(clk, sig->pixelclock); + +- /* Use the integer divisor rate - avoid fractional dividers */ +- round = rate; ++ in_rate = clk_get_rate(clk); ++ div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; ++ if (div == 0) ++ div = 1; ++ ++ clkgen0 = div << 4; + } + } + +- ret = clk_set_parent(di->clk_di_pixel, parent); +- if (ret) { +- dev_err(di->ipu->dev, +- "setting pixel clock to parent %s failed with %d\n", +- __clk_get_name(parent), ret); +- return ret; +- } ++ di->clk_di_pixel = clk; ++ ++ /* Set the divider */ ++ ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); + + /* +- * CLKMODE_SYNC means that we want the DI to be clocked at the +- * same rate as the parent clock. This is needed (eg) for LDB +- * which needs to be fed with the same pixel clock. +- * +- * Note: clk_set_rate(clk, clk_round_rate(clk, rate)) is the +- * same as clk_set_rate(clk, rate); ++ * Set the high/low periods. Bits 24:16 give us the falling edge, ++ * and bits 8:0 give the rising edge. LSB is fraction, and is ++ * based on the divider above. We want a 50% duty cycle, so set ++ * the falling edge to be half the divider. + */ +- if (sig->clkflags & IPU_DI_CLKMODE_SYNC) +- round = clk_get_rate(parent); ++ ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); + +- ret = clk_set_rate(di->clk_di_pixel, round); ++ /* Finally select the input clock */ ++ val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; ++ if (clk == di->clk_di) ++ val |= DI_GEN_DI_CLK_EXT; ++ ipu_di_write(di, val, DI_GENERAL); + +- dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, got %luHz\n", ++ dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", + sig->pixelclock, + clk_get_rate(di->clk_ipu), + clk_get_rate(di->clk_di), +- parent == di->clk_di ? "DI" : "IPU", +- clk_get_rate(di->clk_di_pixel)); ++ clk == di->clk_di ? "DI" : "IPU", ++ clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); ++} ++ ++int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) ++{ ++ u32 reg; ++ u32 di_gen, vsync_cnt; ++ u32 div; ++ u32 h_total, v_total; ++ ++ dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", ++ di->id, sig->width, sig->height); ++ ++ if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) ++ return -EINVAL; + + h_total = sig->width + sig->h_sync_width + sig->h_start_width + + sig->h_end_width; + v_total = sig->height + sig->v_sync_width + sig->v_start_width + + sig->v_end_width; + ++ dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", ++ clk_get_rate(di->clk_ipu), ++ clk_get_rate(di->clk_di), ++ sig->pixelclock); ++ + mutex_lock(&di_mutex); + ++ ipu_di_config_clock(di, sig); ++ + div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; + div = div / 16; /* Now divider is integer portion */ + +@@ -709,7 +621,11 @@ EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); + + int ipu_di_enable(struct ipu_di *di) + { +- int ret = clk_prepare_enable(di->clk_di_pixel); ++ int ret; ++ ++ WARN_ON(IS_ERR(di->clk_di_pixel)); ++ ++ ret = clk_prepare_enable(di->clk_di_pixel); + if (ret) + return ret; + +@@ -721,6 +637,8 @@ EXPORT_SYMBOL_GPL(ipu_di_enable); + + int ipu_di_disable(struct ipu_di *di) + { ++ WARN_ON(IS_ERR(di->clk_di_pixel)); ++ + ipu_module_disable(di->ipu, di->module); + + clk_disable_unprepare(di->clk_di_pixel); +@@ -776,13 +694,6 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, + u32 module, struct clk *clk_ipu) + { + struct ipu_di *di; +- int ret; +- const char *di_parent[2]; +- struct clk_init_data init = { +- .ops = &clk_di_ops, +- .num_parents = 2, +- .flags = 0, +- }; + + if (id > 1) + return -ENODEV; +@@ -804,45 +715,16 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, + if (!di->base) + return -ENOMEM; + +- di_parent[0] = __clk_get_name(di->clk_ipu); +- di_parent[1] = __clk_get_name(di->clk_di); +- + ipu_di_write(di, 0x10, DI_BS_CLKGEN0); + +- init.parent_names = (const char **)&di_parent; +- di->clk_name = kasprintf(GFP_KERNEL, "%s_di%d_pixel", +- dev_name(dev), id); +- if (!di->clk_name) +- return -ENOMEM; +- +- init.name = di->clk_name; +- +- di->clk_hw_out.init = &init; +- di->clk_di_pixel = clk_register(dev, &di->clk_hw_out); +- +- if (IS_ERR(di->clk_di_pixel)) { +- ret = PTR_ERR(di->clk_di_pixel); +- goto failed_clk_register; +- } +- + dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", + id, base, di->base); + di->inuse = false; + di->ipu = ipu; + + return 0; +- +-failed_clk_register: +- +- kfree(di->clk_name); +- +- return ret; + } + + void ipu_di_exit(struct ipu_soc *ipu, int id) + { +- struct ipu_di *di = ipu->di_priv[id]; +- +- clk_unregister(di->clk_di_pixel); +- kfree(di->clk_name); + } +-- +1.8.5.3 + diff --git a/patches/imx_drm/0009-imx-drm-add-imx6-DT-configuration-for-HDMI.patch b/patches/imx_drm/0009-imx-drm-add-imx6-DT-configuration-for-HDMI.patch new file mode 100644 index 0000000000000000000000000000000000000000..ebd76a048d53469dd296d0c6a2662ef0612abe98 --- /dev/null +++ b/patches/imx_drm/0009-imx-drm-add-imx6-DT-configuration-for-HDMI.patch @@ -0,0 +1,66 @@ +From ef200c537fec1c55b3bccdbc5906fd5d953b8e5d Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:22 +0000 +Subject: [PATCH 09/34] imx-drm: add imx6 DT configuration for HDMI + +Extracted from another patch by Fabio Estevam, this adds the DT +configuration for HDMI output on the IMX6 SoCs + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + arch/arm/boot/dts/imx6dl.dtsi | 4 ++++ + arch/arm/boot/dts/imx6q.dtsi | 4 ++++ + arch/arm/boot/dts/imx6qdl.dtsi | 10 ++++++++++ + 3 files changed, 18 insertions(+) + +diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi +index 9c4942f..818bed3 100644 +--- a/arch/arm/boot/dts/imx6dl.dtsi ++++ b/arch/arm/boot/dts/imx6dl.dtsi +@@ -109,3 +109,7 @@ + crtcs = <&ipu1 0>, <&ipu1 1>; + }; + }; ++ ++&hdmi { ++ crtcs = <&ipu1 0>, <&ipu1 1>; ++} +diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi +index fadf498..0ebfea7 100644 +--- a/arch/arm/boot/dts/imx6q.dtsi ++++ b/arch/arm/boot/dts/imx6q.dtsi +@@ -174,3 +174,7 @@ + crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; + }; + }; ++ ++&hdmi { ++ crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; ++}; +diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi +index 947e463..3a4f36f 100644 +--- a/arch/arm/boot/dts/imx6qdl.dtsi ++++ b/arch/arm/boot/dts/imx6qdl.dtsi +@@ -674,6 +674,16 @@ + }; + }; + ++ hdmi: hdmi@0120000 { ++ compatible = "fsl,imx6q-hdmi"; ++ reg = <0x00120000 0x9000>; ++ interrupts = <0 115 0x04>; ++ gpr = <&gpr>; ++ clocks = <&clks 123>, <&clks 124>; ++ clock-names = "iahb", "isfr"; ++ status = "disabled"; ++ }; ++ + dcic1: dcic@020e4000 { + reg = <0x020e4000 0x4000>; + interrupts = <0 124 IRQ_TYPE_LEVEL_HIGH>; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0010-imx-drm-update-and-fix-imx6-DT-descriptions-for-v3-H.patch b/patches/imx_drm/0010-imx-drm-update-and-fix-imx6-DT-descriptions-for-v3-H.patch new file mode 100644 index 0000000000000000000000000000000000000000..ea52e07a656cc69e91fb860da8e809109ef35225 --- /dev/null +++ b/patches/imx_drm/0010-imx-drm-update-and-fix-imx6-DT-descriptions-for-v3-H.patch @@ -0,0 +1,54 @@ +From 532291100b7fb6dead5a328feed1195ed4d241a2 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:27 +0000 +Subject: [PATCH 10/34] imx-drm: update and fix imx6 DT descriptions for v3 + HDMI driver + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + arch/arm/boot/dts/imx6dl.dtsi | 3 ++- + arch/arm/boot/dts/imx6q.dtsi | 1 + + arch/arm/boot/dts/imx6qdl.dtsi | 1 - + 3 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi +index 65e54b4..6dc3970 100644 +--- a/arch/arm/boot/dts/imx6dl.dtsi ++++ b/arch/arm/boot/dts/imx6dl.dtsi +@@ -90,5 +90,6 @@ + }; + + &hdmi { ++ compatible = "fsl,imx6dl-hdmi"; + crtcs = <&ipu1 0>, <&ipu1 1>; +-} ++}; +diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi +index d2467f5..187fe33 100644 +--- a/arch/arm/boot/dts/imx6q.dtsi ++++ b/arch/arm/boot/dts/imx6q.dtsi +@@ -161,5 +161,6 @@ + }; + + &hdmi { ++ compatible = "fsl,imx6q-hdmi"; + crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; + }; +diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi +index 400bbc6..930ebe0 100644 +--- a/arch/arm/boot/dts/imx6qdl.dtsi ++++ b/arch/arm/boot/dts/imx6qdl.dtsi +@@ -1369,7 +1369,6 @@ + }; + + hdmi: hdmi@0120000 { +- compatible = "fsl,imx6q-hdmi"; + reg = <0x00120000 0x9000>; + interrupts = <0 115 0x04>; + gpr = <&gpr>; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0011-imx-drm-imx-drm-core-sanitise-imx_drm_encoder_get_mu.patch b/patches/imx_drm/0011-imx-drm-imx-drm-core-sanitise-imx_drm_encoder_get_mu.patch new file mode 100644 index 0000000000000000000000000000000000000000..00a17dc4e5c6eadd37e42fcfe5efb44c291e0cff --- /dev/null +++ b/patches/imx_drm/0011-imx-drm-imx-drm-core-sanitise-imx_drm_encoder_get_mu.patch @@ -0,0 +1,135 @@ +From fc39343383c52f090d410a7e3ac03c47387e3993 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:32 +0000 +Subject: [PATCH 11/34] imx-drm: imx-drm-core: sanitise + imx_drm_encoder_get_mux_id() + +Address the following issues: +- imx_drm_encoder_get_mux_id() searches the CRTC list for the matching + CRTC, and returns the position within this list as the MUX programming + value for encoders. This is sub-optimal for two reasons: + 1. It relies upon the CRTC list not changing during the lifetime of + the driver. + 2. It is dependent on the initialisation order of the CRTCs. + + We address (1) in this patch, leaving (2) until a better solution can + be found, as (2) requires larger changes. + +- imx_drm_encoder is unused. Instead, pass the drm_encoder which is + slightly more useful; all callers pass encoder->crtc as the required + crtc, so move this inside the function. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 17 ++++++----------- + drivers/staging/imx-drm/imx-drm.h | 3 +-- + drivers/staging/imx-drm/imx-hdmi.c | 3 +-- + drivers/staging/imx-drm/imx-ldb.c | 6 ++---- + 4 files changed, 10 insertions(+), 19 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 236ed66..92fde89 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -53,6 +53,7 @@ struct imx_drm_crtc { + struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; + struct module *owner; + struct crtc_cookie cookie; ++ int mux_id; + }; + + struct imx_drm_encoder { +@@ -503,7 +504,7 @@ int imx_drm_add_crtc(struct drm_crtc *crtc, + imx_drm_crtc->pipe = imxdrm->pipes++; + imx_drm_crtc->cookie.cookie = cookie; + imx_drm_crtc->cookie.id = id; +- ++ imx_drm_crtc->mux_id = imx_drm_crtc->pipe; + imx_drm_crtc->crtc = crtc; + imx_drm_crtc->imxdrm = imxdrm; + +@@ -657,22 +658,16 @@ int imx_drm_encoder_add_possible_crtcs( + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_add_possible_crtcs); + +-int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder, +- struct drm_crtc *crtc) ++int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder) + { + struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_crtc *imx_crtc; +- int i = 0; + +- list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) { +- if (imx_crtc->crtc == crtc) +- goto found; +- i++; +- } ++ list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) ++ if (imx_crtc->crtc == encoder->crtc) ++ return imx_crtc->mux_id; + + return -EINVAL; +-found: +- return i; + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); + +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index ae90c9c..5649f18 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -64,8 +64,7 @@ void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper); + + struct device_node; + +-int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder, +- struct drm_crtc *crtc); ++int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder); + int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder, + struct device_node *np); + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 05cf8a0..a90f08d 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -1467,8 +1467,7 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder) + static void imx_hdmi_encoder_commit(struct drm_encoder *encoder) + { + struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); +- int mux = imx_drm_encoder_get_mux_id(hdmi->imx_drm_encoder, +- encoder->crtc); ++ int mux = imx_drm_encoder_get_mux_id(encoder); + + imx_hdmi_set_ipu_di_mux(hdmi, mux); + +diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c +index 7e59329..4aa47ae 100644 +--- a/drivers/staging/imx-drm/imx-ldb.c ++++ b/drivers/staging/imx-drm/imx-ldb.c +@@ -179,8 +179,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) + 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->imx_drm_encoder, +- encoder->crtc); ++ int mux = imx_drm_encoder_get_mux_id(encoder); + + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { + /* dual channel LVDS mode */ +@@ -216,8 +215,7 @@ 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->imx_drm_encoder, +- encoder->crtc); ++ int mux = imx_drm_encoder_get_mux_id(encoder); + + if (dual) { + clk_prepare_enable(ldb->clk[0]); +-- +1.8.5.3 + diff --git a/patches/imx_drm/0012-imx-drm-imx-drm-core-use-array-instead-of-list-for-C.patch b/patches/imx_drm/0012-imx-drm-imx-drm-core-use-array-instead-of-list-for-C.patch new file mode 100644 index 0000000000000000000000000000000000000000..56ffa0bad0402a433860775b82b304ce1de0c700 --- /dev/null +++ b/patches/imx_drm/0012-imx-drm-imx-drm-core-use-array-instead-of-list-for-C.patch @@ -0,0 +1,177 @@ +From d7c4ec1acf1402d1937298c31fcfe126202f2ae7 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:38 +0000 +Subject: [PATCH 12/34] imx-drm: imx-drm-core: use array instead of list for + CRTCs + +The DRM core indexes vblank by number, so there's little point +maintaining a list, and have to scan the list to find the appropriate +structure. Instead, use an array of pointers to the CRTCs. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 57 ++++++++++++++-------------------- + 1 file changed, 23 insertions(+), 34 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 92fde89..c526884 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -34,10 +34,12 @@ struct crtc_cookie { + struct list_head list; + }; + ++struct imx_drm_crtc; ++ + struct imx_drm_device { + struct drm_device *drm; + struct device *dev; +- struct list_head crtc_list; ++ struct imx_drm_crtc *crtc[MAX_CRTC]; + struct list_head encoder_list; + struct list_head connector_list; + struct mutex mutex; +@@ -47,7 +49,6 @@ struct imx_drm_device { + + struct imx_drm_crtc { + struct drm_crtc *crtc; +- struct list_head list; + struct imx_drm_device *imxdrm; + int pipe; + struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; +@@ -69,6 +70,8 @@ struct imx_drm_connector { + struct module *owner; + }; + ++static struct imx_drm_device *__imx_drm_device(void); ++ + int imx_drm_crtc_id(struct imx_drm_crtc *crtc) + { + return crtc->pipe; +@@ -96,34 +99,28 @@ static int imx_drm_driver_unload(struct drm_device *drm) + return 0; + } + +-/* +- * We don't care at all for crtc numbers, but the core expects the +- * crtcs to be numbered +- */ +-static struct imx_drm_crtc *imx_drm_crtc_by_num(struct imx_drm_device *imxdrm, +- int num) ++struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) + { +- struct imx_drm_crtc *imx_drm_crtc; ++ struct imx_drm_device *imxdrm = __imx_drm_device(); ++ unsigned i; ++ ++ for (i = 0; i < MAX_CRTC; i++) ++ if (imxdrm->crtc[i] && imxdrm->crtc[i]->crtc == crtc) ++ return imxdrm->crtc[i]; + +- list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list) +- if (imx_drm_crtc->pipe == num) +- return imx_drm_crtc; + return NULL; + } + + int imx_drm_crtc_panel_format_pins(struct drm_crtc *crtc, u32 encoder_type, + u32 interface_pix_fmt, int hsync_pin, int vsync_pin) + { +- struct imx_drm_device *imxdrm = crtc->dev->dev_private; +- struct imx_drm_crtc *imx_crtc; + struct imx_drm_crtc_helper_funcs *helper; ++ struct imx_drm_crtc *imx_crtc; + +- list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) +- if (imx_crtc->crtc == crtc) +- goto found; ++ imx_crtc = imx_drm_find_crtc(crtc); ++ if (!imx_crtc) ++ return -EINVAL; + +- return -EINVAL; +-found: + helper = &imx_crtc->imx_drm_helper_funcs; + if (helper->set_interface_pix_fmt) + return helper->set_interface_pix_fmt(crtc, +@@ -162,10 +159,9 @@ EXPORT_SYMBOL_GPL(imx_drm_handle_vblank); + static int imx_drm_enable_vblank(struct drm_device *drm, int crtc) + { + struct imx_drm_device *imxdrm = drm->dev_private; +- struct imx_drm_crtc *imx_drm_crtc; ++ struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; + int ret; + +- imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc); + if (!imx_drm_crtc) + return -EINVAL; + +@@ -181,9 +177,8 @@ static int imx_drm_enable_vblank(struct drm_device *drm, int crtc) + static void imx_drm_disable_vblank(struct drm_device *drm, int crtc) + { + struct imx_drm_device *imxdrm = drm->dev_private; +- struct imx_drm_crtc *imx_drm_crtc; ++ struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; + +- imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc); + if (!imx_drm_crtc) + return; + +@@ -510,7 +505,7 @@ int imx_drm_add_crtc(struct drm_crtc *crtc, + + imx_drm_crtc->owner = owner; + +- list_add_tail(&imx_drm_crtc->list, &imxdrm->crtc_list); ++ imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; + + *new_crtc = imx_drm_crtc; + +@@ -533,7 +528,7 @@ int imx_drm_add_crtc(struct drm_crtc *crtc, + return 0; + + err_register: +- list_del(&imx_drm_crtc->list); ++ imxdrm->crtc[imx_drm_crtc->pipe] = NULL; + kfree(imx_drm_crtc); + err_alloc: + err_busy: +@@ -553,7 +548,7 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) + + drm_crtc_cleanup(imx_drm_crtc->crtc); + +- list_del(&imx_drm_crtc->list); ++ imxdrm->crtc[imx_drm_crtc->pipe] = NULL; + + drm_mode_group_reinit(imxdrm->drm); + +@@ -660,14 +655,9 @@ EXPORT_SYMBOL_GPL(imx_drm_encoder_add_possible_crtcs); + + int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder) + { +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct imx_drm_crtc *imx_crtc; +- +- list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) +- if (imx_crtc->crtc == encoder->crtc) +- return imx_crtc->mux_id; ++ struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc); + +- return -EINVAL; ++ return imx_crtc ? imx_crtc->mux_id : -EINVAL; + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); + +@@ -854,7 +844,6 @@ static int __init imx_drm_init(void) + return -ENOMEM; + + mutex_init(&imx_drm_device->mutex); +- INIT_LIST_HEAD(&imx_drm_device->crtc_list); + INIT_LIST_HEAD(&imx_drm_device->connector_list); + INIT_LIST_HEAD(&imx_drm_device->encoder_list); + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0013-imx-drm-provide-common-connector-mode-validation-fun.patch b/patches/imx_drm/0013-imx-drm-provide-common-connector-mode-validation-fun.patch new file mode 100644 index 0000000000000000000000000000000000000000..1d00f9d118810e9e98fb202629315cbe3629ccda --- /dev/null +++ b/patches/imx_drm/0013-imx-drm-provide-common-connector-mode-validation-fun.patch @@ -0,0 +1,151 @@ +From ddcbc6f2846cb20086a525f169921855473395bd Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:43 +0000 +Subject: [PATCH 13/34] imx-drm: provide common connector mode validation + function + +Provide a common connector mode validation function, which can be used +to limit the available modes according to other components in the +system. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 7 +++++++ + drivers/staging/imx-drm/imx-drm.h | 3 +++ + drivers/staging/imx-drm/imx-hdmi.c | 9 +-------- + drivers/staging/imx-drm/imx-ldb.c | 8 +------- + drivers/staging/imx-drm/imx-tve.c | 5 +++++ + drivers/staging/imx-drm/parallel-display.c | 8 +------- + 6 files changed, 18 insertions(+), 22 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index c526884..7f14ed0 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -211,6 +211,13 @@ static const struct file_operations imx_drm_driver_fops = { + .llseek = noop_llseek, + }; + ++int imx_drm_connector_mode_valid(struct drm_connector *connector, ++ struct drm_display_mode *mode) ++{ ++ return MODE_OK; ++} ++EXPORT_SYMBOL(imx_drm_connector_mode_valid); ++ + static struct imx_drm_device *imx_drm_device; + + static struct imx_drm_device *__imx_drm_device(void) +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index 5649f18..4eb594c 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -68,4 +68,7 @@ int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder); + int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder, + struct device_node *np); + ++int imx_drm_connector_mode_valid(struct drm_connector *connector, ++ struct drm_display_mode *mode); ++ + #endif /* _IMX_DRM_H_ */ +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index a90f08d..4b69045 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -1406,13 +1406,6 @@ static int imx_hdmi_connector_get_modes(struct drm_connector *connector) + return 0; + } + +-static int imx_hdmi_connector_mode_valid(struct drm_connector *connector, +- struct drm_display_mode *mode) +-{ +- +- return MODE_OK; +-} +- + static struct drm_encoder *imx_hdmi_connector_best_encoder(struct drm_connector + *connector) + { +@@ -1501,7 +1494,7 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = { + + static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { + .get_modes = imx_hdmi_connector_get_modes, +- .mode_valid = imx_hdmi_connector_mode_valid, ++ .mode_valid = imx_drm_connector_mode_valid, + .best_encoder = imx_hdmi_connector_best_encoder, + }; + +diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c +index 4aa47ae..c6ec1e9 100644 +--- a/drivers/staging/imx-drm/imx-ldb.c ++++ b/drivers/staging/imx-drm/imx-ldb.c +@@ -120,12 +120,6 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector) + return num_modes; + } + +-static int imx_ldb_connector_mode_valid(struct drm_connector *connector, +- struct drm_display_mode *mode) +-{ +- return 0; +-} +- + static struct drm_encoder *imx_ldb_connector_best_encoder( + struct drm_connector *connector) + { +@@ -329,7 +323,7 @@ static struct drm_connector_funcs imx_ldb_connector_funcs = { + 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, +- .mode_valid = imx_ldb_connector_mode_valid, ++ .mode_valid = imx_drm_connector_mode_valid, + }; + + static struct drm_encoder_funcs imx_ldb_encoder_funcs = { +diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c +index 9abc7ca..2d4e097 100644 +--- a/drivers/staging/imx-drm/imx-tve.c ++++ b/drivers/staging/imx-drm/imx-tve.c +@@ -254,6 +254,11 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector, + { + struct imx_tve *tve = con_to_tve(connector); + unsigned long rate; ++ int ret; ++ ++ ret = imx_drm_connector_mode_valid(connector, mode); ++ if (ret != MODE_OK) ++ return ret; + + /* pixel clock with 2x oversampling */ + rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000; +diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c +index 351d61d..18a3e8a 100644 +--- a/drivers/staging/imx-drm/parallel-display.c ++++ b/drivers/staging/imx-drm/parallel-display.c +@@ -85,12 +85,6 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) + return num_modes; + } + +-static int imx_pd_connector_mode_valid(struct drm_connector *connector, +- struct drm_display_mode *mode) +-{ +- return 0; +-} +- + static struct drm_encoder *imx_pd_connector_best_encoder( + struct drm_connector *connector) + { +@@ -147,7 +141,7 @@ static struct drm_connector_funcs imx_pd_connector_funcs = { + static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { + .get_modes = imx_pd_connector_get_modes, + .best_encoder = imx_pd_connector_best_encoder, +- .mode_valid = imx_pd_connector_mode_valid, ++ .mode_valid = imx_drm_connector_mode_valid, + }; + + static struct drm_encoder_funcs imx_pd_encoder_funcs = { +-- +1.8.5.3 + diff --git a/patches/imx_drm/0014-imx-drm-simplify-setup-of-panel-format.patch b/patches/imx_drm/0014-imx-drm-simplify-setup-of-panel-format.patch new file mode 100644 index 0000000000000000000000000000000000000000..bc579defd1a764eb48c2b84ba87a42c621ecd14c --- /dev/null +++ b/patches/imx_drm/0014-imx-drm-simplify-setup-of-panel-format.patch @@ -0,0 +1,183 @@ +From 0ad8680c797cb91de91436e7b37afdfc1ecdb6a4 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:48 +0000 +Subject: [PATCH 14/34] imx-drm: simplify setup of panel format + +The encoder format passed into imx_drm_crtc_panel_format*() is the +encoder format used for DRM in most cases; the HDMI encoder sets +this to none, but this is incorrect, it should be TMDS. + +Since this is the case, we can pass the drm_encoder structure +directly into this function and use the supplied fields there to +configure the CRTC. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 18 ++++++++---------- + drivers/staging/imx-drm/imx-drm.h | 4 ++-- + drivers/staging/imx-drm/imx-hdmi.c | 3 +-- + drivers/staging/imx-drm/imx-ldb.c | 3 +-- + drivers/staging/imx-drm/imx-tve.c | 12 +++++++----- + drivers/staging/imx-drm/ipuv3-crtc.c | 1 + + drivers/staging/imx-drm/parallel-display.c | 3 +-- + 7 files changed, 21 insertions(+), 23 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 7f14ed0..d9786ec 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -111,32 +111,30 @@ struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) + return NULL; + } + +-int imx_drm_crtc_panel_format_pins(struct drm_crtc *crtc, u32 encoder_type, ++int imx_drm_panel_format_pins(struct drm_encoder *encoder, + u32 interface_pix_fmt, int hsync_pin, int vsync_pin) + { + struct imx_drm_crtc_helper_funcs *helper; + struct imx_drm_crtc *imx_crtc; + +- imx_crtc = imx_drm_find_crtc(crtc); ++ imx_crtc = imx_drm_find_crtc(encoder->crtc); + if (!imx_crtc) + return -EINVAL; + + helper = &imx_crtc->imx_drm_helper_funcs; + if (helper->set_interface_pix_fmt) +- return helper->set_interface_pix_fmt(crtc, +- encoder_type, interface_pix_fmt, ++ return helper->set_interface_pix_fmt(encoder->crtc, ++ encoder->encoder_type, interface_pix_fmt, + hsync_pin, vsync_pin); + return 0; + } +-EXPORT_SYMBOL_GPL(imx_drm_crtc_panel_format_pins); ++EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); + +-int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type, +- u32 interface_pix_fmt) ++int imx_drm_panel_format(struct drm_encoder *encoder, u32 interface_pix_fmt) + { +- return imx_drm_crtc_panel_format_pins(crtc, encoder_type, +- interface_pix_fmt, 2, 3); ++ return imx_drm_panel_format_pins(encoder, interface_pix_fmt, 2, 3); + } +-EXPORT_SYMBOL_GPL(imx_drm_crtc_panel_format); ++EXPORT_SYMBOL_GPL(imx_drm_panel_format); + + int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc) + { +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index 4eb594c..e3ca0c6 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -56,9 +56,9 @@ struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); + + struct drm_device *imx_drm_device_get(void); + void imx_drm_device_put(void); +-int imx_drm_crtc_panel_format_pins(struct drm_crtc *crtc, u32 encoder_type, ++int imx_drm_panel_format_pins(struct drm_encoder *encoder, + u32 interface_pix_fmt, int hsync_pin, int vsync_pin); +-int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type, ++int imx_drm_panel_format(struct drm_encoder *encoder, + u32 interface_pix_fmt); + void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper); + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 4b69045..50475e6 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -1453,8 +1453,7 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder) + struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); + + imx_hdmi_poweroff(hdmi); +- imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_NONE, +- V4L2_PIX_FMT_RGB24); ++ imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24); + } + + static void imx_hdmi_encoder_commit(struct drm_encoder *encoder) +diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c +index c6ec1e9..dd29a4a 100644 +--- a/drivers/staging/imx-drm/imx-ldb.c ++++ b/drivers/staging/imx-drm/imx-ldb.c +@@ -200,8 +200,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) + pixel_fmt = V4L2_PIX_FMT_RGB24; + } + +- imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_LVDS, +- pixel_fmt); ++ imx_drm_panel_format(encoder, pixel_fmt); + } + + static void imx_ldb_encoder_commit(struct drm_encoder *encoder) +diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c +index 2d4e097..77131e5 100644 +--- a/drivers/staging/imx-drm/imx-tve.c ++++ b/drivers/staging/imx-drm/imx-tve.c +@@ -310,13 +310,11 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder) + + switch (tve->mode) { + case TVE_MODE_VGA: +- imx_drm_crtc_panel_format_pins(encoder->crtc, +- DRM_MODE_ENCODER_DAC, IPU_PIX_FMT_GBR24, ++ imx_drm_panel_format_pins(encoder, IPU_PIX_FMT_GBR24, + tve->hsync_pin, tve->vsync_pin); + break; + case TVE_MODE_TVOUT: +- imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_TVDAC, +- V4L2_PIX_FMT_YUV444); ++ imx_drm_panel_format(encoder, V4L2_PIX_FMT_YUV444); + break; + } + } +@@ -510,12 +508,16 @@ static int tve_clk_init(struct imx_tve *tve, void __iomem *base) + + static int imx_tve_register(struct imx_tve *tve) + { ++ int encoder_type; + int ret; + ++ encoder_type = tve->mode == TVE_MODE_VGA ? ++ DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC; ++ + tve->connector.funcs = &imx_tve_connector_funcs; + tve->encoder.funcs = &imx_tve_encoder_funcs; + +- tve->encoder.encoder_type = DRM_MODE_ENCODER_NONE; ++ tve->encoder.encoder_type = encoder_type; + tve->connector.connector_type = DRM_MODE_CONNECTOR_VGA; + + drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs); +diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c +index 22be104..08e0a3b 100644 +--- a/drivers/staging/imx-drm/ipuv3-crtc.c ++++ b/drivers/staging/imx-drm/ipuv3-crtc.c +@@ -284,6 +284,7 @@ static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, + 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; +diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c +index 18a3e8a..12bcf4f 100644 +--- a/drivers/staging/imx-drm/parallel-display.c ++++ b/drivers/staging/imx-drm/parallel-display.c +@@ -108,8 +108,7 @@ static void imx_pd_encoder_prepare(struct drm_encoder *encoder) + { + struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); + +- imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_NONE, +- imxpd->interface_pix_fmt); ++ imx_drm_panel_format(encoder, imxpd->interface_pix_fmt); + } + + static void imx_pd_encoder_commit(struct drm_encoder *encoder) +-- +1.8.5.3 + diff --git a/patches/imx_drm/0015-imx-drm-convert-to-componentised-device-support.patch b/patches/imx_drm/0015-imx-drm-convert-to-componentised-device-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..a42598d47ed910b92302ef4b0a864c74a023cb92 --- /dev/null +++ b/patches/imx_drm/0015-imx-drm-convert-to-componentised-device-support.patch @@ -0,0 +1,823 @@ +From 91e42a3b42ed03dfe1245bbabd6fc89e9e72418b Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:53 +0000 +Subject: [PATCH 15/34] imx-drm: convert to componentised device support + +Use the componentised device support for imx-drm. This requires all +the sub-components and the master device to register with the component +device support. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + arch/arm/boot/dts/imx51-babbage.dts | 10 ++- + arch/arm/boot/dts/imx53-m53evk.dts | 8 ++- + arch/arm/boot/dts/imx53-mba53.dts | 6 ++ + arch/arm/boot/dts/imx53-qsb-common.dtsi | 8 ++- + arch/arm/boot/dts/imx6q-sabresd.dts | 4 ++ + arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 6 ++ + drivers/staging/imx-drm/imx-drm-core.c | 105 +++++++++++++++++++++++------ + drivers/staging/imx-drm/imx-ldb.c | 40 +++++++---- + drivers/staging/imx-drm/imx-tve.c | 63 ++++++++++------- + drivers/staging/imx-drm/ipuv3-crtc.c | 46 +++++++++---- + drivers/staging/imx-drm/parallel-display.c | 30 +++++++-- + 11 files changed, 246 insertions(+), 80 deletions(-) + +diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts +index 121dadd..ebe6c1d 100644 +--- a/arch/arm/boot/dts/imx51-babbage.dts ++++ b/arch/arm/boot/dts/imx51-babbage.dts +@@ -21,7 +21,7 @@ + reg = <0x90000000 0x20000000>; + }; + +- display@di0 { ++ display0: display@di0 { + compatible = "fsl,imx-parallel-display"; + crtcs = <&ipu 0>; + interface-pix-fmt = "rgb24"; +@@ -43,7 +43,7 @@ + }; + }; + +- display@di1 { ++ display1: display@di1 { + compatible = "fsl,imx-parallel-display"; + crtcs = <&ipu 1>; + interface-pix-fmt = "rgb565"; +@@ -92,6 +92,12 @@ + }; + }; + ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ crtcs = <&ipu 0>, <&ipu 1>; ++ connectors = <&display0>, <&display1>; ++ }; ++ + sound { + compatible = "fsl,imx51-babbage-sgtl5000", + "fsl,imx-audio-sgtl5000"; +diff --git a/arch/arm/boot/dts/imx53-m53evk.dts b/arch/arm/boot/dts/imx53-m53evk.dts +index e8d11e2..d939ba8 100644 +--- a/arch/arm/boot/dts/imx53-m53evk.dts ++++ b/arch/arm/boot/dts/imx53-m53evk.dts +@@ -21,7 +21,7 @@ + }; + + soc { +- display@di1 { ++ display1: display@di1 { + compatible = "fsl,imx-parallel-display"; + crtcs = <&ipu 1>; + interface-pix-fmt = "bgr666"; +@@ -54,6 +54,12 @@ + power-supply = <®_backlight>; + }; + ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ crtcs = <&ipu 1>; ++ connectors = <&display1>; ++ }; ++ + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; +diff --git a/arch/arm/boot/dts/imx53-mba53.dts b/arch/arm/boot/dts/imx53-mba53.dts +index 55af110..a398745 100644 +--- a/arch/arm/boot/dts/imx53-mba53.dts ++++ b/arch/arm/boot/dts/imx53-mba53.dts +@@ -35,6 +35,12 @@ + status = "disabled"; + }; + ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ crtcs = <&ipu 1>; ++ connectors = <&disp1>, <&tve>; ++ }; ++ + regulators { + compatible = "simple-bus"; + #address-cells = <1>; +diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi +index 2dca98b..50823d0 100644 +--- a/arch/arm/boot/dts/imx53-qsb-common.dtsi ++++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi +@@ -17,7 +17,7 @@ + reg = <0x70000000 0x40000000>; + }; + +- display@di0 { ++ display0: display@di0 { + compatible = "fsl,imx-parallel-display"; + crtcs = <&ipu 0>; + interface-pix-fmt = "rgb565"; +@@ -68,6 +68,12 @@ + }; + }; + ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ crtcs = <&ipu 0>; ++ connectors = <&display0>; ++ }; ++ + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; +diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts +index 9cbdfe7..66f220a 100644 +--- a/arch/arm/boot/dts/imx6q-sabresd.dts ++++ b/arch/arm/boot/dts/imx6q-sabresd.dts +@@ -20,6 +20,10 @@ + compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; + }; + ++&imx_drm { ++ crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; ++}; ++ + &sata { + status = "okay"; + }; +diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +index 0d816d3..04487cb 100644 +--- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi ++++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +@@ -79,6 +79,12 @@ + }; + }; + ++ imx_drm: imx-drm { ++ compatible = "fsl,imx-drm"; ++ crtcs = <&ipu1 0>, <&ipu1 1>; ++ connectors = <&ldb>; ++ }; ++ + sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index d9786ec..82b0337 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -13,7 +13,7 @@ + * GNU General Public License for more details. + * + */ +- ++#include <linux/component.h> + #include <linux/device.h> + #include <linux/platform_device.h> + #include <drm/drmP.h> +@@ -90,6 +90,8 @@ static int imx_drm_driver_unload(struct drm_device *drm) + { + struct imx_drm_device *imxdrm = drm->dev_private; + ++ component_unbind_all(drm->dev, drm); ++ + imx_drm_device_put(); + + drm_vblank_cleanup(drm); +@@ -371,11 +373,8 @@ static void imx_drm_connector_unregister( + } + + /* +- * Called by the CRTC driver when all CRTCs are registered. This +- * puts all the pieces together and initializes the driver. +- * Once this is called no more CRTCs can be registered since +- * the drm core has hardcoded the number of crtcs in several +- * places. ++ * Main DRM initialisation. This binds, initialises and registers ++ * with DRM the subcomponents of the driver. + */ + static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + { +@@ -428,8 +427,15 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + + platform_set_drvdata(drm->platformdev, drm); + mutex_unlock(&imxdrm->mutex); ++ ++ /* Now try and bind all our sub-components */ ++ ret = component_bind_all(drm->dev, drm); ++ if (ret) ++ goto err_relock; + return 0; + ++err_relock: ++ mutex_lock(&imxdrm->mutex); + err_vblank: + drm_vblank_cleanup(drm); + err_kms: +@@ -809,6 +815,70 @@ static struct drm_driver imx_drm_driver = { + .patchlevel = 0, + }; + ++static int compare_parent_of(struct device *dev, void *data) ++{ ++ struct of_phandle_args *args = data; ++ return dev->parent && dev->parent->of_node == args->np; ++} ++ ++static int compare_of(struct device *dev, void *data) ++{ ++ return dev->of_node == data; ++} ++ ++static int imx_drm_add_components(struct device *master, struct master *m) ++{ ++ struct device_node *np = master->of_node; ++ unsigned i; ++ int ret; ++ ++ for (i = 0; ; i++) { ++ struct of_phandle_args args; ++ ++ ret = of_parse_phandle_with_fixed_args(np, "crtcs", 1, ++ i, &args); ++ if (ret) ++ break; ++ ++ ret = component_master_add_child(m, compare_parent_of, &args); ++ of_node_put(args.np); ++ ++ if (ret) ++ return ret; ++ } ++ ++ for (i = 0; ; i++) { ++ struct device_node *node; ++ ++ node = of_parse_phandle(np, "connectors", i); ++ if (!node) ++ break; ++ ++ ret = component_master_add_child(m, compare_of, node); ++ of_node_put(node); ++ ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static int imx_drm_bind(struct device *dev) ++{ ++ return drm_platform_init(&imx_drm_driver, to_platform_device(dev)); ++} ++ ++static void imx_drm_unbind(struct device *dev) ++{ ++ drm_put_dev(dev_get_drvdata(dev)); ++} ++ ++static const struct component_master_ops imx_drm_ops = { ++ .add_components = imx_drm_add_components, ++ .bind = imx_drm_bind, ++ .unbind = imx_drm_unbind, ++}; ++ + static int imx_drm_platform_probe(struct platform_device *pdev) + { + int ret; +@@ -819,27 +889,31 @@ static int imx_drm_platform_probe(struct platform_device *pdev) + + imx_drm_device->dev = &pdev->dev; + +- return drm_platform_init(&imx_drm_driver, pdev); ++ return component_master_add(&pdev->dev, &imx_drm_ops); + } + + static int imx_drm_platform_remove(struct platform_device *pdev) + { +- drm_put_dev(platform_get_drvdata(pdev)); +- ++ component_master_del(&pdev->dev, &imx_drm_ops); + return 0; + } + ++static const struct of_device_id imx_drm_dt_ids[] = { ++ { .compatible = "fsl,imx-drm", }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, imx_drm_dt_ids); ++ + static struct platform_driver imx_drm_pdrv = { + .probe = imx_drm_platform_probe, + .remove = imx_drm_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = "imx-drm", ++ .of_match_table = imx_drm_dt_ids, + }, + }; + +-static struct platform_device *imx_drm_pdev; +- + static int __init imx_drm_init(void) + { + int ret; +@@ -852,12 +926,6 @@ static int __init imx_drm_init(void) + INIT_LIST_HEAD(&imx_drm_device->connector_list); + INIT_LIST_HEAD(&imx_drm_device->encoder_list); + +- imx_drm_pdev = platform_device_register_simple("imx-drm", -1, NULL, 0); +- if (IS_ERR(imx_drm_pdev)) { +- ret = PTR_ERR(imx_drm_pdev); +- goto err_pdev; +- } +- + ret = platform_driver_register(&imx_drm_pdrv); + if (ret) + goto err_pdrv; +@@ -865,8 +933,6 @@ static int __init imx_drm_init(void) + return 0; + + err_pdrv: +- platform_device_unregister(imx_drm_pdev); +-err_pdev: + kfree(imx_drm_device); + + return ret; +@@ -874,7 +940,6 @@ err_pdev: + + static void __exit imx_drm_exit(void) + { +- platform_device_unregister(imx_drm_pdev); + platform_driver_unregister(&imx_drm_pdrv); + + kfree(imx_drm_device); +diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c +index dd29a4a..d00f93f 100644 +--- a/drivers/staging/imx-drm/imx-ldb.c ++++ b/drivers/staging/imx-drm/imx-ldb.c +@@ -20,6 +20,7 @@ + + #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> +@@ -450,11 +451,11 @@ static const struct of_device_id imx_ldb_dt_ids[] = { + }; + MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids); + +-static int imx_ldb_probe(struct platform_device *pdev) ++static int imx_ldb_bind(struct device *dev, struct device *master, void *data) + { +- struct device_node *np = pdev->dev.of_node; ++ struct device_node *np = dev->of_node; + const struct of_device_id *of_id = +- of_match_device(imx_ldb_dt_ids, &pdev->dev); ++ of_match_device(imx_ldb_dt_ids, dev); + struct device_node *child; + const u8 *edidp; + struct imx_ldb *imx_ldb; +@@ -464,17 +465,17 @@ static int imx_ldb_probe(struct platform_device *pdev) + int ret; + int i; + +- imx_ldb = devm_kzalloc(&pdev->dev, sizeof(*imx_ldb), GFP_KERNEL); ++ 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(&pdev->dev, "failed to get parent regmap\n"); ++ dev_err(dev, "failed to get parent regmap\n"); + return PTR_ERR(imx_ldb->regmap); + } + +- imx_ldb->dev = &pdev->dev; ++ imx_ldb->dev = dev; + + if (of_id) + imx_ldb->lvds_mux = of_id->data; +@@ -512,7 +513,7 @@ static int imx_ldb_probe(struct platform_device *pdev) + return -EINVAL; + + if (dual && i > 0) { +- dev_warn(&pdev->dev, "dual-channel mode, ignoring second output\n"); ++ dev_warn(dev, "dual-channel mode, ignoring second output\n"); + continue; + } + +@@ -551,7 +552,7 @@ static int imx_ldb_probe(struct platform_device *pdev) + break; + case LVDS_BIT_MAP_JEIDA: + if (datawidth == 18) { +- dev_err(&pdev->dev, "JEIDA standard only supported in 24 bit\n"); ++ dev_err(dev, "JEIDA standard only supported in 24 bit\n"); + return -EINVAL; + } + if (i == 0 || dual) +@@ -560,7 +561,7 @@ static int imx_ldb_probe(struct platform_device *pdev) + imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | LDB_BIT_MAP_CH1_JEIDA; + break; + default: +- dev_err(&pdev->dev, "data mapping not specified or invalid\n"); ++ dev_err(dev, "data mapping not specified or invalid\n"); + return -EINVAL; + } + +@@ -571,14 +572,15 @@ static int imx_ldb_probe(struct platform_device *pdev) + imx_drm_encoder_add_possible_crtcs(channel->imx_drm_encoder, child); + } + +- platform_set_drvdata(pdev, imx_ldb); ++ dev_set_drvdata(dev, imx_ldb); + + return 0; + } + +-static int imx_ldb_remove(struct platform_device *pdev) ++static void imx_ldb_unbind(struct device *dev, struct device *master, ++ void *data) + { +- struct imx_ldb *imx_ldb = platform_get_drvdata(pdev); ++ struct imx_ldb *imx_ldb = dev_get_drvdata(dev); + int i; + + for (i = 0; i < 2; i++) { +@@ -591,7 +593,21 @@ static int imx_ldb_remove(struct platform_device *pdev) + imx_drm_remove_connector(channel->imx_drm_connector); + imx_drm_remove_encoder(channel->imx_drm_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; + } + +diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c +index 77131e5..ad840d7 100644 +--- a/drivers/staging/imx-drm/imx-tve.c ++++ b/drivers/staging/imx-drm/imx-tve.c +@@ -20,6 +20,7 @@ + + #include <linux/clk.h> + #include <linux/clk-provider.h> ++#include <linux/component.h> + #include <linux/module.h> + #include <linux/i2c.h> + #include <linux/regmap.h> +@@ -583,9 +584,10 @@ static const int of_get_tve_mode(struct device_node *np) + return -EINVAL; + } + +-static int imx_tve_probe(struct platform_device *pdev) ++static int imx_tve_bind(struct device *dev, struct device *master, void *data) + { +- struct device_node *np = pdev->dev.of_node; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct device_node *np = dev->of_node; + struct device_node *ddc_node; + struct imx_tve *tve; + struct resource *res; +@@ -594,11 +596,11 @@ static int imx_tve_probe(struct platform_device *pdev) + int irq; + int ret; + +- tve = devm_kzalloc(&pdev->dev, sizeof(*tve), GFP_KERNEL); ++ tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL); + if (!tve) + return -ENOMEM; + +- tve->dev = &pdev->dev; ++ tve->dev = dev; + spin_lock_init(&tve->lock); + + ddc_node = of_parse_phandle(np, "ddc", 0); +@@ -609,7 +611,7 @@ static int imx_tve_probe(struct platform_device *pdev) + + tve->mode = of_get_tve_mode(np); + if (tve->mode != TVE_MODE_VGA) { +- dev_err(&pdev->dev, "only VGA mode supported, currently\n"); ++ dev_err(dev, "only VGA mode supported, currently\n"); + return -EINVAL; + } + +@@ -618,7 +620,7 @@ static int imx_tve_probe(struct platform_device *pdev) + &tve->hsync_pin); + + if (ret < 0) { +- dev_err(&pdev->dev, "failed to get vsync pin\n"); ++ dev_err(dev, "failed to get vsync pin\n"); + return ret; + } + +@@ -626,40 +628,40 @@ static int imx_tve_probe(struct platform_device *pdev) + &tve->vsync_pin); + + if (ret < 0) { +- dev_err(&pdev->dev, "failed to get vsync pin\n"); ++ dev_err(dev, "failed to get vsync pin\n"); + return ret; + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(&pdev->dev, res); ++ base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + tve_regmap_config.lock_arg = tve; +- tve->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "tve", base, ++ tve->regmap = devm_regmap_init_mmio_clk(dev, "tve", base, + &tve_regmap_config); + if (IS_ERR(tve->regmap)) { +- dev_err(&pdev->dev, "failed to init regmap: %ld\n", ++ dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(tve->regmap)); + return PTR_ERR(tve->regmap); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { +- dev_err(&pdev->dev, "failed to get irq\n"); ++ dev_err(dev, "failed to get irq\n"); + return irq; + } + +- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, ++ ret = devm_request_threaded_irq(dev, irq, NULL, + imx_tve_irq_handler, IRQF_ONESHOT, + "imx-tve", tve); + if (ret < 0) { +- dev_err(&pdev->dev, "failed to request irq: %d\n", ret); ++ dev_err(dev, "failed to request irq: %d\n", ret); + return ret; + } + +- tve->dac_reg = devm_regulator_get(&pdev->dev, "dac"); ++ tve->dac_reg = devm_regulator_get(dev, "dac"); + if (!IS_ERR(tve->dac_reg)) { + regulator_set_voltage(tve->dac_reg, 2750000, 2750000); + ret = regulator_enable(tve->dac_reg); +@@ -667,17 +669,17 @@ static int imx_tve_probe(struct platform_device *pdev) + return ret; + } + +- tve->clk = devm_clk_get(&pdev->dev, "tve"); ++ tve->clk = devm_clk_get(dev, "tve"); + if (IS_ERR(tve->clk)) { +- dev_err(&pdev->dev, "failed to get high speed tve clock: %ld\n", ++ dev_err(dev, "failed to get high speed tve clock: %ld\n", + PTR_ERR(tve->clk)); + return PTR_ERR(tve->clk); + } + + /* this is the IPU DI clock input selector, can be parented to tve_di */ +- tve->di_sel_clk = devm_clk_get(&pdev->dev, "di_sel"); ++ tve->di_sel_clk = devm_clk_get(dev, "di_sel"); + if (IS_ERR(tve->di_sel_clk)) { +- dev_err(&pdev->dev, "failed to get ipu di mux clock: %ld\n", ++ dev_err(dev, "failed to get ipu di mux clock: %ld\n", + PTR_ERR(tve->di_sel_clk)); + return PTR_ERR(tve->di_sel_clk); + } +@@ -688,11 +690,11 @@ static int imx_tve_probe(struct platform_device *pdev) + + ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val); + if (ret < 0) { +- dev_err(&pdev->dev, "failed to read configuration register: %d\n", ret); ++ dev_err(dev, "failed to read configuration register: %d\n", ret); + return ret; + } + if (val != 0x00100000) { +- dev_err(&pdev->dev, "configuration register default value indicates this is not a TVEv2\n"); ++ dev_err(dev, "configuration register default value indicates this is not a TVEv2\n"); + return -ENODEV; + } + +@@ -705,14 +707,15 @@ static int imx_tve_probe(struct platform_device *pdev) + + ret = imx_drm_encoder_add_possible_crtcs(tve->imx_drm_encoder, np); + +- platform_set_drvdata(pdev, tve); ++ dev_set_drvdata(dev, tve); + + return 0; + } + +-static int imx_tve_remove(struct platform_device *pdev) ++static void imx_tve_unbind(struct device *dev, struct device *master, ++ void *data) + { +- struct imx_tve *tve = platform_get_drvdata(pdev); ++ struct imx_tve *tve = dev_get_drvdata(dev); + struct drm_connector *connector = &tve->connector; + struct drm_encoder *encoder = &tve->encoder; + +@@ -723,7 +726,21 @@ static int imx_tve_remove(struct platform_device *pdev) + + if (!IS_ERR(tve->dac_reg)) + regulator_disable(tve->dac_reg); ++} + ++static const struct component_ops imx_tve_ops = { ++ .bind = imx_tve_bind, ++ .unbind = imx_tve_unbind, ++}; ++ ++static int imx_tve_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &imx_tve_ops); ++} ++ ++static int imx_tve_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &imx_tve_ops); + return 0; + } + +diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c +index 08e0a3b..d779ad2 100644 +--- a/drivers/staging/imx-drm/ipuv3-crtc.c ++++ b/drivers/staging/imx-drm/ipuv3-crtc.c +@@ -17,6 +17,7 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ ++#include <linux/component.h> + #include <linux/module.h> + #include <linux/export.h> + #include <linux/device.h> +@@ -400,43 +401,60 @@ err_put_resources: + return ret; + } + +-static int ipu_drm_probe(struct platform_device *pdev) ++static int ipu_drm_bind(struct device *dev, struct device *master, void *data) + { +- struct ipu_client_platformdata *pdata = pdev->dev.platform_data; ++ struct ipu_client_platformdata *pdata = dev->platform_data; + struct ipu_crtc *ipu_crtc; + int ret; + +- if (!pdata) +- return -EINVAL; +- +- ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); +- if (ret) +- return ret; +- +- ipu_crtc = devm_kzalloc(&pdev->dev, sizeof(*ipu_crtc), GFP_KERNEL); ++ ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL); + if (!ipu_crtc) + return -ENOMEM; + +- ipu_crtc->dev = &pdev->dev; ++ ipu_crtc->dev = dev; + + ret = ipu_crtc_init(ipu_crtc, pdata); + if (ret) + return ret; + +- platform_set_drvdata(pdev, ipu_crtc); ++ dev_set_drvdata(dev, ipu_crtc); + + return 0; + } + +-static int ipu_drm_remove(struct platform_device *pdev) ++static void ipu_drm_unbind(struct device *dev, struct device *master, ++ void *data) + { +- struct ipu_crtc *ipu_crtc = platform_get_drvdata(pdev); ++ struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev); + + imx_drm_remove_crtc(ipu_crtc->imx_crtc); + + ipu_plane_put_resources(ipu_crtc->plane[0]); + ipu_put_resources(ipu_crtc); ++} ++ ++static const struct component_ops ipu_crtc_ops = { ++ .bind = ipu_drm_bind, ++ .unbind = ipu_drm_unbind, ++}; + ++static int ipu_drm_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ if (!pdev->dev.platform_data) ++ return -EINVAL; ++ ++ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ if (ret) ++ return ret; ++ ++ return component_add(&pdev->dev, &ipu_crtc_ops); ++} ++ ++static int ipu_drm_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &ipu_crtc_ops); + return 0; + } + +diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c +index 12bcf4f..4019cae 100644 +--- a/drivers/staging/imx-drm/parallel-display.c ++++ b/drivers/staging/imx-drm/parallel-display.c +@@ -18,6 +18,7 @@ + * MA 02110-1301, USA. + */ + ++#include <linux/component.h> + #include <linux/module.h> + #include <drm/drmP.h> + #include <drm/drm_fb_helper.h> +@@ -192,15 +193,15 @@ static int imx_pd_register(struct imx_parallel_display *imxpd) + return 0; + } + +-static int imx_pd_probe(struct platform_device *pdev) ++static int imx_pd_bind(struct device *dev, struct device *master, void *data) + { +- struct device_node *np = pdev->dev.of_node; ++ struct device_node *np = dev->of_node; + const u8 *edidp; + struct imx_parallel_display *imxpd; + int ret; + const char *fmt; + +- imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL); ++ imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); + if (!imxpd) + return -ENOMEM; + +@@ -218,7 +219,7 @@ static int imx_pd_probe(struct platform_device *pdev) + imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666; + } + +- imxpd->dev = &pdev->dev; ++ imxpd->dev = dev; + + ret = imx_pd_register(imxpd); + if (ret) +@@ -226,14 +227,15 @@ static int imx_pd_probe(struct platform_device *pdev) + + ret = imx_drm_encoder_add_possible_crtcs(imxpd->imx_drm_encoder, np); + +- platform_set_drvdata(pdev, imxpd); ++ dev_set_drvdata(dev, imxpd); + + return 0; + } + +-static int imx_pd_remove(struct platform_device *pdev) ++static void imx_pd_unbind(struct device *dev, struct device *master, ++ void *data) + { +- struct imx_parallel_display *imxpd = platform_get_drvdata(pdev); ++ struct imx_parallel_display *imxpd = dev_get_drvdata(dev); + struct drm_connector *connector = &imxpd->connector; + struct drm_encoder *encoder = &imxpd->encoder; + +@@ -241,7 +243,21 @@ static int imx_pd_remove(struct platform_device *pdev) + + imx_drm_remove_connector(imxpd->imx_drm_connector); + imx_drm_remove_encoder(imxpd->imx_drm_encoder); ++} + ++static const struct component_ops imx_pd_ops = { ++ .bind = imx_pd_bind, ++ .unbind = imx_pd_unbind, ++}; ++ ++static int imx_pd_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &imx_pd_ops); ++} ++ ++static int imx_pd_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &imx_pd_ops); + return 0; + } + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0016-imx-drm-imx-hdmi-convert-to-a-component-device.patch b/patches/imx_drm/0016-imx-drm-imx-hdmi-convert-to-a-component-device.patch new file mode 100644 index 0000000000000000000000000000000000000000..62b9ba1fc5aa0de3a37c8615ffcd8b6ff5496b4f --- /dev/null +++ b/patches/imx_drm/0016-imx-drm-imx-hdmi-convert-to-a-component-device.patch @@ -0,0 +1,129 @@ +From 85adfe924160e949b48227e82a54459f03c58558 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:10:58 +0000 +Subject: [PATCH 16/34] imx-drm: imx-hdmi: convert to a component device + +Convert imx-hdmi to be a component device; it will bind and unbind +at the appropriate moment in the main DRM driver's functions. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 41 +++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 12 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 50475e6..14b4a4b 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -12,6 +12,7 @@ + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + */ + ++#include <linux/component.h> + #include <linux/irq.h> + #include <linux/delay.h> + #include <linux/err.h> +@@ -1582,21 +1583,22 @@ static const struct of_device_id imx_hdmi_dt_ids[] = { + }; + MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids); + +-static int imx_hdmi_platform_probe(struct platform_device *pdev) ++static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + { ++ struct platform_device *pdev = to_platform_device(dev); + const struct of_device_id *of_id = +- of_match_device(imx_hdmi_dt_ids, &pdev->dev); +- struct device_node *np = pdev->dev.of_node; ++ of_match_device(imx_hdmi_dt_ids, dev); ++ struct device_node *np = dev->of_node; + struct device_node *ddc_node; + struct imx_hdmi *hdmi; + struct resource *iores; + int ret, irq; + +- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); ++ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + +- hdmi->dev = &pdev->dev; ++ hdmi->dev = dev; + hdmi->sample_rate = 48000; + hdmi->ratio = 100; + +@@ -1620,13 +1622,13 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) + if (irq < 0) + return -EINVAL; + +- ret = devm_request_irq(&pdev->dev, irq, imx_hdmi_irq, 0, +- dev_name(&pdev->dev), hdmi); ++ ret = devm_request_irq(dev, irq, imx_hdmi_irq, 0, ++ dev_name(dev), hdmi); + if (ret) + return ret; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- hdmi->regs = devm_ioremap_resource(&pdev->dev, iores); ++ hdmi->regs = devm_ioremap_resource(dev, iores); + if (IS_ERR(hdmi->regs)) + return PTR_ERR(hdmi->regs); + +@@ -1665,7 +1667,7 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) + } + + /* Product and revision IDs */ +- dev_info(&pdev->dev, ++ dev_info(dev, + "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", + hdmi_readb(hdmi, HDMI_DESIGN_ID), + hdmi_readb(hdmi, HDMI_REVISION_ID), +@@ -1699,7 +1701,7 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) + + imx_drm_encoder_add_possible_crtcs(hdmi->imx_drm_encoder, np); + +- platform_set_drvdata(pdev, hdmi); ++ dev_set_drvdata(dev, hdmi); + + return 0; + +@@ -1711,9 +1713,10 @@ err_isfr: + return ret; + } + +-static int imx_hdmi_platform_remove(struct platform_device *pdev) ++static void imx_hdmi_unbind(struct device *dev, struct device *master, ++ void *data) + { +- struct imx_hdmi *hdmi = platform_get_drvdata(pdev); ++ struct imx_hdmi *hdmi = dev_get_drvdata(dev); + struct drm_connector *connector = &hdmi->connector; + struct drm_encoder *encoder = &hdmi->encoder; + +@@ -1724,7 +1727,21 @@ static int imx_hdmi_platform_remove(struct platform_device *pdev) + clk_disable_unprepare(hdmi->iahb_clk); + clk_disable_unprepare(hdmi->isfr_clk); + i2c_put_adapter(hdmi->ddc); ++} + ++static const struct component_ops hdmi_ops = { ++ .bind = imx_hdmi_bind, ++ .unbind = imx_hdmi_unbind, ++}; ++ ++static int imx_hdmi_platform_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &hdmi_ops); ++} ++ ++static int imx_hdmi_platform_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &hdmi_ops); + return 0; + } + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0017-imx-drm-delay-publishing-sysfs-connector-entries.patch b/patches/imx_drm/0017-imx-drm-delay-publishing-sysfs-connector-entries.patch new file mode 100644 index 0000000000000000000000000000000000000000..fe8e6cfd57892d45f0e5fd0d11ca1c5d21d4c094 --- /dev/null +++ b/patches/imx_drm/0017-imx-drm-delay-publishing-sysfs-connector-entries.patch @@ -0,0 +1,71 @@ +From fe8f9b8adc4e3ef0efa6bf43383f1e440faea00d Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:03 +0000 +Subject: [PATCH 17/34] imx-drm: delay publishing sysfs connector entries + +Delay publishing sysfs connector entries until all components have +initialised. This reduces the probability of generating false hotplug +events when we're uncertain whether the driver can fully initialise. +This also pulls that code out of the individual imx-drm connector +drivers. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 82b0337..3cd330e 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -355,7 +355,7 @@ static int imx_drm_connector_register( + imx_drm_connector->connector->connector_type); + drm_mode_group_reinit(imxdrm->drm); + +- return drm_sysfs_connector_add(imx_drm_connector->connector); ++ return 0; + } + + /* +@@ -379,6 +379,7 @@ static void imx_drm_connector_unregister( + static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + { + struct imx_drm_device *imxdrm = __imx_drm_device(); ++ struct drm_connector *connector; + int ret; + + imxdrm->drm = drm; +@@ -432,8 +433,27 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + ret = component_bind_all(drm->dev, drm); + if (ret) + goto err_relock; ++ ++ /* ++ * All components are now added, we can publish the connector sysfs ++ * entries to userspace. This will generate hotplug events and so ++ * userspace will expect to be able to access DRM at this point. ++ */ ++ list_for_each_entry(connector, &drm->mode_config.connector_list, head) { ++ ret = drm_sysfs_connector_add(connector); ++ if (ret) { ++ dev_err(drm->dev, ++ "[CONNECTOR:%d:%s] drm_sysfs_connector_add failed: %d\n", ++ connector->base.id, ++ drm_get_connector_name(connector), ret); ++ goto err_unbind; ++ } ++ } ++ + return 0; + ++err_unbind: ++ component_unbind_all(drm->dev, drm); + err_relock: + mutex_lock(&imxdrm->mutex); + err_vblank: +-- +1.8.5.3 + diff --git a/patches/imx_drm/0018-imx-drm-remove-separate-imx-fbdev.patch b/patches/imx_drm/0018-imx-drm-remove-separate-imx-fbdev.patch new file mode 100644 index 0000000000000000000000000000000000000000..4dc7c325cb6e01b64079da23bf12f3d578957161 --- /dev/null +++ b/patches/imx_drm/0018-imx-drm-remove-separate-imx-fbdev.patch @@ -0,0 +1,246 @@ +From d2f8f5f2af13f2960d0e71bae5793bb75adc23ad Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:08 +0000 +Subject: [PATCH 18/34] imx-drm: remove separate imx-fbdev + +Now that we know when the components of the imx-drm subsystem will be +initialised, we can move the fbdev helper initialisation and teardown +into imx-drm-core. This gives us the required ordering that DRM wants +in both driver load and unload methods. + +We can also stop exporting the imx_drm_device_get() and +imx_drm_device_put() methods; nothing but the fbdev helper was making +use of these. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/Makefile | 1 - + drivers/staging/imx-drm/imx-drm-core.c | 43 ++++++++++++++------ + drivers/staging/imx-drm/imx-drm.h | 3 -- + drivers/staging/imx-drm/imx-fbdev.c | 74 ---------------------------------- + 4 files changed, 31 insertions(+), 90 deletions(-) + delete mode 100644 drivers/staging/imx-drm/imx-fbdev.c + +diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile +index 4677585..5239f90 100644 +--- a/drivers/staging/imx-drm/Makefile ++++ b/drivers/staging/imx-drm/Makefile +@@ -6,7 +6,6 @@ obj-$(CONFIG_DRM_IMX) += imxdrm.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 +-obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o + obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/ + + imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 3cd330e..5a60886 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -70,6 +70,10 @@ struct imx_drm_connector { + struct module *owner; + }; + ++static int legacyfb_depth = 16; ++module_param(legacyfb_depth, int, 0444); ++ ++static void imx_drm_device_put(void); + static struct imx_drm_device *__imx_drm_device(void); + + int imx_drm_crtc_id(struct imx_drm_crtc *crtc) +@@ -80,16 +84,23 @@ EXPORT_SYMBOL_GPL(imx_drm_crtc_id); + + static void imx_drm_driver_lastclose(struct drm_device *drm) + { ++#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) + struct imx_drm_device *imxdrm = drm->dev_private; + + if (imxdrm->fbhelper) + drm_fbdev_cma_restore_mode(imxdrm->fbhelper); ++#endif + } + + static int imx_drm_driver_unload(struct drm_device *drm) + { ++#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) + struct imx_drm_device *imxdrm = drm->dev_private; + ++ if (imxdrm->fbhelper) ++ drm_fbdev_cma_fini(imxdrm->fbhelper); ++#endif ++ + component_unbind_all(drm->dev, drm); + + imx_drm_device_put(); +@@ -225,7 +236,7 @@ static struct imx_drm_device *__imx_drm_device(void) + return imx_drm_device; + } + +-struct drm_device *imx_drm_device_get(void) ++static struct drm_device *imx_drm_device_get(void) + { + struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_encoder *enc; +@@ -273,9 +284,8 @@ unwind_enc: + return NULL; + + } +-EXPORT_SYMBOL_GPL(imx_drm_device_get); + +-void imx_drm_device_put(void) ++static void imx_drm_device_put(void) + { + struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_encoder *enc; +@@ -295,7 +305,6 @@ void imx_drm_device_put(void) + + mutex_unlock(&imxdrm->mutex); + } +-EXPORT_SYMBOL_GPL(imx_drm_device_put); + + static int drm_mode_group_reinit(struct drm_device *dev) + { +@@ -450,6 +459,24 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + } + } + ++ /* ++ * All components are now initialised, so setup the fb helper. ++ * The fb helper takes copies of key hardware information, so the ++ * crtcs/connectors/encoders must not change after this point. ++ */ ++#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) ++ if (legacyfb_depth != 16 && legacyfb_depth != 32) { ++ dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); ++ legacyfb_depth = 16; ++ } ++ imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, ++ drm->mode_config.num_crtc, MAX_CRTC); ++ if (IS_ERR(imxdrm->fbhelper)) { ++ ret = PTR_ERR(imxdrm->fbhelper); ++ imxdrm->fbhelper = NULL; ++ goto err_unbind; ++ } ++#endif + return 0; + + err_unbind: +@@ -767,14 +794,6 @@ err_busy: + } + EXPORT_SYMBOL_GPL(imx_drm_add_connector); + +-void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- +- imxdrm->fbhelper = fbdev_helper; +-} +-EXPORT_SYMBOL_GPL(imx_drm_fb_helper_set); +- + /* + * imx_drm_remove_connector - remove a connector + */ +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index e3ca0c6..d1fb114 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -54,13 +54,10 @@ void imx_drm_mode_config_init(struct drm_device *drm); + + struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); + +-struct drm_device *imx_drm_device_get(void); +-void imx_drm_device_put(void); + int imx_drm_panel_format_pins(struct drm_encoder *encoder, + u32 interface_pix_fmt, int hsync_pin, int vsync_pin); + int imx_drm_panel_format(struct drm_encoder *encoder, + u32 interface_pix_fmt); +-void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper); + + struct device_node; + +diff --git a/drivers/staging/imx-drm/imx-fbdev.c b/drivers/staging/imx-drm/imx-fbdev.c +deleted file mode 100644 +index 8331739..0000000 +--- a/drivers/staging/imx-drm/imx-fbdev.c ++++ /dev/null +@@ -1,74 +0,0 @@ +-/* +- * i.MX drm driver +- * +- * Copyright (C) 2012 Sascha Hauer, Pengutronix +- * +- * Based on Samsung Exynos code +- * +- * Copyright (c) 2011 Samsung Electronics Co., Ltd. +- * +- * 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 <linux/module.h> +-#include <drm/drmP.h> +-#include <drm/drm_crtc.h> +-#include <drm/drm_crtc_helper.h> +-#include <drm/drm_fb_cma_helper.h> +- +-#include "imx-drm.h" +- +-#define MAX_CONNECTOR 4 +-#define PREFERRED_BPP 16 +- +-static struct drm_fbdev_cma *fbdev_cma; +- +-static int legacyfb_depth = 16; +- +-module_param(legacyfb_depth, int, 0444); +- +-static int __init imx_fb_helper_init(void) +-{ +- struct drm_device *drm = imx_drm_device_get(); +- +- if (!drm) +- return -EINVAL; +- +- if (legacyfb_depth != 16 && legacyfb_depth != 32) { +- pr_warn("i.MX legacyfb: invalid legacyfb_depth setting. defaulting to 16bpp\n"); +- legacyfb_depth = 16; +- } +- +- fbdev_cma = drm_fbdev_cma_init(drm, legacyfb_depth, +- drm->mode_config.num_crtc, MAX_CONNECTOR); +- +- if (IS_ERR(fbdev_cma)) { +- imx_drm_device_put(); +- return PTR_ERR(fbdev_cma); +- } +- +- imx_drm_fb_helper_set(fbdev_cma); +- +- return 0; +-} +- +-static void __exit imx_fb_helper_exit(void) +-{ +- imx_drm_fb_helper_set(NULL); +- drm_fbdev_cma_fini(fbdev_cma); +- imx_drm_device_put(); +-} +- +-late_initcall(imx_fb_helper_init); +-module_exit(imx_fb_helper_exit); +- +-MODULE_DESCRIPTION("Freescale i.MX legacy fb driver"); +-MODULE_AUTHOR("Sascha Hauer, Pengutronix"); +-MODULE_LICENSE("GPL"); +-- +1.8.5.3 + diff --git a/patches/imx_drm/0019-imx-drm-remove-imx-fb.c.patch b/patches/imx_drm/0019-imx-drm-remove-imx-fb.c.patch new file mode 100644 index 0000000000000000000000000000000000000000..7793d783e253d33c36c85052f11ecdbfd47989b1 --- /dev/null +++ b/patches/imx_drm/0019-imx-drm-remove-imx-fb.c.patch @@ -0,0 +1,123 @@ +From f4aa6ec9c3f2bd067af5ef67a990cfc97ade3b13 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:13 +0000 +Subject: [PATCH 19/34] imx-drm: remove imx-fb.c + +imx-fb.c doesn't need to be separate from imx-drm-core.c - all it is +doing is setting up the minimum and maximum sizes of the scanout +buffers, and setting up the mode_config function pointers. Move the +contents into imx-drm-core.c and kill this file. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/Makefile | 2 +- + drivers/staging/imx-drm/imx-drm-core.c | 16 +++++++++++- + drivers/staging/imx-drm/imx-fb.c | 47 ---------------------------------- + 3 files changed, 16 insertions(+), 49 deletions(-) + delete mode 100644 drivers/staging/imx-drm/imx-fb.c + +diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile +index 5239f90..129e3a3 100644 +--- a/drivers/staging/imx-drm/Makefile ++++ b/drivers/staging/imx-drm/Makefile +@@ -1,5 +1,5 @@ + +-imxdrm-objs := imx-drm-core.o imx-fb.o ++imxdrm-objs := imx-drm-core.o + + obj-$(CONFIG_DRM_IMX) += imxdrm.o + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 5a60886..3e3fd28 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -381,6 +381,10 @@ static void imx_drm_connector_unregister( + drm_mode_group_reinit(imxdrm->drm); + } + ++static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { ++ .fb_create = drm_fb_cma_create, ++}; ++ + /* + * Main DRM initialisation. This binds, initialises and registers + * with DRM the subcomponents of the driver. +@@ -406,8 +410,18 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + */ + drm->irq_enabled = true; + ++ /* ++ * set max width and height as default value(4096x4096). ++ * this value would be used to check framebuffer size limitation ++ * at drm_mode_addfb(). ++ */ ++ drm->mode_config.min_width = 64; ++ drm->mode_config.min_height = 64; ++ drm->mode_config.max_width = 4096; ++ drm->mode_config.max_height = 4096; ++ drm->mode_config.funcs = &imx_drm_mode_config_funcs; ++ + drm_mode_config_init(drm); +- imx_drm_mode_config_init(drm); + + mutex_lock(&imxdrm->mutex); + +diff --git a/drivers/staging/imx-drm/imx-fb.c b/drivers/staging/imx-drm/imx-fb.c +deleted file mode 100644 +index 03a7b4e..0000000 +--- a/drivers/staging/imx-drm/imx-fb.c ++++ /dev/null +@@ -1,47 +0,0 @@ +-/* +- * i.MX drm driver +- * +- * Copyright (C) 2012 Sascha Hauer, Pengutronix +- * +- * Based on Samsung Exynos code +- * +- * Copyright (c) 2011 Samsung Electronics Co., Ltd. +- * +- * 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 <linux/module.h> +-#include <drm/drmP.h> +-#include <drm/drm_crtc.h> +-#include <drm/drm_crtc_helper.h> +-#include <drm/drm_gem_cma_helper.h> +-#include <drm/drm_fb_cma_helper.h> +- +-#include "imx-drm.h" +- +-static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { +- .fb_create = drm_fb_cma_create, +-}; +- +-void imx_drm_mode_config_init(struct drm_device *dev) +-{ +- dev->mode_config.min_width = 64; +- dev->mode_config.min_height = 64; +- +- /* +- * set max width and height as default value(4096x4096). +- * this value would be used to check framebuffer size limitation +- * at drm_mode_addfb(). +- */ +- dev->mode_config.max_width = 4096; +- dev->mode_config.max_height = 4096; +- +- dev->mode_config.funcs = &imx_drm_mode_config_funcs; +-} +-- +1.8.5.3 + diff --git a/patches/imx_drm/0020-imx-drm-use-supplied-drm_device-where-possible.patch b/patches/imx_drm/0020-imx-drm-use-supplied-drm_device-where-possible.patch new file mode 100644 index 0000000000000000000000000000000000000000..eaf1c1c35c002c9f8376823bf14d71f9d4f8aaf0 --- /dev/null +++ b/patches/imx_drm/0020-imx-drm-use-supplied-drm_device-where-possible.patch @@ -0,0 +1,206 @@ +From d47cbb62536e5d97a35a7be953a311c46a55cd39 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:19 +0000 +Subject: [PATCH 20/34] imx-drm: use supplied drm_device where possible + +The component helper provides us the drm_device which is being +registered. Rather than having to reference a global in imx-drm-core, +use this to get the imxdrm device, and also use it to register the CRTC +against. + +This means we never have CRTCs/encoders/connectors without the drivers +private data being accessible. + +Remove the module owner field as well; this provides no protection +against the device being unbound. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 34 +++++----------------------------- + drivers/staging/imx-drm/imx-drm.h | 4 ++-- + drivers/staging/imx-drm/ipuv3-crtc.c | 9 +++++---- + 3 files changed, 12 insertions(+), 35 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 3e3fd28..3d1c6b6 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -52,7 +52,6 @@ struct imx_drm_crtc { + struct imx_drm_device *imxdrm; + int pipe; + struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; +- struct module *owner; + struct crtc_cookie cookie; + int mux_id; + }; +@@ -74,7 +73,6 @@ static int legacyfb_depth = 16; + module_param(legacyfb_depth, int, 0444); + + static void imx_drm_device_put(void); +-static struct imx_drm_device *__imx_drm_device(void); + + int imx_drm_crtc_id(struct imx_drm_crtc *crtc) + { +@@ -114,7 +112,7 @@ static int imx_drm_driver_unload(struct drm_device *drm) + + struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) + { +- struct imx_drm_device *imxdrm = __imx_drm_device(); ++ struct imx_drm_device *imxdrm = crtc->dev->dev_private; + unsigned i; + + for (i = 0; i < MAX_CRTC; i++) +@@ -241,7 +239,6 @@ static struct drm_device *imx_drm_device_get(void) + struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_encoder *enc; + struct imx_drm_connector *con; +- struct imx_drm_crtc *crtc; + + list_for_each_entry(enc, &imxdrm->encoder_list, list) { + if (!try_module_get(enc->owner)) { +@@ -259,19 +256,8 @@ static struct drm_device *imx_drm_device_get(void) + } + } + +- list_for_each_entry(crtc, &imxdrm->crtc_list, list) { +- if (!try_module_get(crtc->owner)) { +- dev_err(imxdrm->dev, "could not get module %s\n", +- module_name(crtc->owner)); +- goto unwind_crtc; +- } +- } +- + return imxdrm->drm; + +-unwind_crtc: +- list_for_each_entry_continue_reverse(crtc, &imxdrm->crtc_list, list) +- module_put(crtc->owner); + unwind_con: + list_for_each_entry_continue_reverse(con, &imxdrm->connector_list, list) + module_put(con->owner); +@@ -290,13 +276,9 @@ static void imx_drm_device_put(void) + struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_encoder *enc; + struct imx_drm_connector *con; +- struct imx_drm_crtc *crtc; + + mutex_lock(&imxdrm->mutex); + +- list_for_each_entry(crtc, &imxdrm->crtc_list, list) +- module_put(crtc->owner); +- + list_for_each_entry(con, &imxdrm->connector_list, list) + module_put(con->owner); + +@@ -536,12 +518,12 @@ static void imx_drm_update_possible_crtcs(void) + * The return value if !NULL is a cookie for the caller to pass to + * imx_drm_remove_crtc later. + */ +-int imx_drm_add_crtc(struct drm_crtc *crtc, ++int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + struct imx_drm_crtc **new_crtc, + const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs, +- struct module *owner, void *cookie, int id) ++ void *cookie, int id) + { +- struct imx_drm_device *imxdrm = __imx_drm_device(); ++ struct imx_drm_device *imxdrm = drm->dev_private; + struct imx_drm_crtc *imx_drm_crtc; + int ret; + +@@ -575,8 +557,6 @@ int imx_drm_add_crtc(struct drm_crtc *crtc, + imx_drm_crtc->crtc = crtc; + imx_drm_crtc->imxdrm = imxdrm; + +- imx_drm_crtc->owner = owner; +- + imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; + + *new_crtc = imx_drm_crtc; +@@ -588,11 +568,9 @@ int imx_drm_add_crtc(struct drm_crtc *crtc, + drm_crtc_helper_add(crtc, + imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs); + +- drm_crtc_init(imxdrm->drm, crtc, ++ drm_crtc_init(drm, crtc, + imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs); + +- drm_mode_group_reinit(imxdrm->drm); +- + imx_drm_update_possible_crtcs(); + + mutex_unlock(&imxdrm->mutex); +@@ -622,8 +600,6 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) + + imxdrm->crtc[imx_drm_crtc->pipe] = NULL; + +- drm_mode_group_reinit(imxdrm->drm); +- + mutex_unlock(&imxdrm->mutex); + + kfree(imx_drm_crtc); +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index d1fb114..7846523 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -25,10 +25,10 @@ struct imx_drm_crtc_helper_funcs { + const struct drm_crtc_funcs *crtc_funcs; + }; + +-int imx_drm_add_crtc(struct drm_crtc *crtc, ++int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + struct imx_drm_crtc **new_crtc, + const struct imx_drm_crtc_helper_funcs *imx_helper_funcs, +- struct module *owner, void *cookie, int id); ++ void *cookie, int id); + int imx_drm_remove_crtc(struct imx_drm_crtc *); + int imx_drm_init_drm(struct platform_device *pdev, + int preferred_bpp); +diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c +index d779ad2..e646017 100644 +--- a/drivers/staging/imx-drm/ipuv3-crtc.c ++++ b/drivers/staging/imx-drm/ipuv3-crtc.c +@@ -336,7 +336,7 @@ err_out: + } + + static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, +- struct ipu_client_platformdata *pdata) ++ struct ipu_client_platformdata *pdata, struct drm_device *drm) + { + struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); + int dp = -EINVAL; +@@ -350,9 +350,9 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, + return ret; + } + +- ret = imx_drm_add_crtc(&ipu_crtc->base, ++ ret = imx_drm_add_crtc(drm, &ipu_crtc->base, + &ipu_crtc->imx_crtc, +- &ipu_crtc_helper_funcs, THIS_MODULE, ++ &ipu_crtc_helper_funcs, + ipu_crtc->dev->parent->of_node, pdata->di); + if (ret) { + dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); +@@ -404,6 +404,7 @@ err_put_resources: + static int ipu_drm_bind(struct device *dev, struct device *master, void *data) + { + struct ipu_client_platformdata *pdata = dev->platform_data; ++ struct drm_device *drm = data; + struct ipu_crtc *ipu_crtc; + int ret; + +@@ -413,7 +414,7 @@ static int ipu_drm_bind(struct device *dev, struct device *master, void *data) + + ipu_crtc->dev = dev; + +- ret = ipu_crtc_init(ipu_crtc, pdata); ++ ret = ipu_crtc_init(ipu_crtc, pdata, drm); + if (ret) + return ret; + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0021-imx-drm-imx-drm-core-provide-helper-function-to-pars.patch b/patches/imx_drm/0021-imx-drm-imx-drm-core-provide-helper-function-to-pars.patch new file mode 100644 index 0000000000000000000000000000000000000000..7af95c5b92169fd97063b47e98adf950669c9749 --- /dev/null +++ b/patches/imx_drm/0021-imx-drm-imx-drm-core-provide-helper-function-to-pars.patch @@ -0,0 +1,112 @@ +From 2a9f8c0acc17669c07a4e39312d4b69880ca9518 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:24 +0000 +Subject: [PATCH 21/34] imx-drm: imx-drm-core: provide helper function to parse + possible crtcs + +Provide a helper function to parse possible crtcs before the encoder +is registered. The crtc mask is derived from the position of the +CRTCs registered in the drm_device. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 66 ++++++++++++++++++++++++++++++++++ + drivers/staging/imx-drm/imx-drm.h | 2 ++ + 2 files changed, 68 insertions(+) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 3d1c6b6..5cac6ee 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -658,6 +658,72 @@ err_busy: + } + EXPORT_SYMBOL_GPL(imx_drm_add_encoder); + ++/* ++ * Find the DRM CRTC possible mask for the device node cookie/id. ++ * ++ * The encoder possible masks are defined by their position in the ++ * mode_config crtc_list. This means that CRTCs must not be added ++ * or removed once the DRM device has been fully initialised. ++ */ ++static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, ++ void *cookie, int id) ++{ ++ unsigned i; ++ ++ for (i = 0; i < MAX_CRTC; i++) { ++ struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; ++ if (imx_drm_crtc && imx_drm_crtc->cookie.id == id && ++ imx_drm_crtc->cookie.cookie == cookie) ++ return drm_crtc_mask(imx_drm_crtc->crtc); ++ } ++ ++ return 0; ++} ++ ++int imx_drm_encoder_parse_of(struct drm_device *drm, ++ struct drm_encoder *encoder, struct device_node *np) ++{ ++ struct imx_drm_device *imxdrm = drm->dev_private; ++ uint32_t crtc_mask = 0; ++ int i, ret = 0; ++ ++ for (i = 0; !ret; i++) { ++ struct of_phandle_args args; ++ uint32_t mask; ++ int id; ++ ++ ret = of_parse_phandle_with_args(np, "crtcs", "#crtc-cells", i, ++ &args); ++ if (ret == -ENOENT) ++ break; ++ if (ret < 0) ++ return ret; ++ ++ id = args.args_count > 0 ? args.args[0] : 0; ++ mask = imx_drm_find_crtc_mask(imxdrm, args.np, id); ++ of_node_put(args.np); ++ ++ /* ++ * If we failed to find the CRTC(s) which this encoder is ++ * supposed to be connected to, it's because the CRTC has ++ * not been registered yet. Defer probing, and hope that ++ * the required CRTC is added later. ++ */ ++ if (mask == 0) ++ return -EPROBE_DEFER; ++ ++ crtc_mask |= mask; ++ } ++ ++ encoder->possible_crtcs = crtc_mask; ++ ++ /* FIXME: this is the mask of outputs which can clone this output. */ ++ encoder->possible_clones = ~0; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); ++ + int imx_drm_encoder_add_possible_crtcs( + struct imx_drm_encoder *imx_drm_encoder, + struct device_node *np) +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index 7846523..49d4aaf 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -64,6 +64,8 @@ struct device_node; + int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder); + int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder, + struct device_node *np); ++int imx_drm_encoder_parse_of(struct drm_device *drm, ++ struct drm_encoder *encoder, struct device_node *np); + + int imx_drm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); +-- +1.8.5.3 + diff --git a/patches/imx_drm/0022-imx-drm-imx-drm-core-provide-common-connector-and-en.patch b/patches/imx_drm/0022-imx-drm-imx-drm-core-provide-common-connector-and-en.patch new file mode 100644 index 0000000000000000000000000000000000000000..91635b674abe767b0b9cf78311a04427d63eda50 --- /dev/null +++ b/patches/imx_drm/0022-imx-drm-imx-drm-core-provide-common-connector-and-en.patch @@ -0,0 +1,65 @@ +From 492e1547ad1e36e14109a886d9a7ae7b0986e2dc Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:29 +0000 +Subject: [PATCH 22/34] imx-drm: imx-drm-core: provide common connector and + encoder cleanup functions + +Provide two helper functions to assist with cleaning up imx-drm +connectors and encoders. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 13 +++++++++++++ + drivers/staging/imx-drm/imx-drm.h | 3 +++ + 2 files changed, 16 insertions(+) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 5cac6ee..1f50acd 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -363,6 +363,19 @@ static void imx_drm_connector_unregister( + drm_mode_group_reinit(imxdrm->drm); + } + ++void imx_drm_connector_destroy(struct drm_connector *connector) ++{ ++ drm_sysfs_connector_remove(connector); ++ drm_connector_cleanup(connector); ++} ++EXPORT_SYMBOL_GPL(imx_drm_connector_destroy); ++ ++void imx_drm_encoder_destroy(struct drm_encoder *encoder) ++{ ++ drm_encoder_cleanup(encoder); ++} ++EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); ++ + static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { + .fb_create = drm_fb_cma_create, + }; +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index 49d4aaf..0543606 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -8,6 +8,7 @@ + struct drm_crtc; + struct drm_connector; + struct drm_device; ++struct drm_display_mode; + struct drm_encoder; + struct imx_drm_crtc; + struct drm_fbdev_cma; +@@ -69,5 +70,7 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, + + int imx_drm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); ++void imx_drm_connector_destroy(struct drm_connector *connector); ++void imx_drm_encoder_destroy(struct drm_encoder *encoder); + + #endif /* _IMX_DRM_H_ */ +-- +1.8.5.3 + diff --git a/patches/imx_drm/0023-imx-drm-parallel-display-imx-tve-imx-ldb-initialise-.patch b/patches/imx_drm/0023-imx-drm-parallel-display-imx-tve-imx-ldb-initialise-.patch new file mode 100644 index 0000000000000000000000000000000000000000..889e2b38ead344196cc6eb094b926751a06821f3 --- /dev/null +++ b/patches/imx_drm/0023-imx-drm-parallel-display-imx-tve-imx-ldb-initialise-.patch @@ -0,0 +1,461 @@ +From 5a9eae85f9f047c3da4aa372a7fbbe1d4c1d94b8 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:34 +0000 +Subject: [PATCH 23/34] imx-drm: parallel-display,imx-tve,imx-ldb: initialise + drm components directly + +Now that our bind function is only ever called during the main DRM +driver ->load callback, we don't need to have the imx_drm_connector or +imx_drm_encoder abstractions anymore. So let's get rid of it, and move +the DRM connector and encoder setup into the connector support files. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-ldb.c | 68 ++++++++++-------------------- + drivers/staging/imx-drm/imx-tve.c | 58 +++++++------------------ + drivers/staging/imx-drm/parallel-display.c | 61 ++++++++------------------- + 3 files changed, 54 insertions(+), 133 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c +index d00f93f..5168c76 100644 +--- a/drivers/staging/imx-drm/imx-ldb.c ++++ b/drivers/staging/imx-drm/imx-ldb.c +@@ -59,9 +59,8 @@ struct imx_ldb; + struct imx_ldb_channel { + struct imx_ldb *ldb; + struct drm_connector connector; +- struct imx_drm_connector *imx_drm_connector; + struct drm_encoder encoder; +- struct imx_drm_encoder *imx_drm_encoder; ++ struct device_node *child; + int chno; + void *edid; + int edid_len; +@@ -92,11 +91,6 @@ static enum drm_connector_status imx_ldb_connector_detect( + return connector_status_connected; + } + +-static void imx_ldb_connector_destroy(struct drm_connector *connector) +-{ +- /* do not free here */ +-} +- + static int imx_ldb_connector_get_modes(struct drm_connector *connector) + { + struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); +@@ -308,16 +302,11 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder) + } + } + +-static void imx_ldb_encoder_destroy(struct drm_encoder *encoder) +-{ +- /* do not free here */ +-} +- + 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_ldb_connector_destroy, ++ .destroy = imx_drm_connector_destroy, + }; + + static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { +@@ -327,7 +316,7 @@ static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { + }; + + static struct drm_encoder_funcs imx_ldb_encoder_funcs = { +- .destroy = imx_ldb_encoder_destroy, ++ .destroy = imx_drm_encoder_destroy, + }; + + static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { +@@ -354,45 +343,36 @@ static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno) + return PTR_ERR_OR_ZERO(ldb->clk_pll[chno]); + } + +-static int imx_ldb_register(struct imx_ldb_channel *imx_ldb_ch) ++static int imx_ldb_register(struct drm_device *drm, ++ struct imx_ldb_channel *imx_ldb_ch) + { +- int ret; + 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); ++ ret = imx_ldb_get_clk(ldb, 1); + if (ret) + return ret; + } + +- imx_ldb_ch->connector.funcs = &imx_ldb_connector_funcs; +- imx_ldb_ch->encoder.funcs = &imx_ldb_encoder_funcs; +- +- imx_ldb_ch->encoder.encoder_type = DRM_MODE_ENCODER_LVDS; +- imx_ldb_ch->connector.connector_type = DRM_MODE_CONNECTOR_LVDS; +- + drm_encoder_helper_add(&imx_ldb_ch->encoder, + &imx_ldb_encoder_helper_funcs); +- ret = imx_drm_add_encoder(&imx_ldb_ch->encoder, +- &imx_ldb_ch->imx_drm_encoder, THIS_MODULE); +- if (ret) { +- dev_err(ldb->dev, "adding encoder failed with %d\n", ret); +- return ret; +- } ++ 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); +- +- ret = imx_drm_add_connector(&imx_ldb_ch->connector, +- &imx_ldb_ch->imx_drm_connector, THIS_MODULE); +- if (ret) { +- imx_drm_remove_encoder(imx_ldb_ch->imx_drm_encoder); +- dev_err(ldb->dev, "adding connector failed with %d\n", ret); +- return ret; +- } ++ 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); +@@ -453,6 +433,7 @@ 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); +@@ -523,6 +504,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) + 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) { +@@ -565,11 +547,9 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) + return -EINVAL; + } + +- ret = imx_ldb_register(channel); ++ ret = imx_ldb_register(drm, channel); + if (ret) + return ret; +- +- imx_drm_encoder_add_possible_crtcs(channel->imx_drm_encoder, child); + } + + dev_set_drvdata(dev, imx_ldb); +@@ -585,13 +565,9 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, + + for (i = 0; i < 2; i++) { + struct imx_ldb_channel *channel = &imx_ldb->channel[i]; +- struct drm_connector *connector = &channel->connector; +- struct drm_encoder *encoder = &channel->encoder; +- +- drm_mode_connector_detach_encoder(connector, encoder); + +- imx_drm_remove_connector(channel->imx_drm_connector); +- imx_drm_remove_encoder(channel->imx_drm_encoder); ++ channel->connector.funcs->destroy(&channel->connector); ++ channel->encoder.funcs->destroy(&channel->encoder); + } + } + +diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c +index ad840d7..702c0c3 100644 +--- a/drivers/staging/imx-drm/imx-tve.c ++++ b/drivers/staging/imx-drm/imx-tve.c +@@ -111,9 +111,7 @@ enum { + + struct imx_tve { + struct drm_connector connector; +- struct imx_drm_connector *imx_drm_connector; + struct drm_encoder encoder; +- struct imx_drm_encoder *imx_drm_encoder; + struct device *dev; + spinlock_t lock; /* register lock */ + bool enabled; +@@ -226,11 +224,6 @@ static enum drm_connector_status imx_tve_connector_detect( + return connector_status_connected; + } + +-static void imx_tve_connector_destroy(struct drm_connector *connector) +-{ +- /* do not free here */ +-} +- + static int imx_tve_connector_get_modes(struct drm_connector *connector) + { + struct imx_tve *tve = con_to_tve(connector); +@@ -368,16 +361,11 @@ static void imx_tve_encoder_disable(struct drm_encoder *encoder) + tve_disable(tve); + } + +-static void imx_tve_encoder_destroy(struct drm_encoder *encoder) +-{ +- /* do not free here */ +-} +- + 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_tve_connector_destroy, ++ .destroy = imx_drm_connector_destroy, + }; + + static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { +@@ -387,7 +375,7 @@ static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { + }; + + static struct drm_encoder_funcs imx_tve_encoder_funcs = { +- .destroy = imx_tve_encoder_destroy, ++ .destroy = imx_drm_encoder_destroy, + }; + + static struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = { +@@ -507,7 +495,7 @@ static int tve_clk_init(struct imx_tve *tve, void __iomem *base) + return 0; + } + +-static int imx_tve_register(struct imx_tve *tve) ++static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve) + { + int encoder_type; + int ret; +@@ -515,30 +503,19 @@ static int imx_tve_register(struct imx_tve *tve) + encoder_type = tve->mode == TVE_MODE_VGA ? + DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC; + +- tve->connector.funcs = &imx_tve_connector_funcs; +- tve->encoder.funcs = &imx_tve_encoder_funcs; +- +- tve->encoder.encoder_type = encoder_type; +- tve->connector.connector_type = DRM_MODE_CONNECTOR_VGA; ++ ret = imx_drm_encoder_parse_of(drm, &tve->encoder, ++ tve->dev->of_node); ++ if (ret) ++ return ret; + + drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs); +- ret = imx_drm_add_encoder(&tve->encoder, &tve->imx_drm_encoder, +- THIS_MODULE); +- if (ret) { +- dev_err(tve->dev, "adding encoder failed with %d\n", ret); +- return ret; +- } ++ drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs, ++ encoder_type); + + drm_connector_helper_add(&tve->connector, + &imx_tve_connector_helper_funcs); +- +- ret = imx_drm_add_connector(&tve->connector, +- &tve->imx_drm_connector, THIS_MODULE); +- if (ret) { +- imx_drm_remove_encoder(tve->imx_drm_encoder); +- dev_err(tve->dev, "adding connector failed with %d\n", ret); +- return ret; +- } ++ drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs, ++ DRM_MODE_CONNECTOR_VGA); + + drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder); + +@@ -587,6 +564,7 @@ static const int of_get_tve_mode(struct device_node *np) + static int imx_tve_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); ++ struct drm_device *drm = data; + struct device_node *np = dev->of_node; + struct device_node *ddc_node; + struct imx_tve *tve; +@@ -701,12 +679,10 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) + /* disable cable detection for VGA mode */ + ret = regmap_write(tve->regmap, TVE_CD_CONT_REG, 0); + +- ret = imx_tve_register(tve); ++ ret = imx_tve_register(drm, tve); + if (ret) + return ret; + +- ret = imx_drm_encoder_add_possible_crtcs(tve->imx_drm_encoder, np); +- + dev_set_drvdata(dev, tve); + + return 0; +@@ -716,13 +692,9 @@ static void imx_tve_unbind(struct device *dev, struct device *master, + void *data) + { + struct imx_tve *tve = dev_get_drvdata(dev); +- struct drm_connector *connector = &tve->connector; +- struct drm_encoder *encoder = &tve->encoder; +- +- drm_mode_connector_detach_encoder(connector, encoder); + +- imx_drm_remove_connector(tve->imx_drm_connector); +- imx_drm_remove_encoder(tve->imx_drm_encoder); ++ tve->connector.funcs->destroy(&tve->connector); ++ tve->encoder.funcs->destroy(&tve->encoder); + + if (!IS_ERR(tve->dac_reg)) + regulator_disable(tve->dac_reg); +diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c +index 4019cae..d610f07 100644 +--- a/drivers/staging/imx-drm/parallel-display.c ++++ b/drivers/staging/imx-drm/parallel-display.c +@@ -33,9 +33,7 @@ + + struct imx_parallel_display { + struct drm_connector connector; +- struct imx_drm_connector *imx_drm_connector; + struct drm_encoder encoder; +- struct imx_drm_encoder *imx_drm_encoder; + struct device *dev; + void *edid; + int edid_len; +@@ -50,11 +48,6 @@ static enum drm_connector_status imx_pd_connector_detect( + return connector_status_connected; + } + +-static void imx_pd_connector_destroy(struct drm_connector *connector) +-{ +- /* do not free here */ +-} +- + static int imx_pd_connector_get_modes(struct drm_connector *connector) + { + struct imx_parallel_display *imxpd = con_to_imxpd(connector); +@@ -126,16 +119,11 @@ static void imx_pd_encoder_disable(struct drm_encoder *encoder) + { + } + +-static void imx_pd_encoder_destroy(struct drm_encoder *encoder) +-{ +- /* do not free here */ +-} +- + static struct drm_connector_funcs imx_pd_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = imx_pd_connector_detect, +- .destroy = imx_pd_connector_destroy, ++ .destroy = imx_drm_connector_destroy, + }; + + static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { +@@ -145,7 +133,7 @@ static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { + }; + + static struct drm_encoder_funcs imx_pd_encoder_funcs = { +- .destroy = imx_pd_encoder_destroy, ++ .destroy = imx_drm_encoder_destroy, + }; + + static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { +@@ -157,36 +145,26 @@ static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { + .disable = imx_pd_encoder_disable, + }; + +-static int imx_pd_register(struct imx_parallel_display *imxpd) ++static int imx_pd_register(struct drm_device *drm, ++ struct imx_parallel_display *imxpd) + { + int ret; + +- drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); +- +- imxpd->connector.funcs = &imx_pd_connector_funcs; +- imxpd->encoder.funcs = &imx_pd_encoder_funcs; +- +- imxpd->encoder.encoder_type = DRM_MODE_ENCODER_NONE; +- imxpd->connector.connector_type = DRM_MODE_CONNECTOR_VGA; ++ ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder, ++ imxpd->dev->of_node); ++ if (ret) ++ return ret; + + drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs); +- ret = imx_drm_add_encoder(&imxpd->encoder, &imxpd->imx_drm_encoder, +- THIS_MODULE); +- if (ret) { +- dev_err(imxpd->dev, "adding encoder failed with %d\n", ret); +- return ret; +- } ++ drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs, ++ DRM_MODE_ENCODER_NONE); + + drm_connector_helper_add(&imxpd->connector, + &imx_pd_connector_helper_funcs); ++ drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs, ++ DRM_MODE_CONNECTOR_VGA); + +- ret = imx_drm_add_connector(&imxpd->connector, +- &imxpd->imx_drm_connector, THIS_MODULE); +- if (ret) { +- imx_drm_remove_encoder(imxpd->imx_drm_encoder); +- dev_err(imxpd->dev, "adding connector failed with %d\n", ret); +- return ret; +- } ++ drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); + + imxpd->connector.encoder = &imxpd->encoder; + +@@ -195,6 +173,7 @@ static int imx_pd_register(struct imx_parallel_display *imxpd) + + static int imx_pd_bind(struct device *dev, struct device *master, void *data) + { ++ struct drm_device *drm = data; + struct device_node *np = dev->of_node; + const u8 *edidp; + struct imx_parallel_display *imxpd; +@@ -221,12 +200,10 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) + + imxpd->dev = dev; + +- ret = imx_pd_register(imxpd); ++ ret = imx_pd_register(drm, imxpd); + if (ret) + return ret; + +- ret = imx_drm_encoder_add_possible_crtcs(imxpd->imx_drm_encoder, np); +- + dev_set_drvdata(dev, imxpd); + + return 0; +@@ -236,13 +213,9 @@ static void imx_pd_unbind(struct device *dev, struct device *master, + void *data) + { + struct imx_parallel_display *imxpd = dev_get_drvdata(dev); +- struct drm_connector *connector = &imxpd->connector; +- struct drm_encoder *encoder = &imxpd->encoder; +- +- drm_mode_connector_detach_encoder(connector, encoder); + +- imx_drm_remove_connector(imxpd->imx_drm_connector); +- imx_drm_remove_encoder(imxpd->imx_drm_encoder); ++ imxpd->encoder.funcs->destroy(&imxpd->encoder); ++ imxpd->connector.funcs->destroy(&imxpd->connector); + } + + static const struct component_ops imx_pd_ops = { +-- +1.8.5.3 + diff --git a/patches/imx_drm/0024-imx-drm-imx-hdmi-initialise-drm-components-directly.patch b/patches/imx_drm/0024-imx-drm-imx-hdmi-initialise-drm-components-directly.patch new file mode 100644 index 0000000000000000000000000000000000000000..051ff95cf77ab0384212a2ace19986f1bbfe080c --- /dev/null +++ b/patches/imx_drm/0024-imx-drm-imx-hdmi-initialise-drm-components-directly.patch @@ -0,0 +1,149 @@ +From 55c91d0127a9cf36e45801047d3ba20035622e6a Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:39 +0000 +Subject: [PATCH 24/34] imx-drm: imx-hdmi: initialise drm components directly + +Rather than calling into imx-drm-core to setup and register the encoders +and connectors, do this ourselves. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 56 ++++++++++---------------------------- + 1 file changed, 15 insertions(+), 41 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 14b4a4b..8c58645 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -113,9 +113,7 @@ struct hdmi_data_info { + + struct imx_hdmi { + struct drm_connector connector; +- struct imx_drm_connector *imx_drm_connector; + struct drm_encoder encoder; +- struct imx_drm_encoder *imx_drm_encoder; + + enum imx_hdmi_devtype dev_type; + struct device *dev; +@@ -1378,10 +1376,6 @@ static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector + return connector_status_connected; + } + +-static void imx_hdmi_connector_destroy(struct drm_connector *connector) +-{ +-} +- + static int imx_hdmi_connector_get_modes(struct drm_connector *connector) + { + struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, +@@ -1467,13 +1461,8 @@ static void imx_hdmi_encoder_commit(struct drm_encoder *encoder) + imx_hdmi_poweron(hdmi); + } + +-static void imx_hdmi_encoder_destroy(struct drm_encoder *encoder) +-{ +- return; +-} +- + static struct drm_encoder_funcs imx_hdmi_encoder_funcs = { +- .destroy = imx_hdmi_encoder_destroy, ++ .destroy = imx_drm_encoder_destroy, + }; + + static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = { +@@ -1489,7 +1478,7 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = imx_hdmi_connector_detect, +- .destroy = imx_hdmi_connector_destroy, ++ .destroy = imx_drm_connector_destroy, + }; + + static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { +@@ -1529,34 +1518,23 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) + return IRQ_HANDLED; + } + +-static int imx_hdmi_register(struct imx_hdmi *hdmi) ++static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi) + { + int ret; + +- hdmi->connector.funcs = &imx_hdmi_connector_funcs; +- hdmi->encoder.funcs = &imx_hdmi_encoder_funcs; +- +- hdmi->encoder.encoder_type = DRM_MODE_ENCODER_TMDS; +- hdmi->connector.connector_type = DRM_MODE_CONNECTOR_HDMIA; ++ ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder, ++ hdmi->dev->of_node); ++ if (ret) ++ return ret; + + drm_encoder_helper_add(&hdmi->encoder, &imx_hdmi_encoder_helper_funcs); +- ret = imx_drm_add_encoder(&hdmi->encoder, &hdmi->imx_drm_encoder, +- THIS_MODULE); +- if (ret) { +- dev_err(hdmi->dev, "adding encoder failed: %d\n", ret); +- return ret; +- } ++ drm_encoder_init(drm, &hdmi->encoder, &imx_hdmi_encoder_funcs, ++ DRM_MODE_ENCODER_TMDS); + + drm_connector_helper_add(&hdmi->connector, + &imx_hdmi_connector_helper_funcs); +- +- ret = imx_drm_add_connector(&hdmi->connector, +- &hdmi->imx_drm_connector, THIS_MODULE); +- if (ret) { +- imx_drm_remove_encoder(hdmi->imx_drm_encoder); +- dev_err(hdmi->dev, "adding connector failed: %d\n", ret); +- return ret; +- } ++ drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs, ++ DRM_MODE_CONNECTOR_HDMIA); + + hdmi->connector.encoder = &hdmi->encoder; + +@@ -1588,6 +1566,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + struct platform_device *pdev = to_platform_device(dev); + 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 imx_hdmi *hdmi; +@@ -1695,12 +1674,10 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + if (ret) + goto err_iahb; + +- ret = imx_hdmi_register(hdmi); ++ ret = imx_hdmi_register(drm, hdmi); + if (ret) + goto err_iahb; + +- imx_drm_encoder_add_possible_crtcs(hdmi->imx_drm_encoder, np); +- + dev_set_drvdata(dev, hdmi); + + return 0; +@@ -1717,12 +1694,9 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master, + void *data) + { + struct imx_hdmi *hdmi = dev_get_drvdata(dev); +- struct drm_connector *connector = &hdmi->connector; +- struct drm_encoder *encoder = &hdmi->encoder; + +- drm_mode_connector_detach_encoder(connector, encoder); +- imx_drm_remove_connector(hdmi->imx_drm_connector); +- imx_drm_remove_encoder(hdmi->imx_drm_encoder); ++ hdmi->connector.funcs->destroy(&hdmi->connector); ++ hdmi->encoder.funcs->destroy(&hdmi->encoder); + + clk_disable_unprepare(hdmi->iahb_clk); + clk_disable_unprepare(hdmi->isfr_clk); +-- +1.8.5.3 + diff --git a/patches/imx_drm/0025-imx-drm-imx-drm-core-remove-imx_drm_connector-and-im.patch b/patches/imx_drm/0025-imx-drm-imx-drm-core-remove-imx_drm_connector-and-im.patch new file mode 100644 index 0000000000000000000000000000000000000000..71ab52af266ff77e80f63b7eae9524b71a413ee7 --- /dev/null +++ b/patches/imx_drm/0025-imx-drm-imx-drm-core-remove-imx_drm_connector-and-im.patch @@ -0,0 +1,517 @@ +From a2b51b9e8c36c870a07883d1d212a368d42b5f3e Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:45 +0000 +Subject: [PATCH 25/34] imx-drm: imx-drm-core: remove imx_drm_connector and + imx_drm_encoder code + +The core imx_drm_connector and imx_drm_encoder code is no longer +required - the connectors and encoders are all using the component +support, so we can remove this. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 371 +-------------------------------- + drivers/staging/imx-drm/imx-drm.h | 14 -- + 2 files changed, 1 insertion(+), 384 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 1f50acd..b27c425 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -40,8 +40,6 @@ struct imx_drm_device { + struct drm_device *drm; + struct device *dev; + struct imx_drm_crtc *crtc[MAX_CRTC]; +- struct list_head encoder_list; +- struct list_head connector_list; + struct mutex mutex; + int pipes; + struct drm_fbdev_cma *fbhelper; +@@ -56,24 +54,9 @@ struct imx_drm_crtc { + int mux_id; + }; + +-struct imx_drm_encoder { +- struct drm_encoder *encoder; +- struct list_head list; +- struct module *owner; +- struct list_head possible_crtcs; +-}; +- +-struct imx_drm_connector { +- struct drm_connector *connector; +- struct list_head list; +- struct module *owner; +-}; +- + static int legacyfb_depth = 16; + module_param(legacyfb_depth, int, 0444); + +-static void imx_drm_device_put(void); +- + int imx_drm_crtc_id(struct imx_drm_crtc *crtc) + { + return crtc->pipe; +@@ -101,8 +84,6 @@ static int imx_drm_driver_unload(struct drm_device *drm) + + component_unbind_all(drm->dev, drm); + +- imx_drm_device_put(); +- + drm_vblank_cleanup(drm); + drm_kms_helper_poll_fini(drm); + drm_mode_config_cleanup(drm); +@@ -234,135 +215,6 @@ static struct imx_drm_device *__imx_drm_device(void) + return imx_drm_device; + } + +-static struct drm_device *imx_drm_device_get(void) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct imx_drm_encoder *enc; +- struct imx_drm_connector *con; +- +- list_for_each_entry(enc, &imxdrm->encoder_list, list) { +- if (!try_module_get(enc->owner)) { +- dev_err(imxdrm->dev, "could not get module %s\n", +- module_name(enc->owner)); +- goto unwind_enc; +- } +- } +- +- list_for_each_entry(con, &imxdrm->connector_list, list) { +- if (!try_module_get(con->owner)) { +- dev_err(imxdrm->dev, "could not get module %s\n", +- module_name(con->owner)); +- goto unwind_con; +- } +- } +- +- return imxdrm->drm; +- +-unwind_con: +- list_for_each_entry_continue_reverse(con, &imxdrm->connector_list, list) +- module_put(con->owner); +-unwind_enc: +- list_for_each_entry_continue_reverse(enc, &imxdrm->encoder_list, list) +- module_put(enc->owner); +- +- mutex_unlock(&imxdrm->mutex); +- +- return NULL; +- +-} +- +-static void imx_drm_device_put(void) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct imx_drm_encoder *enc; +- struct imx_drm_connector *con; +- +- mutex_lock(&imxdrm->mutex); +- +- list_for_each_entry(con, &imxdrm->connector_list, list) +- module_put(con->owner); +- +- list_for_each_entry(enc, &imxdrm->encoder_list, list) +- module_put(enc->owner); +- +- mutex_unlock(&imxdrm->mutex); +-} +- +-static int drm_mode_group_reinit(struct drm_device *dev) +-{ +- struct drm_mode_group *group = &dev->primary->mode_group; +- uint32_t *id_list = group->id_list; +- int ret; +- +- ret = drm_mode_group_init_legacy_group(dev, group); +- if (ret < 0) +- return ret; +- +- kfree(id_list); +- return 0; +-} +- +-/* +- * register an encoder to the drm core +- */ +-static int imx_drm_encoder_register(struct imx_drm_encoder *imx_drm_encoder) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- +- INIT_LIST_HEAD(&imx_drm_encoder->possible_crtcs); +- +- drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder, +- imx_drm_encoder->encoder->funcs, +- imx_drm_encoder->encoder->encoder_type); +- +- drm_mode_group_reinit(imxdrm->drm); +- +- return 0; +-} +- +-/* +- * unregister an encoder from the drm core +- */ +-static void imx_drm_encoder_unregister(struct imx_drm_encoder +- *imx_drm_encoder) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- +- drm_encoder_cleanup(imx_drm_encoder->encoder); +- +- drm_mode_group_reinit(imxdrm->drm); +-} +- +-/* +- * register a connector to the drm core +- */ +-static int imx_drm_connector_register( +- struct imx_drm_connector *imx_drm_connector) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- +- drm_connector_init(imxdrm->drm, imx_drm_connector->connector, +- imx_drm_connector->connector->funcs, +- imx_drm_connector->connector->connector_type); +- drm_mode_group_reinit(imxdrm->drm); +- +- return 0; +-} +- +-/* +- * unregister a connector from the drm core +- */ +-static void imx_drm_connector_unregister( +- struct imx_drm_connector *imx_drm_connector) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- +- drm_sysfs_connector_remove(imx_drm_connector->connector); +- drm_connector_cleanup(imx_drm_connector->connector); +- +- drm_mode_group_reinit(imxdrm->drm); +-} +- + void imx_drm_connector_destroy(struct drm_connector *connector) + { + drm_sysfs_connector_remove(connector); +@@ -439,12 +291,8 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + */ + drm->vblank_disable_allowed = true; + +- if (!imx_drm_device_get()) { +- ret = -EINVAL; +- goto err_vblank; +- } +- + platform_set_drvdata(drm->platformdev, drm); ++ + mutex_unlock(&imxdrm->mutex); + + /* Now try and bind all our sub-components */ +@@ -492,7 +340,6 @@ err_unbind: + component_unbind_all(drm->dev, drm); + err_relock: + mutex_lock(&imxdrm->mutex); +-err_vblank: + drm_vblank_cleanup(drm); + err_kms: + drm_kms_helper_poll_fini(drm); +@@ -502,29 +349,6 @@ err_kms: + return ret; + } + +-static void imx_drm_update_possible_crtcs(void) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct imx_drm_crtc *imx_drm_crtc; +- struct imx_drm_encoder *enc; +- struct crtc_cookie *cookie; +- +- list_for_each_entry(enc, &imxdrm->encoder_list, list) { +- u32 possible_crtcs = 0; +- +- list_for_each_entry(cookie, &enc->possible_crtcs, list) { +- list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list) { +- if (imx_drm_crtc->cookie.cookie == cookie->cookie && +- imx_drm_crtc->cookie.id == cookie->id) { +- possible_crtcs |= 1 << imx_drm_crtc->pipe; +- } +- } +- } +- enc->encoder->possible_crtcs = possible_crtcs; +- enc->encoder->possible_clones = possible_crtcs; +- } +-} +- + /* + * imx_drm_add_crtc - add a new crtc + * +@@ -584,8 +408,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + drm_crtc_init(drm, crtc, + imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs); + +- imx_drm_update_possible_crtcs(); +- + mutex_unlock(&imxdrm->mutex); + + return 0; +@@ -622,56 +444,6 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) + EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); + + /* +- * imx_drm_add_encoder - add a new encoder +- */ +-int imx_drm_add_encoder(struct drm_encoder *encoder, +- struct imx_drm_encoder **newenc, struct module *owner) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct imx_drm_encoder *imx_drm_encoder; +- int ret; +- +- mutex_lock(&imxdrm->mutex); +- +- if (imxdrm->drm->open_count) { +- ret = -EBUSY; +- goto err_busy; +- } +- +- imx_drm_encoder = kzalloc(sizeof(*imx_drm_encoder), GFP_KERNEL); +- if (!imx_drm_encoder) { +- ret = -ENOMEM; +- goto err_alloc; +- } +- +- imx_drm_encoder->encoder = encoder; +- imx_drm_encoder->owner = owner; +- +- ret = imx_drm_encoder_register(imx_drm_encoder); +- if (ret) { +- ret = -ENOMEM; +- goto err_register; +- } +- +- list_add_tail(&imx_drm_encoder->list, &imxdrm->encoder_list); +- +- *newenc = imx_drm_encoder; +- +- mutex_unlock(&imxdrm->mutex); +- +- return 0; +- +-err_register: +- kfree(imx_drm_encoder); +-err_alloc: +-err_busy: +- mutex_unlock(&imxdrm->mutex); +- +- return ret; +-} +-EXPORT_SYMBOL_GPL(imx_drm_add_encoder); +- +-/* + * Find the DRM CRTC possible mask for the device node cookie/id. + * + * The encoder possible masks are defined by their position in the +@@ -737,49 +509,6 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); + +-int imx_drm_encoder_add_possible_crtcs( +- struct imx_drm_encoder *imx_drm_encoder, +- struct device_node *np) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct of_phandle_args args; +- struct crtc_cookie *c; +- int ret = 0; +- int i; +- +- if (!list_empty(&imx_drm_encoder->possible_crtcs)) +- return -EBUSY; +- +- for (i = 0; !ret; i++) { +- ret = of_parse_phandle_with_args(np, "crtcs", +- "#crtc-cells", i, &args); +- if (ret < 0) +- break; +- +- c = kzalloc(sizeof(*c), GFP_KERNEL); +- if (!c) { +- of_node_put(args.np); +- return -ENOMEM; +- } +- +- c->cookie = args.np; +- c->id = args.args_count > 0 ? args.args[0] : 0; +- +- of_node_put(args.np); +- +- mutex_lock(&imxdrm->mutex); +- +- list_add_tail(&c->list, &imx_drm_encoder->possible_crtcs); +- +- mutex_unlock(&imxdrm->mutex); +- } +- +- imx_drm_update_possible_crtcs(); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(imx_drm_encoder_add_possible_crtcs); +- + int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder) + { + struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc); +@@ -788,102 +517,6 @@ int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder) + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); + +-/* +- * imx_drm_remove_encoder - remove an encoder +- */ +-int imx_drm_remove_encoder(struct imx_drm_encoder *imx_drm_encoder) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct crtc_cookie *c, *tmp; +- +- mutex_lock(&imxdrm->mutex); +- +- imx_drm_encoder_unregister(imx_drm_encoder); +- +- list_del(&imx_drm_encoder->list); +- +- list_for_each_entry_safe(c, tmp, &imx_drm_encoder->possible_crtcs, +- list) +- kfree(c); +- +- mutex_unlock(&imxdrm->mutex); +- +- kfree(imx_drm_encoder); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(imx_drm_remove_encoder); +- +-/* +- * imx_drm_add_connector - add a connector +- */ +-int imx_drm_add_connector(struct drm_connector *connector, +- struct imx_drm_connector **new_con, +- struct module *owner) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- struct imx_drm_connector *imx_drm_connector; +- int ret; +- +- mutex_lock(&imxdrm->mutex); +- +- if (imxdrm->drm->open_count) { +- ret = -EBUSY; +- goto err_busy; +- } +- +- imx_drm_connector = kzalloc(sizeof(*imx_drm_connector), GFP_KERNEL); +- if (!imx_drm_connector) { +- ret = -ENOMEM; +- goto err_alloc; +- } +- +- imx_drm_connector->connector = connector; +- imx_drm_connector->owner = owner; +- +- ret = imx_drm_connector_register(imx_drm_connector); +- if (ret) +- goto err_register; +- +- list_add_tail(&imx_drm_connector->list, &imxdrm->connector_list); +- +- *new_con = imx_drm_connector; +- +- mutex_unlock(&imxdrm->mutex); +- +- return 0; +- +-err_register: +- kfree(imx_drm_connector); +-err_alloc: +-err_busy: +- mutex_unlock(&imxdrm->mutex); +- +- return ret; +-} +-EXPORT_SYMBOL_GPL(imx_drm_add_connector); +- +-/* +- * imx_drm_remove_connector - remove a connector +- */ +-int imx_drm_remove_connector(struct imx_drm_connector *imx_drm_connector) +-{ +- struct imx_drm_device *imxdrm = __imx_drm_device(); +- +- mutex_lock(&imxdrm->mutex); +- +- imx_drm_connector_unregister(imx_drm_connector); +- +- list_del(&imx_drm_connector->list); +- +- mutex_unlock(&imxdrm->mutex); +- +- kfree(imx_drm_connector); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(imx_drm_remove_connector); +- + static const struct drm_ioctl_desc imx_drm_ioctls[] = { + /* none so far */ + }; +@@ -1031,8 +664,6 @@ static int __init imx_drm_init(void) + return -ENOMEM; + + mutex_init(&imx_drm_device->mutex); +- INIT_LIST_HEAD(&imx_drm_device->connector_list); +- INIT_LIST_HEAD(&imx_drm_device->encoder_list); + + ret = platform_driver_register(&imx_drm_pdrv); + if (ret) +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index 0543606..ae9c96d 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -39,18 +39,6 @@ int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc); + void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc); + void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc); + +-struct imx_drm_encoder; +-int imx_drm_add_encoder(struct drm_encoder *encoder, +- struct imx_drm_encoder **new_enc, +- struct module *owner); +-int imx_drm_remove_encoder(struct imx_drm_encoder *); +- +-struct imx_drm_connector; +-int imx_drm_add_connector(struct drm_connector *connector, +- struct imx_drm_connector **new_con, +- struct module *owner); +-int imx_drm_remove_connector(struct imx_drm_connector *); +- + void imx_drm_mode_config_init(struct drm_device *drm); + + struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); +@@ -63,8 +51,6 @@ int imx_drm_panel_format(struct drm_encoder *encoder, + struct device_node; + + int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder); +-int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder, +- struct device_node *np); + int imx_drm_encoder_parse_of(struct drm_device *drm, + struct drm_encoder *encoder, struct device_node *np); + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0026-imx-drm-imx-drm-core-get-rid-of-drm_mode_group_init_.patch b/patches/imx_drm/0026-imx-drm-imx-drm-core-get-rid-of-drm_mode_group_init_.patch new file mode 100644 index 0000000000000000000000000000000000000000..68277d47076a8b2286ebe1b8a8881217a564371b --- /dev/null +++ b/patches/imx_drm/0026-imx-drm-imx-drm-core-get-rid-of-drm_mode_group_init_.patch @@ -0,0 +1,39 @@ +From 592d52a2596477ab364eef2ff8bb6e62c523dfd1 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:50 +0000 +Subject: [PATCH 26/34] imx-drm: imx-drm-core: get rid of + drm_mode_group_init_legacy_group() + +Since we're now operating like a conventional DRM driver, doing all +the initialisation within the driver's ->load callback, we don't +need to mess around with the mode groups - we can rely on the one +in the DRM platform code. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index b27c425..dd97412 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -274,12 +274,6 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + + drm_kms_helper_poll_init(drm); + +- /* setup the grouping for the legacy output */ +- ret = drm_mode_group_init_legacy_group(drm, +- &drm->primary->mode_group); +- if (ret) +- goto err_kms; +- + ret = drm_vblank_init(drm, MAX_CRTC); + if (ret) + goto err_kms; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0027-imx-drm-imx-drm-core-kill-off-mutex.patch b/patches/imx_drm/0027-imx-drm-imx-drm-core-kill-off-mutex.patch new file mode 100644 index 0000000000000000000000000000000000000000..4979436d2be59bb2ab70c341432650e249d04464 --- /dev/null +++ b/patches/imx_drm/0027-imx-drm-imx-drm-core-kill-off-mutex.patch @@ -0,0 +1,136 @@ +From bb898b9ac39720f30491ca1c3654ddc62a7bd827 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:11:55 +0000 +Subject: [PATCH 27/34] imx-drm: imx-drm-core: kill off mutex + +This mutex doesn't protect anything anymore; get rid of it. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 26 +++----------------------- + 1 file changed, 3 insertions(+), 23 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index dd97412..d5b82cb 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -40,14 +40,12 @@ struct imx_drm_device { + struct drm_device *drm; + struct device *dev; + struct imx_drm_crtc *crtc[MAX_CRTC]; +- struct mutex mutex; + int pipes; + struct drm_fbdev_cma *fbhelper; + }; + + struct imx_drm_crtc { + struct drm_crtc *crtc; +- struct imx_drm_device *imxdrm; + int pipe; + struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; + struct crtc_cookie cookie; +@@ -270,8 +268,6 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + + drm_mode_config_init(drm); + +- mutex_lock(&imxdrm->mutex); +- + drm_kms_helper_poll_init(drm); + + ret = drm_vblank_init(drm, MAX_CRTC); +@@ -287,12 +283,10 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + + platform_set_drvdata(drm->platformdev, drm); + +- mutex_unlock(&imxdrm->mutex); +- + /* Now try and bind all our sub-components */ + ret = component_bind_all(drm->dev, drm); + if (ret) +- goto err_relock; ++ goto err_vblank; + + /* + * All components are now added, we can publish the connector sysfs +@@ -332,13 +326,11 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + + err_unbind: + component_unbind_all(drm->dev, drm); +-err_relock: +- mutex_lock(&imxdrm->mutex); ++err_vblank: + drm_vblank_cleanup(drm); + err_kms: + drm_kms_helper_poll_fini(drm); + drm_mode_config_cleanup(drm); +- mutex_unlock(&imxdrm->mutex); + + return ret; + } +@@ -358,8 +350,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + struct imx_drm_crtc *imx_drm_crtc; + int ret; + +- mutex_lock(&imxdrm->mutex); +- + /* + * The vblank arrays are dimensioned by MAX_CRTC - we can't + * pass IDs greater than this to those functions. +@@ -386,7 +376,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + imx_drm_crtc->cookie.id = id; + imx_drm_crtc->mux_id = imx_drm_crtc->pipe; + imx_drm_crtc->crtc = crtc; +- imx_drm_crtc->imxdrm = imxdrm; + + imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; + +@@ -402,8 +391,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + drm_crtc_init(drm, crtc, + imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs); + +- mutex_unlock(&imxdrm->mutex); +- + return 0; + + err_register: +@@ -411,7 +398,6 @@ err_register: + kfree(imx_drm_crtc); + err_alloc: + err_busy: +- mutex_unlock(&imxdrm->mutex); + return ret; + } + EXPORT_SYMBOL_GPL(imx_drm_add_crtc); +@@ -421,16 +407,12 @@ EXPORT_SYMBOL_GPL(imx_drm_add_crtc); + */ + int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) + { +- struct imx_drm_device *imxdrm = imx_drm_crtc->imxdrm; +- +- mutex_lock(&imxdrm->mutex); ++ struct imx_drm_device *imxdrm = imx_drm_crtc->crtc->dev->dev_private; + + drm_crtc_cleanup(imx_drm_crtc->crtc); + + imxdrm->crtc[imx_drm_crtc->pipe] = NULL; + +- mutex_unlock(&imxdrm->mutex); +- + kfree(imx_drm_crtc); + + return 0; +@@ -657,8 +639,6 @@ static int __init imx_drm_init(void) + if (!imx_drm_device) + return -ENOMEM; + +- mutex_init(&imx_drm_device->mutex); +- + ret = platform_driver_register(&imx_drm_pdrv); + if (ret) + goto err_pdrv; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0028-imx-drm-imx-drm-core-move-allocation-of-imxdrm-devic.patch b/patches/imx_drm/0028-imx-drm-imx-drm-core-move-allocation-of-imxdrm-devic.patch new file mode 100644 index 0000000000000000000000000000000000000000..71129a91d1d95fdd33a3a6f2d94e82ae9983b496 --- /dev/null +++ b/patches/imx_drm/0028-imx-drm-imx-drm-core-move-allocation-of-imxdrm-devic.patch @@ -0,0 +1,110 @@ +From 4bf7aba3833afd64d1bc56e6f1774c47a97d31ca Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:12:00 +0000 +Subject: [PATCH 28/34] imx-drm: imx-drm-core: move allocation of imxdrm device + to driver load function + +It is now no longer necessary to keep this structure around; we can +allocate it upon DRM driver load and destroy it thereafter without +affecting the other components now. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 47 +++++----------------------------- + 1 file changed, 6 insertions(+), 41 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index d5b82cb..35c8f7c 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -38,7 +38,6 @@ struct imx_drm_crtc; + + struct imx_drm_device { + struct drm_device *drm; +- struct device *dev; + struct imx_drm_crtc *crtc[MAX_CRTC]; + int pipes; + struct drm_fbdev_cma *fbhelper; +@@ -206,13 +205,6 @@ int imx_drm_connector_mode_valid(struct drm_connector *connector, + } + EXPORT_SYMBOL(imx_drm_connector_mode_valid); + +-static struct imx_drm_device *imx_drm_device; +- +-static struct imx_drm_device *__imx_drm_device(void) +-{ +- return imx_drm_device; +-} +- + void imx_drm_connector_destroy(struct drm_connector *connector) + { + drm_sysfs_connector_remove(connector); +@@ -236,10 +228,14 @@ static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { + */ + static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + { +- struct imx_drm_device *imxdrm = __imx_drm_device(); ++ struct imx_drm_device *imxdrm; + struct drm_connector *connector; + int ret; + ++ imxdrm = devm_kzalloc(drm->dev, sizeof(*imxdrm), GFP_KERNEL); ++ if (!imxdrm) ++ return -ENOMEM; ++ + imxdrm->drm = drm; + + drm->dev_private = imxdrm; +@@ -604,8 +600,6 @@ static int imx_drm_platform_probe(struct platform_device *pdev) + if (ret) + return ret; + +- imx_drm_device->dev = &pdev->dev; +- + return component_master_add(&pdev->dev, &imx_drm_ops); + } + +@@ -630,36 +624,7 @@ static struct platform_driver imx_drm_pdrv = { + .of_match_table = imx_drm_dt_ids, + }, + }; +- +-static int __init imx_drm_init(void) +-{ +- int ret; +- +- imx_drm_device = kzalloc(sizeof(*imx_drm_device), GFP_KERNEL); +- if (!imx_drm_device) +- return -ENOMEM; +- +- ret = platform_driver_register(&imx_drm_pdrv); +- if (ret) +- goto err_pdrv; +- +- return 0; +- +-err_pdrv: +- kfree(imx_drm_device); +- +- return ret; +-} +- +-static void __exit imx_drm_exit(void) +-{ +- platform_driver_unregister(&imx_drm_pdrv); +- +- kfree(imx_drm_device); +-} +- +-module_init(imx_drm_init); +-module_exit(imx_drm_exit); ++module_platform_driver(imx_drm_pdrv); + + MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); + MODULE_DESCRIPTION("i.MX drm driver core"); +-- +1.8.5.3 + diff --git a/patches/imx_drm/0029-imx-drm-imx-drm-core-various-cleanups.patch b/patches/imx_drm/0029-imx-drm-imx-drm-core-various-cleanups.patch new file mode 100644 index 0000000000000000000000000000000000000000..43aae1848b40b5c38401ba7a9e014af946c96048 --- /dev/null +++ b/patches/imx_drm/0029-imx-drm-imx-drm-core-various-cleanups.patch @@ -0,0 +1,160 @@ +From 992a433091047c6e39a2799dbef9bf641d1a7a39 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:12:05 +0000 +Subject: [PATCH 29/34] imx-drm: imx-drm-core: various cleanups + +Various cleanups are possible after the previous round of changes; these +have no real functional bearing other than tidying up the code. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 47 ++++++++++++---------------------- + drivers/staging/imx-drm/imx-drm.h | 5 ++-- + 2 files changed, 19 insertions(+), 33 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 35c8f7c..7939cea 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -15,12 +15,12 @@ + */ + #include <linux/component.h> + #include <linux/device.h> ++#include <linux/fb.h> ++#include <linux/module.h> + #include <linux/platform_device.h> + #include <drm/drmP.h> + #include <drm/drm_fb_helper.h> + #include <drm/drm_crtc_helper.h> +-#include <linux/fb.h> +-#include <linux/module.h> + #include <drm/drm_gem_cma_helper.h> + #include <drm/drm_fb_cma_helper.h> + +@@ -28,12 +28,6 @@ + + #define MAX_CRTC 4 + +-struct crtc_cookie { +- void *cookie; +- int id; +- struct list_head list; +-}; +- + struct imx_drm_crtc; + + struct imx_drm_device { +@@ -47,7 +41,8 @@ struct imx_drm_crtc { + struct drm_crtc *crtc; + int pipe; + struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; +- struct crtc_cookie cookie; ++ void *cookie; ++ int id; + int mux_id; + }; + +@@ -271,9 +266,9 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + goto err_kms; + + /* +- * with vblank_disable_allowed = true, vblank interrupt will be disabled +- * by drm timer once a current process gives up ownership of +- * vblank event.(after drm_vblank_put function is called) ++ * with vblank_disable_allowed = true, vblank interrupt will be ++ * disabled by drm timer once a current process gives up ownership ++ * of vblank event. (after drm_vblank_put function is called) + */ + drm->vblank_disable_allowed = true; + +@@ -350,26 +345,20 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + * The vblank arrays are dimensioned by MAX_CRTC - we can't + * pass IDs greater than this to those functions. + */ +- if (imxdrm->pipes >= MAX_CRTC) { +- ret = -EINVAL; +- goto err_busy; +- } ++ if (imxdrm->pipes >= MAX_CRTC) ++ return -EINVAL; + +- if (imxdrm->drm->open_count) { +- ret = -EBUSY; +- goto err_busy; +- } ++ if (imxdrm->drm->open_count) ++ return -EBUSY; + + imx_drm_crtc = kzalloc(sizeof(*imx_drm_crtc), GFP_KERNEL); +- if (!imx_drm_crtc) { +- ret = -ENOMEM; +- goto err_alloc; +- } ++ if (!imx_drm_crtc) ++ return -ENOMEM; + + imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; + imx_drm_crtc->pipe = imxdrm->pipes++; +- imx_drm_crtc->cookie.cookie = cookie; +- imx_drm_crtc->cookie.id = id; ++ imx_drm_crtc->cookie = cookie; ++ imx_drm_crtc->id = id; + imx_drm_crtc->mux_id = imx_drm_crtc->pipe; + imx_drm_crtc->crtc = crtc; + +@@ -392,8 +381,6 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + err_register: + imxdrm->crtc[imx_drm_crtc->pipe] = NULL; + kfree(imx_drm_crtc); +-err_alloc: +-err_busy: + return ret; + } + EXPORT_SYMBOL_GPL(imx_drm_add_crtc); +@@ -429,8 +416,8 @@ static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, + + for (i = 0; i < MAX_CRTC; i++) { + struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; +- if (imx_drm_crtc && imx_drm_crtc->cookie.id == id && +- imx_drm_crtc->cookie.cookie == cookie) ++ if (imx_drm_crtc && imx_drm_crtc->id == id && ++ imx_drm_crtc->cookie == cookie) + return drm_crtc_mask(imx_drm_crtc->crtc); + } + +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index ae9c96d..aa21028 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -5,14 +5,15 @@ + + #define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3') + ++struct device_node; + struct drm_crtc; + struct drm_connector; + struct drm_device; + struct drm_display_mode; + struct drm_encoder; +-struct imx_drm_crtc; + struct drm_fbdev_cma; + struct drm_framebuffer; ++struct imx_drm_crtc; + struct platform_device; + + int imx_drm_crtc_id(struct imx_drm_crtc *crtc); +@@ -48,8 +49,6 @@ int imx_drm_panel_format_pins(struct drm_encoder *encoder, + int imx_drm_panel_format(struct drm_encoder *encoder, + u32 interface_pix_fmt); + +-struct device_node; +- + int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder); + int imx_drm_encoder_parse_of(struct drm_device *drm, + struct drm_encoder *encoder, struct device_node *np); +-- +1.8.5.3 + diff --git a/patches/imx_drm/0030-imx-drm-imx-drm-core-add-core-hotplug-connector-supp.patch b/patches/imx_drm/0030-imx-drm-imx-drm-core-add-core-hotplug-connector-supp.patch new file mode 100644 index 0000000000000000000000000000000000000000..ceb257b504cccc7adbea82b0aafb681ffb1ec5f2 --- /dev/null +++ b/patches/imx_drm/0030-imx-drm-imx-drm-core-add-core-hotplug-connector-supp.patch @@ -0,0 +1,91 @@ +From b7c8c869f298d28f9475a66d66bdded6cfc8f92e Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:12:10 +0000 +Subject: [PATCH 30/34] imx-drm: imx-drm-core: add core hotplug connector + support + +Add core imx-drm support for hotplug connector support. We need to +setup the poll helper after we've setup the connectors; the helper +scans the connectors to determine their capabilities. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-drm-core.c | 21 +++++++++++++++++---- + 1 file changed, 17 insertions(+), 4 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 7939cea..dcba518 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -69,7 +69,11 @@ static int imx_drm_driver_unload(struct drm_device *drm) + { + #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) + struct imx_drm_device *imxdrm = drm->dev_private; ++#endif ++ ++ drm_kms_helper_poll_fini(drm); + ++#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) + if (imxdrm->fbhelper) + drm_fbdev_cma_fini(imxdrm->fbhelper); + #endif +@@ -77,7 +81,6 @@ static int imx_drm_driver_unload(struct drm_device *drm) + component_unbind_all(drm->dev, drm); + + drm_vblank_cleanup(drm); +- drm_kms_helper_poll_fini(drm); + drm_mode_config_cleanup(drm); + + return 0; +@@ -213,8 +216,18 @@ void imx_drm_encoder_destroy(struct drm_encoder *encoder) + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); + ++static void imx_drm_output_poll_changed(struct drm_device *drm) ++{ ++#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) ++ struct imx_drm_device *imxdrm = drm->dev_private; ++ ++ drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); ++#endif ++} ++ + static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { + .fb_create = drm_fb_cma_create, ++ .output_poll_changed = imx_drm_output_poll_changed, + }; + + /* +@@ -259,8 +272,6 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + + drm_mode_config_init(drm); + +- drm_kms_helper_poll_init(drm); +- + ret = drm_vblank_init(drm, MAX_CRTC); + if (ret) + goto err_kms; +@@ -313,6 +324,9 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) + goto err_unbind; + } + #endif ++ ++ drm_kms_helper_poll_init(drm); ++ + return 0; + + err_unbind: +@@ -320,7 +334,6 @@ err_unbind: + err_vblank: + drm_vblank_cleanup(drm); + err_kms: +- drm_kms_helper_poll_fini(drm); + drm_mode_config_cleanup(drm); + + return ret; +-- +1.8.5.3 + diff --git a/patches/imx_drm/0031-imx-drm-imx-hdmi-add-hotplug-support-to-HDMI-compone.patch b/patches/imx_drm/0031-imx-drm-imx-hdmi-add-hotplug-support-to-HDMI-compone.patch new file mode 100644 index 0000000000000000000000000000000000000000..54783087ebf95b0daa299890dbe2387d593474c6 --- /dev/null +++ b/patches/imx_drm/0031-imx-drm-imx-hdmi-add-hotplug-support-to-HDMI-compone.patch @@ -0,0 +1,151 @@ +From de50a3caeba3881bc646e896569abf0a3640efd4 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:12:15 +0000 +Subject: [PATCH 31/34] imx-drm: imx-hdmi: add hotplug support to HDMI + component + +Add hotplug support. We have to make the interrupt handler threaded so +we can call drm_helper_hpd_irq_event(). Keeping in mind that we will +want to share the interrupt with other HDMI interface drivers (eg, audio +and CEC) put the groundwork in now for that, rather than just using +IRQF_ONESHOT. + +Also, we must not call drm_helper_hpd_irq_event() until we have fully +setup the connector; keep the interrupt(s) muted until after that point. + +Acked-by: Philipp Zabel <p.zabel@pengutronix.de> +Acked-by: Shawn Guo <shawn.guo@linaro.org> +Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/imx-hdmi.c | 40 +++++++++++++++++++++++++++++++------- + 1 file changed, 33 insertions(+), 7 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 8c58645..ab16aba 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -120,6 +120,8 @@ struct imx_hdmi { + struct clk *isfr_clk; + struct clk *iahb_clk; + ++ enum drm_connector_status connector_status; ++ + struct hdmi_data_info hdmi_data; + int vic; + +@@ -1301,9 +1303,6 @@ static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi) + /* Clear Hotplug interrupts */ + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + +- /* Unmute interrupts */ +- hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); +- + return 0; + } + +@@ -1372,8 +1371,9 @@ static void imx_hdmi_poweroff(struct imx_hdmi *hdmi) + static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector + *connector, bool force) + { +- /* FIXME */ +- return connector_status_connected; ++ struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, ++ connector); ++ return hdmi->connector_status; + } + + static int imx_hdmi_connector_get_modes(struct drm_connector *connector) +@@ -1487,6 +1487,18 @@ static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { + .best_encoder = imx_hdmi_connector_best_encoder, + }; + ++static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id) ++{ ++ struct imx_hdmi *hdmi = dev_id; ++ u8 intr_stat; ++ ++ intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); ++ if (intr_stat) ++ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); ++ ++ return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE; ++} ++ + static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) + { + struct imx_hdmi *hdmi = dev_id; +@@ -1503,17 +1515,21 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) + + hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0); + ++ hdmi->connector_status = connector_status_connected; + imx_hdmi_poweron(hdmi); + } else { + dev_dbg(hdmi->dev, "EVENT=plugout\n"); + + hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, HDMI_PHY_POL0); + ++ hdmi->connector_status = connector_status_disconnected; + imx_hdmi_poweroff(hdmi); + } ++ drm_helper_hpd_irq_event(hdmi->connector.dev); + } + + hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); ++ hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + + return IRQ_HANDLED; + } +@@ -1527,6 +1543,8 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi) + if (ret) + return ret; + ++ hdmi->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); +@@ -1578,6 +1596,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + return -ENOMEM; + + hdmi->dev = dev; ++ hdmi->connector_status = connector_status_disconnected; + hdmi->sample_rate = 48000; + hdmi->ratio = 100; + +@@ -1601,8 +1620,9 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + if (irq < 0) + return -EINVAL; + +- ret = devm_request_irq(dev, irq, imx_hdmi_irq, 0, +- dev_name(dev), hdmi); ++ ret = devm_request_threaded_irq(dev, irq, imx_hdmi_hardirq, ++ imx_hdmi_irq, IRQF_SHARED, ++ dev_name(dev), hdmi); + if (ret) + return ret; + +@@ -1678,6 +1698,9 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + if (ret) + goto err_iahb; + ++ /* Unmute interrupts */ ++ hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); ++ + dev_set_drvdata(dev, hdmi); + + return 0; +@@ -1695,6 +1718,9 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master, + { + struct imx_hdmi *hdmi = dev_get_drvdata(dev); + ++ /* Disable all interrupts */ ++ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); ++ + hdmi->connector.funcs->destroy(&hdmi->connector); + hdmi->encoder.funcs->destroy(&hdmi->encoder); + +-- +1.8.5.3 + diff --git a/patches/imx_drm/0032-imx-drm-dw-hdmi-audio-add-audio-driver.patch b/patches/imx_drm/0032-imx-drm-dw-hdmi-audio-add-audio-driver.patch new file mode 100644 index 0000000000000000000000000000000000000000..988d3f2a10e2117901dce9a1a44fe2bb0d8b049f --- /dev/null +++ b/patches/imx_drm/0032-imx-drm-dw-hdmi-audio-add-audio-driver.patch @@ -0,0 +1,651 @@ +From 70d75ac722650442c2ef8831adb340202a25bcea Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:12:20 +0000 +Subject: [PATCH 32/34] imx-drm: dw-hdmi-audio: add audio driver + +Add ALSA based HDMI audio driver for imx-hdmi. The imx-hdmi is a +Synopsis DesignWare module, so let's name it after that. The only +buffer format supported is its own special IEC958 based format, which +is not compatible with any ALSA format. To avoid doing too much data +manipulation within the driver, we support only ALSAs IEC958 LE, and +24-bit PCM formats for 2 to 6 channels. + +This allows us to modify the buffer in place as each period is passed +for DMA without needing a separate buffer. + +A more desirable solution would be to have this conversion in userspace, +but ALSA does not appear to allow such transformations outside of +libasound itself. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/Makefile | 3 +- + drivers/staging/imx-drm/dw-hdmi-audio.c | 499 ++++++++++++++++++++++++++++++++ + drivers/staging/imx-drm/dw-hdmi-audio.h | 13 + + drivers/staging/imx-drm/imx-hdmi.c | 21 ++ + drivers/staging/imx-drm/imx-hdmi.h | 4 + + 5 files changed, 539 insertions(+), 1 deletion(-) + create mode 100644 drivers/staging/imx-drm/dw-hdmi-audio.c + create mode 100644 drivers/staging/imx-drm/dw-hdmi-audio.h + +diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile +index 129e3a3..f554aa6 100644 +--- a/drivers/staging/imx-drm/Makefile ++++ b/drivers/staging/imx-drm/Makefile +@@ -1,5 +1,6 @@ + + imxdrm-objs := imx-drm-core.o ++imxhdmi-objs := imx-hdmi.o dw-hdmi-audio.o + + obj-$(CONFIG_DRM_IMX) += imxdrm.o + +@@ -10,4 +11,4 @@ obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/ + + imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o + obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o +-obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o ++obj-$(CONFIG_DRM_IMX_HDMI) += imxhdmi.o +diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c +new file mode 100644 +index 0000000..a17714d +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-audio.c +@@ -0,0 +1,499 @@ ++/* ++ * 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/io.h> ++#include <linux/interrupt.h> ++ ++#include <sound/asoundef.h> ++#include <sound/core.h> ++#include <sound/initval.h> ++#include <sound/pcm.h> ++ ++#include "imx-hdmi.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 = HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK, ++ HDMI_AHB_DMA_CONF0_EN_HLOCK = HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK, ++ HDMI_AHB_DMA_START_START = BIT(HDMI_AHB_DMA_START_START_OFFSET), ++ HDMI_AHB_DMA_STOP_STOP = BIT(HDMI_AHB_DMA_STOP_STOP_OFFSET), ++ 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_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, ++}; ++ ++struct snd_dw_hdmi { ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ void __iomem *base; ++ int irq; ++ struct imx_hdmi *hdmi; ++ struct snd_pcm_substream *substream; ++ void (*reformat)(struct snd_dw_hdmi *, size_t, size_t); ++ void *buf_base; ++ 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_writeb(unsigned long val, void __iomem *ptr) ++{ ++ writeb(val, ptr); ++} ++ ++static unsigned dw_hdmi_readb(void __iomem *ptr) ++{ ++ return readb(ptr); ++} ++ ++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 *ptr = dw->buf_base + offset; ++ uint32_t *end = dw->buf_base + offset + bytes; ++ ++ do { ++ uint32_t b, sample = *ptr; ++ ++ b = (sample & 8) << (28 - 3); ++ ++ sample >>= 4; ++ ++ *ptr++ = sample | b; ++ } while (ptr < 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 *ptr = dw->buf_base + offset; ++ uint32_t *end = dw->buf_base + 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 = *ptr; ++ ++ sample &= ~0xff000000; ++ sample |= *cs++ << 24; ++ sample |= parity(sample & ~0xf8000000); ++ ++ *ptr++ = sample; ++ } while (--i); ++ } while (ptr < end); ++} ++ ++static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw, ++ struct snd_pcm_runtime *runtime) ++{ ++ uint8_t cs[3]; ++ 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) ++{ ++ unsigned long start, stop; ++ ++ start = dw->buf_addr + dw->buf_offset; ++ stop = start + dw->buf_period - 1; ++ ++ dw->reformat(dw, dw->buf_offset, dw->buf_period); ++ ++ /* Setup the hardware start/stop addresses */ ++ dw_hdmi_writel(start, dw->base + HDMI_AHB_DMA_STRADDR0); ++ dw_hdmi_writel(stop, dw->base + HDMI_AHB_DMA_STPADDR0); ++ ++ /* Clear all irqs before enabling irqs and starting DMA */ ++ dw_hdmi_writeb(HDMI_IH_AHBDMAAUD_STAT0_ALL, ++ dw->base + HDMI_IH_AHBDMAAUD_STAT0); ++ dw_hdmi_writeb(~HDMI_AHB_DMA_DONE, dw->base + HDMI_AHB_DMA_MASK); ++ dw_hdmi_writeb(HDMI_AHB_DMA_START_START, dw->base + HDMI_AHB_DMA_START); ++} ++ ++static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw) ++{ ++ dw->substream = NULL; ++ ++ /* Disable interrupts before disabling DMA */ ++ dw_hdmi_writeb(~0, dw->base + HDMI_AHB_DMA_MASK); ++ dw_hdmi_writeb(HDMI_AHB_DMA_STOP_STOP, dw->base + HDMI_AHB_DMA_STOP); ++} ++ ++static irqreturn_t snd_dw_hdmi_irq(int irq, void *data) ++{ ++ struct snd_dw_hdmi *dw = data; ++ struct snd_pcm_substream *substream; ++ unsigned stat; ++ ++ stat = dw_hdmi_readb(dw->base + HDMI_IH_AHBDMAAUD_STAT0); ++ if (!stat) ++ return IRQ_NONE; ++ ++ dw_hdmi_writeb(stat, dw->base + HDMI_IH_AHBDMAAUD_STAT0); ++ ++ substream = dw->substream; ++ if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) { ++ dw->buf_offset += dw->buf_period; ++ if (dw->buf_offset >= dw->buf_size) ++ dw->buf_offset = 0; ++ ++ 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 int dw_hdmi_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_dw_hdmi *dw = substream->private_data; ++ int ret; ++ ++ /* Clear FIFO */ ++ dw_hdmi_writeb(HDMI_AHB_DMA_CONF0_SW_FIFO_RST, ++ dw->base + HDMI_AHB_DMA_CONF0); ++ ++ /* Configure interrupt polarities */ ++ dw_hdmi_writeb(~0, dw->base + HDMI_AHB_DMA_POL); ++ dw_hdmi_writeb(~0, dw->base + HDMI_AHB_DMA_BUFFPOL); ++ ++ /* Keep interrupts masked */ ++ dw_hdmi_writeb(~0, dw->base + HDMI_AHB_DMA_MASK); ++ ++ ret = request_irq(dw->irq, snd_dw_hdmi_irq, IRQF_SHARED, ++ "dw-hdmi-audio", dw); ++ if (ret) ++ return ret; ++ ++ /* Un-mute done interrupt */ ++ dw_hdmi_writeb(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL & ++ ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE, ++ dw->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); ++ ++ runtime->hw = dw_hdmi_hw; ++ snd_pcm_limit_hw_rates(runtime); ++ ++ return 0; ++} ++ ++static int dw_hdmi_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_dw_hdmi *dw = substream->private_data; ++ ++ /* Mute all interrupts */ ++ dw_hdmi_writeb(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, ++ dw->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); ++ ++ free_irq(dw->irq, dw); ++ ++ return 0; ++} ++ ++static int dw_hdmi_hw_free(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++static int dw_hdmi_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ return snd_pcm_lib_malloc_pages(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; ++ } ++ ++ imx_hdmi_set_sample_rate(dw->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; ++ ++ dw_hdmi_writeb(threshold, dw->base + HDMI_AHB_DMA_THRSLD); ++ dw_hdmi_writeb(conf0, dw->base + HDMI_AHB_DMA_CONF0); ++ dw_hdmi_writeb(conf1, dw->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_base = runtime->dma_area; ++ dw->buf_addr = runtime->dma_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; ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ dw->buf_offset = 0; ++ dw->substream = substream; ++ dw_hdmi_start_dma(dw); ++ 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, ++}; ++ ++int snd_dw_hdmi_probe(struct snd_dw_hdmi **dwp, struct device *dev, ++ void __iomem *base, int irq, struct imx_hdmi *hdmi) ++{ ++ struct snd_dw_hdmi *dw; ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ unsigned revision; ++ int ret; ++ ++ dw_hdmi_writeb(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL, ++ base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); ++ revision = dw_hdmi_readb(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_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, ++ THIS_MODULE, sizeof(struct snd_dw_hdmi), &card); ++ if (ret < 0) ++ return ret; ++ ++ snd_card_set_dev(card, dev); ++ ++ 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, irq); ++ ++ dw = card->private_data; ++ dw->card = card; ++ dw->base = base; ++ dw->irq = irq; ++ dw->hdmi = hdmi; ++ 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, ++ NULL, 0, 64 * 1024); ++ ++ ret = snd_card_register(card); ++ if (ret < 0) ++ goto err; ++ ++ *dwp = dw; ++ ++ return 0; ++ ++err: ++ snd_card_free(card); ++ return ret; ++} ++ ++void snd_dw_hdmi_remove(struct snd_dw_hdmi *dw) ++{ ++ snd_card_free(dw->card); ++} +diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.h b/drivers/staging/imx-drm/dw-hdmi-audio.h +new file mode 100644 +index 0000000..82a709c +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-audio.h +@@ -0,0 +1,13 @@ ++#ifndef DW_HDMI_AUDIO_H ++#define DW_HDMI_AUDIO_H ++ ++#include <linux/irqreturn.h> ++ ++struct snd_dw_hdmi; ++struct imx_hdmi; ++ ++int snd_dw_hdmi_probe(struct snd_dw_hdmi **dwp, struct device *, ++ void __iomem *, int, struct imx_hdmi *); ++void snd_dw_hdmi_remove(struct snd_dw_hdmi *dw); ++ ++#endif +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index ab16aba..5755368 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -28,6 +28,7 @@ + #include <drm/drm_edid.h> + #include <drm/drm_encoder_slave.h> + ++#include "dw-hdmi-audio.h" + #include "ipu-v3/imx-ipu-v3.h" + #include "imx-hdmi.h" + #include "imx-drm.h" +@@ -115,6 +116,7 @@ struct imx_hdmi { + struct drm_connector connector; + struct drm_encoder encoder; + ++ struct snd_dw_hdmi *audio; + enum imx_hdmi_devtype dev_type; + struct device *dev; + struct clk *isfr_clk; +@@ -362,6 +364,13 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi) + hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); + } + ++void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned int rate) ++{ ++ hdmi->sample_rate = rate; ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); ++} ++EXPORT_SYMBOL(imx_hdmi_set_sample_rate); ++ + /* + * this submodule is responsible for the video data synchronization. + * for example, for RGB 4:4:4 input, the data map is defined as +@@ -1701,10 +1710,20 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + /* Unmute interrupts */ + hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + ++ ret = snd_dw_hdmi_probe(&hdmi->audio, dev, hdmi->regs, irq, hdmi); ++ if (ret) ++ goto err_audio; ++ + dev_set_drvdata(dev, hdmi); + + return 0; + ++err_audio: ++ /* Disable all interrupts */ ++ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); ++ ++ hdmi->connector.funcs->destroy(&hdmi->connector); ++ hdmi->encoder.funcs->destroy(&hdmi->encoder); + err_iahb: + clk_disable_unprepare(hdmi->iahb_clk); + err_isfr: +@@ -1718,6 +1737,8 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master, + { + struct imx_hdmi *hdmi = dev_get_drvdata(dev); + ++ snd_dw_hdmi_remove(hdmi->audio); ++ + /* Disable all interrupts */ + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + +diff --git a/drivers/staging/imx-drm/imx-hdmi.h b/drivers/staging/imx-drm/imx-hdmi.h +index 39b6776..8029feb 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.h ++++ b/drivers/staging/imx-drm/imx-hdmi.h +@@ -1029,4 +1029,8 @@ enum { + HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2, + HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0, + }; ++ ++struct imx_hdmi; ++void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned int rate); ++ + #endif /* __IMX_HDMI_H__ */ +-- +1.8.5.3 + diff --git a/patches/imx_drm/0033-imx-drm-dw-hdmi-audio-parse-ELD-from-HDMI-driver.patch b/patches/imx_drm/0033-imx-drm-dw-hdmi-audio-parse-ELD-from-HDMI-driver.patch new file mode 100644 index 0000000000000000000000000000000000000000..8707092c7deb6e5376ed51c23a3d512009bb9473 --- /dev/null +++ b/patches/imx_drm/0033-imx-drm-dw-hdmi-audio-parse-ELD-from-HDMI-driver.patch @@ -0,0 +1,126 @@ +From 2a7d2ce84f05fd886c20d3b7b237a8d0c933820a Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:12:25 +0000 +Subject: [PATCH 33/34] imx-drm: dw-hdmi-audio: parse ELD from HDMI driver + +Parse the ELD (EDID like data) stored from the HDMI driver to restrict +the sample rates and channels which are available to ALSA. This causes +the ALSA device to reflect the capabilities of the overall audio path, +not just what is supported at the HDMI source interface level. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/dw-hdmi-audio.c | 51 +++++++++++++++++++++++++++++++++ + drivers/staging/imx-drm/imx-hdmi.c | 8 ++++++ + drivers/staging/imx-drm/imx-hdmi.h | 1 + + 3 files changed, 60 insertions(+) + +diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c +index a17714d..7bff8c9 100644 +--- a/drivers/staging/imx-drm/dw-hdmi-audio.c ++++ b/drivers/staging/imx-drm/dw-hdmi-audio.c +@@ -273,6 +273,56 @@ static struct snd_pcm_hardware dw_hdmi_hw = { + .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) ++{ ++ uint8_t *sad, *eld = imx_hdmi_get_eld(dw->hdmi); ++ 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; +@@ -301,6 +351,7 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream) + dw->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0); + + runtime->hw = dw_hdmi_hw; ++ dw_hdmi_parse_eld(dw, runtime); + snd_pcm_limit_hw_rates(runtime); + + return 0; +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 5755368..df20f94 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -371,6 +371,12 @@ void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned int rate) + } + EXPORT_SYMBOL(imx_hdmi_set_sample_rate); + ++uint8_t *imx_hdmi_get_eld(struct imx_hdmi *hdmi) ++{ ++ return hdmi->connector.eld; ++} ++EXPORT_SYMBOL(imx_hdmi_get_eld); ++ + /* + * this submodule is responsible for the video data synchronization. + * for example, for RGB 4:4:4 input, the data map is defined as +@@ -1402,6 +1408,8 @@ static int imx_hdmi_connector_get_modes(struct drm_connector *connector) + + 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); + } else { + dev_dbg(hdmi->dev, "failed to get edid\n"); +diff --git a/drivers/staging/imx-drm/imx-hdmi.h b/drivers/staging/imx-drm/imx-hdmi.h +index 8029feb..5baaa9c 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.h ++++ b/drivers/staging/imx-drm/imx-hdmi.h +@@ -1032,5 +1032,6 @@ enum { + + struct imx_hdmi; + void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned int rate); ++uint8_t *imx_hdmi_get_eld(struct imx_hdmi *hdmi); + + #endif /* __IMX_HDMI_H__ */ +-- +1.8.5.3 + diff --git a/patches/imx_drm/0034-imx-drm-add-CEC-HDMI-driver.patch b/patches/imx_drm/0034-imx-drm-add-CEC-HDMI-driver.patch new file mode 100644 index 0000000000000000000000000000000000000000..a6913de314861d3fc3e926c0b194e65c4a17f204 --- /dev/null +++ b/patches/imx_drm/0034-imx-drm-add-CEC-HDMI-driver.patch @@ -0,0 +1,800 @@ +From 32f4c5063d0b0caa6347660784adb4cbb005b984 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Tue, 18 Feb 2014 20:12:31 +0000 +Subject: [PATCH 34/34] imx-drm: add CEC HDMI driver + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/staging/imx-drm/Kconfig | 8 + + drivers/staging/imx-drm/Makefile | 1 + + drivers/staging/imx-drm/dw-hdmi-cec.c | 561 ++++++++++++++++++++++++++++++++++ + drivers/staging/imx-drm/dw-hdmi-cec.h | 16 + + drivers/staging/imx-drm/imx-hdmi.c | 66 +++- + drivers/staging/imx-drm/imx-hdmi.h | 3 + + 6 files changed, 645 insertions(+), 10 deletions(-) + create mode 100644 drivers/staging/imx-drm/dw-hdmi-cec.c + create mode 100644 drivers/staging/imx-drm/dw-hdmi-cec.h + +diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig +index 78319ad..8f4c568 100644 +--- a/drivers/staging/imx-drm/Kconfig ++++ b/drivers/staging/imx-drm/Kconfig +@@ -59,3 +59,11 @@ config DRM_IMX_HDMI + depends on DRM_IMX + help + Choose this if you want to use HDMI on i.MX6. ++ ++config DRM_DW_HDMI_CEC ++ tristate "Synopsis Designware CEC interface" ++ depends on DRM_IMX_HDMI != n ++ help ++ Support the CEC interface which is part of the Synposis ++ Designware HDMI block. This is used in conjunction with ++ the i.MX HDMI driver. +diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile +index f554aa6..53bc725 100644 +--- a/drivers/staging/imx-drm/Makefile ++++ b/drivers/staging/imx-drm/Makefile +@@ -12,3 +12,4 @@ obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/ + imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o + obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o + obj-$(CONFIG_DRM_IMX_HDMI) += imxhdmi.o ++obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o +diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.c b/drivers/staging/imx-drm/dw-hdmi-cec.c +new file mode 100644 +index 0000000..fee508d +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-cec.c +@@ -0,0 +1,561 @@ ++/* 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/cdev.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/platform_device.h> ++#include <linux/poll.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/wait.h> ++ ++#include "imx-hdmi.h" ++#include "dw-hdmi-cec.h" ++ ++#define MAX_MESSAGE_LEN 16 ++#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, ++}; ++ ++static struct class *cec_class; ++static int cec_major; ++ ++struct dw_hdmi_cec { ++ struct device *dev; ++ struct cdev cdev; ++ void __iomem *base; ++ const struct dw_hdmi_cec_ops *ops; ++ void *ops_data; ++ int irq; ++ ++ struct mutex mutex; ++ unsigned users; ++ ++ spinlock_t lock; ++ wait_queue_head_t waitq; ++ struct list_head events; ++ uint8_t write_busy; ++ ++ uint8_t retries; ++ uint16_t addresses; ++ uint16_t physical; ++}; ++ ++enum { ++ MESSAGE_TYPE_RECEIVE_SUCCESS = 1, ++ MESSAGE_TYPE_NOACK, ++ MESSAGE_TYPE_DISCONNECTED, ++ MESSAGE_TYPE_CONNECTED, ++ MESSAGE_TYPE_SEND_SUCCESS, ++ MESSAGE_TYPE_SEND_ERROR, ++}; ++ ++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]), ++}; ++ ++struct dw_hdmi_cec_user_event { ++ uint32_t event_type; ++ uint32_t msg_len; ++ uint8_t msg[MAX_MESSAGE_LEN]; ++}; ++ ++struct dw_hdmi_cec_event { ++ struct dw_hdmi_cec_user_event usr; ++ struct list_head node; ++}; ++ ++static void dw_hdmi_event(struct dw_hdmi_cec *cec, int type) ++{ ++ struct dw_hdmi_cec_event *event; ++ unsigned long flags; ++ ++ event = kzalloc(sizeof(*event), GFP_ATOMIC); ++ if (event) { ++ event->usr.event_type = type; ++ ++ if (type == MESSAGE_TYPE_RECEIVE_SUCCESS) { ++ unsigned i; ++ ++ event->usr.msg_len = readb(cec->base + HDMI_CEC_RX_CNT); ++ ++ for (i = 0; i < event->usr.msg_len; i++) ++ event->usr.msg[i] = readb(cec->base + HDMI_CEC_RX_DATA0 + i); ++ ++ writeb(0, cec->base + HDMI_CEC_LOCK); ++ } ++ ++ spin_lock_irqsave(&cec->lock, flags); ++ list_add_tail(&event->node, &cec->events); ++ spin_unlock_irqrestore(&cec->lock, flags); ++ wake_up(&cec->waitq); ++ } ++} ++ ++static void dw_hdmi_set_address(struct dw_hdmi_cec *cec) ++{ ++ writeb(cec->addresses & 255, cec->base + HDMI_CEC_ADDR_L); ++ writeb(cec->addresses >> 8, cec->base + HDMI_CEC_ADDR_H); ++} ++ ++static void dw_hdmi_send_message(struct dw_hdmi_cec *cec, uint8_t *msg, ++ size_t count) ++{ ++ unsigned long flags; ++ 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); ++ ++ spin_lock_irqsave(&cec->lock, flags); ++ cec->retries = 5; ++ cec->write_busy = 1; ++ writeb(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL); ++ spin_unlock_irqrestore(&cec->lock, flags); ++} ++ ++static int dw_hdmi_lock_write(struct dw_hdmi_cec *cec, struct file *file) ++ __acquires(cec->mutex) ++{ ++ int ret; ++ ++ do { ++ if (file->f_flags & O_NONBLOCK) { ++ if (cec->write_busy) ++ return -EAGAIN; ++ } else { ++ ret = wait_event_interruptible(cec->waitq, ++ !cec->write_busy); ++ if (ret) ++ break; ++ } ++ ++ ret = mutex_lock_interruptible(&cec->mutex); ++ if (ret) ++ break; ++ ++ if (!cec->write_busy) ++ break; ++ ++ mutex_unlock(&cec->mutex); ++ } while (1); ++ ++ return ret; ++} ++ ++static irqreturn_t dw_hdmi_cec_irq(int irq, void *data) ++{ ++ struct dw_hdmi_cec *cec = data; ++ 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->retries) { ++ unsigned v = readb(cec->base + HDMI_CEC_CTRL); ++ writeb(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL); ++ cec->retries -= 1; ++ } else { ++ cec->write_busy = 0; ++ dw_hdmi_event(cec, MESSAGE_TYPE_SEND_ERROR); ++ } ++ } else if (stat & (CEC_STAT_DONE | CEC_STAT_NACK)) { ++ cec->retries = 0; ++ cec->write_busy = 0; ++ if (stat & CEC_STAT_DONE) { ++ dw_hdmi_event(cec, MESSAGE_TYPE_SEND_SUCCESS); ++ } else { ++ dw_hdmi_event(cec, MESSAGE_TYPE_NOACK); ++ } ++ } ++ ++ if (stat & CEC_STAT_EOM) ++ dw_hdmi_event(cec, MESSAGE_TYPE_RECEIVE_SUCCESS); ++ ++ return IRQ_HANDLED; ++} ++EXPORT_SYMBOL(dw_hdmi_cec_irq); ++ ++static ssize_t dw_hdmi_cec_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dw_hdmi_cec *cec = file->private_data; ++ ssize_t ret; ++ ++ if (count > sizeof(struct dw_hdmi_cec_user_event)) ++ count = sizeof(struct dw_hdmi_cec_user_event); ++ ++ if (!access_ok(VERIFY_WRITE, buf, count)) ++ return -EFAULT; ++ ++ do { ++ struct dw_hdmi_cec_event *event = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cec->lock, flags); ++ if (!list_empty(&cec->events)) { ++ event = list_first_entry(&cec->events, ++ struct dw_hdmi_cec_event, node); ++ list_del(&event->node); ++ } ++ spin_unlock_irqrestore(&cec->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->waitq, ++ !list_empty(&cec->events)); ++ if (ret) ++ break; ++ } while (1); ++ ++ return ret; ++} ++ ++static ssize_t dw_hdmi_cec_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dw_hdmi_cec *cec = file->private_data; ++ uint8_t msg[MAX_MESSAGE_LEN]; ++ int ret; ++ ++ if (count > sizeof(msg)) ++ return -E2BIG; ++ ++ if (copy_from_user(msg, buf, count)) ++ return -EFAULT; ++ ++ ret = dw_hdmi_lock_write(cec, file); ++ if (ret) ++ return ret; ++ ++ dw_hdmi_send_message(cec, msg, count); ++ ++ mutex_unlock(&cec->mutex); ++ ++ return count; ++} ++ ++static long dw_hdmi_cec_ioctl(struct file *file, u_int cmd, unsigned long arg) ++{ ++ struct dw_hdmi_cec *cec = file->private_data; ++ int ret; ++ ++ switch (cmd) { ++ case HDMICEC_IOC_O_SETLOGICALADDRESS: ++ case HDMICEC_IOC_SETLOGICALADDRESS: ++ if (arg > 15) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ ret = dw_hdmi_lock_write(cec, file); ++ if (ret == 0) { ++ unsigned char msg[1]; ++ ++ cec->addresses = BIT(arg); ++ dw_hdmi_set_address(cec); ++ ++ /* ++ * 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; ++ dw_hdmi_send_message(cec, msg, sizeof(msg)); ++ mutex_unlock(&cec->mutex); ++ } ++ break; ++ ++ case HDMICEC_IOC_STARTDEVICE: ++ ret = mutex_lock_interruptible(&cec->mutex); ++ if (ret == 0) { ++ cec->addresses = BIT(15); ++ dw_hdmi_set_address(cec); ++ mutex_unlock(&cec->mutex); ++ } ++ break; ++ ++ case HDMICEC_IOC_STOPDEVICE: ++ ret = 0; ++ break; ++ ++ case HDMICEC_IOC_GETPHYADDRESS: ++ ret = put_user(cec->physical, (uint16_t __user *)arg); ++ ret = -ENOIOCTLCMD; ++ break; ++ ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ ++ return ret; ++} ++ ++static unsigned dw_hdmi_cec_poll(struct file *file, poll_table *wait) ++{ ++ struct dw_hdmi_cec *cec = file->private_data; ++ unsigned mask = 0; ++ ++ poll_wait(file, &cec->waitq, wait); ++ ++ if (cec->write_busy == 0) ++ mask |= POLLOUT | POLLWRNORM; ++ if (!list_empty(&cec->events)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ return mask; ++} ++ ++static int dw_hdmi_cec_open(struct inode *inode, struct file *file) ++{ ++ struct dw_hdmi_cec *cec = container_of(inode->i_cdev, ++ struct dw_hdmi_cec, cdev); ++ int ret = 0; ++ ++ nonseekable_open(inode, file); ++ ++ file->private_data = cec; ++ ++ ret = mutex_lock_interruptible(&cec->mutex); ++ if (ret) ++ return ret; ++ ++ if (cec->users++ == 0) { ++ unsigned irqs; ++ ++ 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) { ++ cec->users = 0; ++ goto unlock; ++ } ++ ++ cec->addresses = BIT(15); ++ dw_hdmi_set_address(cec); ++ ++ 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); ++ } ++ unlock: ++ mutex_unlock(&cec->mutex); ++ ++ return ret; ++} ++ ++static int dw_hdmi_cec_release(struct inode *inode, struct file *file) ++{ ++ struct dw_hdmi_cec *cec = file->private_data; ++ ++ mutex_lock(&cec->mutex); ++ if (cec->users >= 1) ++ cec->users -= 1; ++ if (cec->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->waitq, !cec->write_busy, ++ msecs_to_jiffies(500)); ++ ++ 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); ++ ++ while (!list_empty(&cec->events)) { ++ struct dw_hdmi_cec_event *event; ++ ++ event = list_first_entry(&cec->events, ++ struct dw_hdmi_cec_event, node); ++ list_del(&event->node); ++ kfree(event); ++ } ++ } ++ mutex_unlock(&cec->mutex); ++ return 0; ++} ++ ++static const struct file_operations hdmi_cec_fops = { ++ .owner = THIS_MODULE, ++ .read = dw_hdmi_cec_read, ++ .write = dw_hdmi_cec_write, ++ .open = dw_hdmi_cec_open, ++ .unlocked_ioctl = dw_hdmi_cec_ioctl, ++ .release = dw_hdmi_cec_release, ++ .poll = dw_hdmi_cec_poll, ++}; ++ ++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; ++ struct device *cd; ++ dev_t devn = MKDEV(cec_major, 0); ++ int ret; ++ ++ 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; ++ ++ INIT_LIST_HEAD(&cec->events); ++ init_waitqueue_head(&cec->waitq); ++ spin_lock_init(&cec->lock); ++ mutex_init(&cec->mutex); ++ ++ /* FIXME: soft-reset the CEC interface */ ++ ++ cec->addresses = BIT(15); ++ dw_hdmi_set_address(cec); ++ 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); ++ ++ cdev_init(&cec->cdev, &hdmi_cec_fops); ++ cec->cdev.owner = THIS_MODULE; ++ ret = cdev_add(&cec->cdev, devn, 1); ++ if (ret < 0) ++ goto err_cdev; ++ ++ cd = device_create(cec_class, cec->dev, devn, NULL, DEV_NAME); ++ if (IS_ERR(cd)) { ++ ret = PTR_ERR(cd); ++ dev_err(cec->dev, "can't create device: %d\n", ret); ++ goto err_dev; ++ } ++ ++ return 0; ++ ++ err_dev: ++ cdev_del(&cec->cdev); ++ err_cdev: ++ return ret; ++} ++ ++static int dw_hdmi_cec_remove(struct platform_device *pdev) ++{ ++ struct dw_hdmi_cec *cec = platform_get_drvdata(pdev); ++ dev_t devn = MKDEV(cec_major, 0); ++ ++ device_destroy(cec_class, devn); ++ cdev_del(&cec->cdev); ++ ++ 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, ++ }, ++}; ++ ++static int dw_hdmi_cec_init(void) ++{ ++ dev_t dev; ++ int ret; ++ ++ cec_class = class_create(THIS_MODULE, DEV_NAME); ++ 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, DEV_NAME); ++ if (ret) { ++ pr_err("cec: can't create character devices: %d\n", ret); ++ goto err_chrdev; ++ } ++ ++ cec_major = MAJOR(dev); ++ ++ ret = platform_driver_register(&dw_hdmi_cec_driver); ++ if (ret) ++ goto err_driver; ++ ++ return 0; ++ ++ err_driver: ++ unregister_chrdev_region(MKDEV(cec_major, 0), 1); ++ err_chrdev: ++ class_destroy(cec_class); ++ err_class: ++ return ret; ++} ++module_init(dw_hdmi_cec_init); ++ ++static void dw_hdmi_cec_exit(void) ++{ ++ platform_driver_unregister(&dw_hdmi_cec_driver); ++ unregister_chrdev_region(MKDEV(cec_major, 0), 1); ++ class_destroy(cec_class); ++} ++module_exit(dw_hdmi_cec_exit); ++ ++MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>"); ++MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec"); +diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.h b/drivers/staging/imx-drm/dw-hdmi-cec.h +new file mode 100644 +index 0000000..5ff40cc +--- /dev/null ++++ b/drivers/staging/imx-drm/dw-hdmi-cec.h +@@ -0,0 +1,16 @@ ++#ifndef DW_HDMI_CEC_H ++#define DW_HDMI_CEC_H ++ ++struct dw_hdmi_cec_ops { ++ void (*enable)(void *); ++ void (*disable)(void *); ++}; ++ ++struct dw_hdmi_cec_data { ++ void __iomem *base; ++ int irq; ++ const struct dw_hdmi_cec_ops *ops; ++ void *ops_data; ++}; ++ ++#endif +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index df20f94..6021913 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -29,6 +29,7 @@ + #include <drm/drm_encoder_slave.h> + + #include "dw-hdmi-audio.h" ++#include "dw-hdmi-cec.h" + #include "ipu-v3/imx-ipu-v3.h" + #include "imx-hdmi.h" + #include "imx-drm.h" +@@ -116,6 +117,7 @@ struct imx_hdmi { + struct drm_connector connector; + struct drm_encoder encoder; + ++ struct platform_device *cec; + struct snd_dw_hdmi *audio; + enum imx_hdmi_devtype dev_type; + struct device *dev; +@@ -128,6 +130,7 @@ struct imx_hdmi { + int vic; + + u8 edid[HDMI_EDID_LEN]; ++ u8 mc_clkdis; + bool cable_plugin; + + bool phy_enabled; +@@ -1154,8 +1157,6 @@ static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi) + /* HDMI Initialization Step B.4 */ + static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) + { +- u8 clkdis; +- + /* control period minimum duration */ + hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR); + hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR); +@@ -1167,23 +1168,28 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) + hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); + + /* Enable pixel clock and tmds data path */ +- clkdis = 0x7F; +- clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE | ++ HDMI_MC_CLKDIS_CSCCLK_DISABLE | ++ HDMI_MC_CLKDIS_AUDCLK_DISABLE | ++ HDMI_MC_CLKDIS_PREPCLK_DISABLE | ++ HDMI_MC_CLKDIS_TMDSCLK_DISABLE; ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + +- clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + + /* Enable csc path */ + if (is_color_space_conversion(hdmi)) { +- clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; +- hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + } + } + + static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi) + { +- hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); ++ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; ++ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + } + + /* Workaround to clear the overflow condition */ +@@ -1578,6 +1584,27 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi) + 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", +@@ -1599,11 +1626,13 @@ MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids); + static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); ++ struct platform_device_info pdevinfo; + const struct of_device_id *of_id = + of_match_device(imx_hdmi_dt_ids, dev); + struct drm_device *drm = data; + struct device_node *np = dev->of_node; + struct device_node *ddc_node; ++ struct dw_hdmi_cec_data cec; + struct imx_hdmi *hdmi; + struct resource *iores; + int ret, irq; +@@ -1616,6 +1645,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + hdmi->connector_status = connector_status_disconnected; + hdmi->sample_rate = 48000; + hdmi->ratio = 100; ++ hdmi->mc_clkdis = 0x7f; + + if (of_id) { + const struct platform_device_id *device_id = of_id->data; +@@ -1722,6 +1752,20 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) + if (ret) + goto err_audio; + ++ cec.base = hdmi->regs; ++ cec.irq = irq; ++ cec.ops = &imx_hdmi_cec_ops; ++ cec.ops_data = hdmi; ++ ++ memset(&pdevinfo, 0, sizeof(pdevinfo)); ++ pdevinfo.parent = dev; ++ pdevinfo.name = "dw-hdmi-cec"; ++ pdevinfo.id = PLATFORM_DEVID_AUTO; ++ pdevinfo.data = &cec; ++ pdevinfo.size_data = sizeof(cec); ++ ++ hdmi->cec = platform_device_register_full(&pdevinfo); ++ + dev_set_drvdata(dev, hdmi); + + return 0; +@@ -1745,6 +1789,8 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master, + { + struct imx_hdmi *hdmi = dev_get_drvdata(dev); + ++ if (!IS_ERR(hdmi->cec)) ++ platform_device_unregister(hdmi->cec); + snd_dw_hdmi_remove(hdmi->audio); + + /* Disable all interrupts */ +diff --git a/drivers/staging/imx-drm/imx-hdmi.h b/drivers/staging/imx-drm/imx-hdmi.h +index 5baaa9c..3dbd376 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.h ++++ b/drivers/staging/imx-drm/imx-hdmi.h +@@ -1034,4 +1034,7 @@ struct imx_hdmi; + void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned int rate); + uint8_t *imx_hdmi_get_eld(struct imx_hdmi *hdmi); + ++void hdmi_enable_cec(struct imx_hdmi *hdmi); ++void hdmi_disable_cec(struct imx_hdmi *hdmi); ++ + #endif /* __IMX_HDMI_H__ */ +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0001-staging-imx-drm-core-don-t-request-probe-deferral-in.patch b/patches/imx_drm_dts/0001-staging-imx-drm-core-don-t-request-probe-deferral-in.patch new file mode 100644 index 0000000000000000000000000000000000000000..5c39e887f55a10ee78552249d342c634135402ca --- /dev/null +++ b/patches/imx_drm_dts/0001-staging-imx-drm-core-don-t-request-probe-deferral-in.patch @@ -0,0 +1,52 @@ +From 5fff65f2d8c6aec1ec57538a614a7325ef51fc30 Mon Sep 17 00:00:00 2001 +From: Lucas Stach <l.stach@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:02 +0100 +Subject: [PATCH 1/9] staging: imx-drm-core: don't request probe deferral in + imx_drm_encoder_parse_of + +Since imx_drm_encoder_parse_of is called from the encoder bind callbacks, +it is too late to request probe deferral. Rather the core should make sure +that the crtcs are bound before the encoders, after all needed components +are probed. + +This fixes probe failure when using the LDB on i.MX6. + +Signed-off-by: Lucas Stach <l.stach@pengutronix.de> +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + drivers/staging/imx-drm/imx-drm-core.c | 16 ++++------------ + 1 file changed, 4 insertions(+), 12 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index dcba518..98a97a2 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -457,21 +457,13 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, + return ret; + + id = args.args_count > 0 ? args.args[0] : 0; +- mask = imx_drm_find_crtc_mask(imxdrm, args.np, id); ++ crtc_mask |= imx_drm_find_crtc_mask(imxdrm, args.np, id); + of_node_put(args.np); +- +- /* +- * If we failed to find the CRTC(s) which this encoder is +- * supposed to be connected to, it's because the CRTC has +- * not been registered yet. Defer probing, and hope that +- * the required CRTC is added later. +- */ +- if (mask == 0) +- return -EPROBE_DEFER; +- +- crtc_mask |= mask; + } + ++ if (i == 0 || !crtc_mask) ++ return -ENOENT; ++ + encoder->possible_crtcs = crtc_mask; + + /* FIXME: this is the mask of outputs which can clone this output. */ +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0002-staging-imx-drm-Add-temporary-copies-of-v4l2-of-pars.patch b/patches/imx_drm_dts/0002-staging-imx-drm-Add-temporary-copies-of-v4l2-of-pars.patch new file mode 100644 index 0000000000000000000000000000000000000000..99cfb0286960616342d58c1966c097497a1817de --- /dev/null +++ b/patches/imx_drm_dts/0002-staging-imx-drm-Add-temporary-copies-of-v4l2-of-pars.patch @@ -0,0 +1,187 @@ +From a4dcc84c582ac27416f4e0a340043d669fab13ef Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <philipp.zabel@gmail.com> +Date: Tue, 18 Feb 2014 12:36:03 +0100 +Subject: [PATCH 2/9] staging: imx-drm: Add temporary copies of v4l2-of parsing + functions + +The existing v4l2-of parser functions for the video interface bindings +described in Documentation/device-tree/bindings/media/video-interfaces.txt +are useful for DRM drivers, too. They will be moved to drivers/media +so they can be used by drm drivers, too. Until then, duplicate the +v4l2-of parser functions temporarily. + +Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com> +--- + drivers/staging/imx-drm/Makefile | 2 +- + drivers/staging/imx-drm/imx-drm-of.c | 132 +++++++++++++++++++++++++++++++++++ + drivers/staging/imx-drm/imx-drm.h | 6 ++ + 3 files changed, 139 insertions(+), 1 deletion(-) + create mode 100644 drivers/staging/imx-drm/imx-drm-of.c + +diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile +index 53bc725..cbe4137 100644 +--- a/drivers/staging/imx-drm/Makefile ++++ b/drivers/staging/imx-drm/Makefile +@@ -1,5 +1,5 @@ + +-imxdrm-objs := imx-drm-core.o ++imxdrm-objs := imx-drm-core.o imx-drm-of.o + imxhdmi-objs := imx-hdmi.o dw-hdmi-audio.o + + obj-$(CONFIG_DRM_IMX) += imxdrm.o +diff --git a/drivers/staging/imx-drm/imx-drm-of.c b/drivers/staging/imx-drm/imx-drm-of.c +new file mode 100644 +index 0000000..e14b4f3 +--- /dev/null ++++ b/drivers/staging/imx-drm/imx-drm-of.c +@@ -0,0 +1,132 @@ ++/* ++ * Video Interface OF binding parsing library ++ * ++ * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. ++ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> ++ * ++ * Copyright (C) 2012 Renesas Electronics Corp. ++ * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/of.h> ++ ++/** ++ * imx_drm_of_get_next_endpoint() - get next endpoint node ++ * @parent: pointer to the parent device node ++ * @prev: previous endpoint node, or NULL to get first ++ * ++ * Return: An 'endpoint' node pointer with refcount incremented. Refcount ++ * of the passed @prev node is decremented. ++ */ ++struct device_node *imx_drm_of_get_next_endpoint( ++ const struct device_node *parent, struct device_node *prev) ++{ ++ struct device_node *endpoint; ++ struct device_node *port = NULL; ++ ++ if (!parent) ++ return NULL; ++ ++ if (!prev) { ++ struct device_node *node; ++ /* ++ * It's the first call, we have to find a port subnode ++ * within this node or within an optional 'ports' node. ++ */ ++ node = of_get_child_by_name(parent, "ports"); ++ if (node) ++ parent = node; ++ ++ port = of_get_child_by_name(parent, "port"); ++ ++ if (port) { ++ /* Found a port, get an endpoint. */ ++ endpoint = of_get_next_child(port, NULL); ++ of_node_put(port); ++ } else { ++ endpoint = NULL; ++ } ++ ++ if (!endpoint) ++ pr_err("%s(): no endpoint nodes specified for %s\n", ++ __func__, parent->full_name); ++ of_node_put(node); ++ } else { ++ port = of_get_parent(prev); ++ if (!port) { ++ /* Hm, has someone given us the root node ?... */ ++ of_node_put(prev); ++ return NULL; ++ } ++ ++ endpoint = of_get_next_child(port, prev); ++ if (endpoint) { ++ of_node_put(port); ++ return endpoint; ++ } ++ ++ /* No more endpoints under this port, try the next one. */ ++ do { ++ port = of_get_next_child(parent, port); ++ if (!port) ++ return NULL; ++ } while (of_node_cmp(port->name, "port")); ++ ++ /* Pick up the first endpoint in this port. */ ++ endpoint = of_get_next_child(port, NULL); ++ of_node_put(port); ++ } ++ ++ return endpoint; ++} ++EXPORT_SYMBOL_GPL(imx_drm_of_get_next_endpoint); ++ ++/** ++ * imx_drm_of_get_remote_port_parent() - get remote port's parent node ++ * @node: pointer to a local endpoint device_node ++ * ++ * Return: Remote device node associated with remote endpoint node linked ++ * to @node. Use of_node_put() on it when done. ++ */ ++struct device_node *imx_drm_of_get_remote_port_parent( ++ const struct device_node *node) ++{ ++ struct device_node *np; ++ unsigned int depth; ++ ++ /* Get remote endpoint node. */ ++ np = of_parse_phandle(node, "remote-endpoint", 0); ++ ++ /* Walk 3 levels up only if there is 'ports' node. */ ++ for (depth = 3; depth && np; depth--) { ++ np = of_get_next_parent(np); ++ if (depth == 2 && of_node_cmp(np->name, "ports")) ++ break; ++ } ++ return np; ++} ++EXPORT_SYMBOL(imx_drm_of_get_remote_port_parent); ++ ++/** ++ * imx_drm_of_get_remote_port() - get remote port node ++ * @node: pointer to a local endpoint device_node ++ * ++ * Return: Remote port node associated with remote endpoint node linked ++ * to @node. Use of_node_put() on it when done. ++ */ ++struct device_node *imx_drm_of_get_remote_port(const struct device_node *node) ++{ ++ struct device_node *np; ++ ++ /* Get remote endpoint node. */ ++ np = of_parse_phandle(node, "remote-endpoint", 0); ++ if (!np) ++ return NULL; ++ return of_get_next_parent(np); ++} ++EXPORT_SYMBOL_GPL(imx_drm_of_get_remote_port); +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index aa21028..793a80b 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -58,4 +58,10 @@ int imx_drm_connector_mode_valid(struct drm_connector *connector, + void imx_drm_connector_destroy(struct drm_connector *connector); + void imx_drm_encoder_destroy(struct drm_encoder *encoder); + ++struct device_node *imx_drm_of_get_next_endpoint( ++ const struct device_node *parent, struct device_node *prev); ++struct device_node *imx_drm_of_get_remote_port_parent( ++ const struct device_node *node); ++struct device_node *imx_drm_of_get_remote_port(const struct device_node *node); ++ + #endif /* _IMX_DRM_H_ */ +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0003-staging-imx-drm-core-Use-OF-graph-to-find-components.patch b/patches/imx_drm_dts/0003-staging-imx-drm-core-Use-OF-graph-to-find-components.patch new file mode 100644 index 0000000000000000000000000000000000000000..d2db7ebc7fb28fd05eda7c6a4a0fe8030382985e --- /dev/null +++ b/patches/imx_drm_dts/0003-staging-imx-drm-core-Use-OF-graph-to-find-components.patch @@ -0,0 +1,486 @@ +From a53be7ca51806b3b0a98842a4b6bc71541e515e3 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <p.zabel@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:04 +0100 +Subject: [PATCH 3/9] staging: imx-drm-core: Use OF graph to find components + and connections between encoder and crtcs + +This patch adds support to find the involved components connected to the +IPU display interface ports using the OF graph bindings documented in +Documentation/devicetree/bindings/media/video-interfaces.txt + +Each display interface needs to have an associated port node in the +device tree. We can associate this node with the crtc platform device +and use it to find the crtc corresponding to a given port node instead +of using a combination of parent device node and id number, as before. + +Explicitly converting the void* cookie to the port device tree node +allows to get rid of the ipu_id and di_id fields. The multiplexer +setting on i.MX6 now can be obtained from the port id (reg property) +in the device tree. + +The imx-drm node now needs a ports property that contains phandles +to each of the IPU display interface port nodes. From there, all +attached encoders are scanned and enabled encoders are added to a +waiting list. +The bind order makes sure that once all components are probed, crtcs +are bound before encoders, so that imx_drm_encoder_parse_of can be +called from the encoder bind callbacks. + +For parsing the OF graph, temporary copies of the V4L2 OF graph +helpers are used, that can be removed again once those are available +at a generic place. + +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + drivers/staging/imx-drm/imx-drm-core.c | 203 ++++++++++++++++++++++----------- + drivers/staging/imx-drm/imx-drm.h | 5 +- + drivers/staging/imx-drm/imx-hdmi.c | 2 +- + drivers/staging/imx-drm/imx-ldb.c | 4 +- + drivers/staging/imx-drm/ipuv3-crtc.c | 47 ++++++-- + 5 files changed, 185 insertions(+), 76 deletions(-) + +diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c +index 98a97a2..92246d9 100644 +--- a/drivers/staging/imx-drm/imx-drm-core.c ++++ b/drivers/staging/imx-drm/imx-drm-core.c +@@ -30,6 +30,11 @@ + + struct imx_drm_crtc; + ++struct imx_drm_component { ++ struct device_node *of_node; ++ struct list_head list; ++}; ++ + struct imx_drm_device { + struct drm_device *drm; + struct imx_drm_crtc *crtc[MAX_CRTC]; +@@ -41,9 +46,7 @@ struct imx_drm_crtc { + struct drm_crtc *crtc; + int pipe; + struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; +- void *cookie; +- int id; +- int mux_id; ++ struct device_node *port; + }; + + static int legacyfb_depth = 16; +@@ -341,14 +344,11 @@ err_kms: + + /* + * imx_drm_add_crtc - add a new crtc +- * +- * The return value if !NULL is a cookie for the caller to pass to +- * imx_drm_remove_crtc later. + */ + int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + struct imx_drm_crtc **new_crtc, + const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs, +- void *cookie, int id) ++ struct device_node *port) + { + struct imx_drm_device *imxdrm = drm->dev_private; + struct imx_drm_crtc *imx_drm_crtc; +@@ -370,9 +370,7 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + + imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; + imx_drm_crtc->pipe = imxdrm->pipes++; +- imx_drm_crtc->cookie = cookie; +- imx_drm_crtc->id = id; +- imx_drm_crtc->mux_id = imx_drm_crtc->pipe; ++ imx_drm_crtc->port = port; + imx_drm_crtc->crtc = crtc; + + imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; +@@ -416,21 +414,26 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) + EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); + + /* +- * Find the DRM CRTC possible mask for the device node cookie/id. ++ * Find the DRM CRTC possible mask for the connected endpoint. + * + * The encoder possible masks are defined by their position in the + * mode_config crtc_list. This means that CRTCs must not be added + * or removed once the DRM device has been fully initialised. + */ + static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, +- void *cookie, int id) ++ struct device_node *endpoint) + { ++ struct device_node *port; + unsigned i; + ++ port = imx_drm_of_get_remote_port(endpoint); ++ if (!port) ++ return 0; ++ of_node_put(port); ++ + for (i = 0; i < MAX_CRTC; i++) { + struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; +- if (imx_drm_crtc && imx_drm_crtc->id == id && +- imx_drm_crtc->cookie == cookie) ++ if (imx_drm_crtc && imx_drm_crtc->port == port) + return drm_crtc_mask(imx_drm_crtc->crtc); + } + +@@ -441,26 +444,20 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, + struct drm_encoder *encoder, struct device_node *np) + { + struct imx_drm_device *imxdrm = drm->dev_private; ++ struct device_node *ep = NULL; + uint32_t crtc_mask = 0; +- int i, ret = 0; +- +- for (i = 0; !ret; i++) { +- struct of_phandle_args args; +- uint32_t mask; +- int id; ++ int i; + +- ret = of_parse_phandle_with_args(np, "crtcs", "#crtc-cells", i, +- &args); +- if (ret == -ENOENT) ++ for (i = 0; ; i++) { ++ ep = imx_drm_of_get_next_endpoint(np, ep); ++ if (!ep) + break; +- if (ret < 0) +- return ret; + +- id = args.args_count > 0 ? args.args[0] : 0; +- crtc_mask |= imx_drm_find_crtc_mask(imxdrm, args.np, id); +- of_node_put(args.np); ++ crtc_mask |= imx_drm_find_crtc_mask(imxdrm, ep); + } + ++ if (ep) ++ of_node_put(ep); + if (i == 0 || !crtc_mask) + return -ENOENT; + +@@ -473,11 +470,36 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); + +-int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder) ++/* ++ * @node: device tree node containing encoder input ports ++ * @encoder: drm_encoder ++ */ ++int imx_drm_encoder_get_mux_id(struct device_node *node, ++ struct drm_encoder *encoder) + { + struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc); ++ struct device_node *ep = NULL; ++ struct device_node *port; ++ int id, ret; ++ ++ if (!node || !imx_crtc) ++ return -EINVAL; ++ ++ do { ++ ep = imx_drm_of_get_next_endpoint(node, ep); ++ if (!ep) ++ break; + +- return imx_crtc ? imx_crtc->mux_id : -EINVAL; ++ port = imx_drm_of_get_remote_port(ep); ++ of_node_put(port); ++ if (port == imx_crtc->port) { ++ ret = of_property_read_u32(ep->parent, "reg", &id); ++ of_node_put(ep); ++ return ret ? ret : id; ++ } ++ } while (ep); ++ ++ return -EINVAL; + } + EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); + +@@ -520,48 +542,29 @@ static struct drm_driver imx_drm_driver = { + .patchlevel = 0, + }; + +-static int compare_parent_of(struct device *dev, void *data) +-{ +- struct of_phandle_args *args = data; +- return dev->parent && dev->parent->of_node == args->np; +-} +- + static int compare_of(struct device *dev, void *data) + { +- return dev->of_node == data; +-} +- +-static int imx_drm_add_components(struct device *master, struct master *m) +-{ +- struct device_node *np = master->of_node; +- unsigned i; +- int ret; +- +- for (i = 0; ; i++) { +- struct of_phandle_args args; ++ struct device_node *np = data; + +- ret = of_parse_phandle_with_fixed_args(np, "crtcs", 1, +- i, &args); +- if (ret) +- break; +- +- ret = component_master_add_child(m, compare_parent_of, &args); +- of_node_put(args.np); +- +- if (ret) +- return ret; ++ /* Special case for LDB, one device for two channels */ ++ if (of_node_cmp(np->name, "lvds-channel") == 0) { ++ np = of_get_parent(np); ++ of_node_put(np); + } + +- for (i = 0; ; i++) { +- struct device_node *node; ++ return dev->of_node == np; ++} + +- node = of_parse_phandle(np, "connectors", i); +- if (!node) +- break; ++static LIST_HEAD(imx_drm_components); + +- ret = component_master_add_child(m, compare_of, node); +- of_node_put(node); ++static int imx_drm_add_components(struct device *master, struct master *m) ++{ ++ struct imx_drm_component *component; ++ int ret; + ++ list_for_each_entry(component, &imx_drm_components, list) { ++ ret = component_master_add_child(m, compare_of, ++ component->of_node); + if (ret) + return ret; + } +@@ -584,9 +587,81 @@ static const struct component_master_ops imx_drm_ops = { + .unbind = imx_drm_unbind, + }; + ++static struct imx_drm_component *imx_drm_find_component(struct device *dev, ++ struct device_node *node) ++{ ++ struct imx_drm_component *component; ++ ++ list_for_each_entry(component, &imx_drm_components, list) ++ if (component->of_node == node) ++ return component; ++ ++ return NULL; ++} ++ ++static int imx_drm_add_component(struct device *dev, struct device_node *node) ++{ ++ struct imx_drm_component *component; ++ ++ if (imx_drm_find_component(dev, node)) ++ return 0; ++ ++ component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); ++ if (!component) ++ return -ENOMEM; ++ ++ component->of_node = node; ++ list_add_tail(&component->list, &imx_drm_components); ++ ++ return 0; ++} ++ + static int imx_drm_platform_probe(struct platform_device *pdev) + { ++ struct device_node *ep, *port, *remote; + int ret; ++ int i; ++ ++ /* ++ * Bind the IPU display interface ports first, so that ++ * imx_drm_encoder_parse_of called from encoder .bind callbacks ++ * works as expected. ++ */ ++ for (i = 0; ; i++) { ++ port = of_parse_phandle(pdev->dev.of_node, "ports", i); ++ if (!port) ++ break; ++ ++ ret = imx_drm_add_component(&pdev->dev, port); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (i == 0) { ++ dev_err(&pdev->dev, "missing 'ports' property\n"); ++ return -ENODEV; ++ } ++ ++ /* Then bind all encoders */ ++ for (i = 0; ; i++) { ++ port = of_parse_phandle(pdev->dev.of_node, "ports", i); ++ if (!port) ++ break; ++ ++ for_each_child_of_node(port, ep) { ++ remote = imx_drm_of_get_remote_port_parent(ep); ++ if (!remote || !of_device_is_available(remote)) { ++ of_node_put(remote); ++ continue; ++ } ++ ++ ret = imx_drm_add_component(&pdev->dev, remote); ++ of_node_put(remote); ++ if (ret < 0) ++ return ret; ++ } ++ of_node_put(port); ++ } + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) +diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h +index 793a80b..447ddd7 100644 +--- a/drivers/staging/imx-drm/imx-drm.h ++++ b/drivers/staging/imx-drm/imx-drm.h +@@ -30,7 +30,7 @@ struct imx_drm_crtc_helper_funcs { + int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, + struct imx_drm_crtc **new_crtc, + const struct imx_drm_crtc_helper_funcs *imx_helper_funcs, +- void *cookie, int id); ++ struct device_node *port); + int imx_drm_remove_crtc(struct imx_drm_crtc *); + int imx_drm_init_drm(struct platform_device *pdev, + int preferred_bpp); +@@ -49,7 +49,8 @@ int imx_drm_panel_format_pins(struct drm_encoder *encoder, + int imx_drm_panel_format(struct drm_encoder *encoder, + u32 interface_pix_fmt); + +-int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder); ++int imx_drm_encoder_get_mux_id(struct device_node *node, ++ struct drm_encoder *encoder); + int imx_drm_encoder_parse_of(struct drm_device *drm, + struct drm_encoder *encoder, struct device_node *np); + +diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c +index 6021913..5b87ed4 100644 +--- a/drivers/staging/imx-drm/imx-hdmi.c ++++ b/drivers/staging/imx-drm/imx-hdmi.c +@@ -1477,7 +1477,7 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder) + static void imx_hdmi_encoder_commit(struct drm_encoder *encoder) + { + struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); +- int mux = imx_drm_encoder_get_mux_id(encoder); ++ int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); + + imx_hdmi_set_ipu_di_mux(hdmi, mux); + +diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c +index 5168c76..301c430 100644 +--- a/drivers/staging/imx-drm/imx-ldb.c ++++ b/drivers/staging/imx-drm/imx-ldb.c +@@ -168,7 +168,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) + u32 pixel_fmt; + unsigned long serial_clk; + unsigned long di_clk = mode->clock * 1000; +- int mux = imx_drm_encoder_get_mux_id(encoder); ++ 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 */ +@@ -203,7 +203,7 @@ 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(encoder); ++ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); + + if (dual) { + clk_prepare_enable(ldb->clk[0]); +diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c +index e646017..a8d0178 100644 +--- a/drivers/staging/imx-drm/ipuv3-crtc.c ++++ b/drivers/staging/imx-drm/ipuv3-crtc.c +@@ -350,10 +350,8 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, + return ret; + } + +- ret = imx_drm_add_crtc(drm, &ipu_crtc->base, +- &ipu_crtc->imx_crtc, +- &ipu_crtc_helper_funcs, +- ipu_crtc->dev->parent->of_node, pdata->di); ++ ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc, ++ &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node); + if (ret) { + dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); + goto err_put_resources; +@@ -401,6 +399,28 @@ err_put_resources: + return ret; + } + ++static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent, ++ int port_id) ++{ ++ struct device_node *port; ++ int id, ret; ++ ++ port = of_get_child_by_name(parent, "port"); ++ while (port) { ++ ret = of_property_read_u32(port, "reg", &id); ++ if (!ret && id == port_id) ++ return port; ++ ++ do { ++ port = of_get_next_child(parent, port); ++ if (!port) ++ return NULL; ++ } while (of_node_cmp(port->name, "port")); ++ } ++ ++ return NULL; ++} ++ + static int ipu_drm_bind(struct device *dev, struct device *master, void *data) + { + struct ipu_client_platformdata *pdata = dev->platform_data; +@@ -441,16 +461,29 @@ static const struct component_ops ipu_crtc_ops = { + + static int ipu_drm_probe(struct platform_device *pdev) + { ++ struct device *dev = &pdev->dev; ++ struct ipu_client_platformdata *pdata = dev->platform_data; + int ret; + +- if (!pdev->dev.platform_data) ++ if (!dev->platform_data) + return -EINVAL; + +- ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ if (!dev->of_node) { ++ /* Associate crtc device with the corresponding DI port node */ ++ dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node, ++ pdata->di + 2); ++ if (!dev->of_node) { ++ dev_err(dev, "missing port@%d node in %s\n", ++ pdata->di + 2, dev->parent->of_node->full_name); ++ return -ENODEV; ++ } ++ } ++ ++ ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + +- return component_add(&pdev->dev, &ipu_crtc_ops); ++ return component_add(dev, &ipu_crtc_ops); + } + + static int ipu_drm_remove(struct platform_device *pdev) +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0004-staging-imx-drm-Document-updated-imx-drm-device-tree.patch b/patches/imx_drm_dts/0004-staging-imx-drm-Document-updated-imx-drm-device-tree.patch new file mode 100644 index 0000000000000000000000000000000000000000..4350639710c68a40681d700a5e44c96cfb73cefe --- /dev/null +++ b/patches/imx_drm_dts/0004-staging-imx-drm-Document-updated-imx-drm-device-tree.patch @@ -0,0 +1,166 @@ +From 7dbc884962d7b3046c6781fe5399825c6419a7aa Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <p.zabel@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:05 +0100 +Subject: [PATCH 4/9] staging: imx-drm: Document updated imx-drm device tree + bindings + +This patch updates the device tree binding documentation for i.MX IPU/display +nodes using the OF graph bindings documented in +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + .../bindings/staging/imx-drm/fsl-imx-drm.txt | 48 +++++++++++++++++++--- + .../devicetree/bindings/staging/imx-drm/ldb.txt | 20 +++++++-- + 2 files changed, 59 insertions(+), 9 deletions(-) + +diff --git a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt +index b876d49..bfa19a4 100644 +--- a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt ++++ b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt +@@ -1,3 +1,22 @@ ++Freescale i.MX DRM master device ++================================ ++ ++The freescale i.MX DRM master device is a virtual device needed to list all ++IPU or other display interface nodes that comprise the graphics subsystem. ++ ++Required properties: ++- compatible: Should be "fsl,imx-drm" ++- ports: Should contain a list of phandles pointing to display interface ports ++ of IPU devices ++ ++example: ++ ++imx-drm { ++ compatible = "fsl,imx-drm"; ++ ports = <&ipu_di0>; ++}; ++ ++ + Freescale i.MX IPUv3 + ==================== + +@@ -7,18 +26,31 @@ Required properties: + datasheet + - interrupts: Should contain sync interrupt and error interrupt, + in this order. +-- #crtc-cells: 1, See below + - resets: phandle pointing to the system reset controller and + reset line index, see reset/fsl,imx-src.txt for details ++Optional properties: ++- port@[0-3]: Port nodes with endpoint definitions as defined in ++ Documentation/devicetree/bindings/media/video-interfaces.txt. ++ Ports 0 and 1 should correspond to CSI0 and CSI1, ++ ports 2 and 3 should correspond to DI0 and DI1, respectively. + + example: + + ipu: ipu@18000000 { +- #crtc-cells = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; + compatible = "fsl,imx53-ipu"; + reg = <0x18000000 0x080000000>; + interrupts = <11 10>; + resets = <&src 2>; ++ ++ ipu_di0: port@2 { ++ reg = <2>; ++ ++ ipu_di0_disp0: endpoint { ++ remote-endpoint = <&display_in>; ++ }; ++ }; + }; + + Parallel display support +@@ -26,19 +58,25 @@ Parallel display support + + Required properties: + - compatible: Should be "fsl,imx-parallel-display" +-- crtc: the crtc this display is connected to, see below + Optional properties: + - interface_pix_fmt: How this display is connected to the +- crtc. Currently supported types: "rgb24", "rgb565", "bgr666" ++ display interface. Currently supported types: "rgb24", "rgb565", "bgr666" + - edid: verbatim EDID data block describing attached display. + - ddc: phandle describing the i2c bus handling the display data + channel ++- port: A port node with endpoint definitions as defined in ++ Documentation/devicetree/bindings/media/video-interfaces.txt. + + example: + + display@di0 { + compatible = "fsl,imx-parallel-display"; + edid = [edid-data]; +- crtc = <&ipu 0>; + interface-pix-fmt = "rgb24"; ++ ++ port { ++ display_in: endpoint { ++ remote-endpoint = <&ipu_di0_disp0>; ++ }; ++ }; + }; +diff --git a/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt b/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt +index ed93778..578a1fc 100644 +--- a/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt ++++ b/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt +@@ -50,12 +50,14 @@ have a look at Documentation/devicetree/bindings/video/display-timing.txt. + + Required properties: + - reg : should be <0> or <1> +- - crtcs : a list of phandles with index pointing to the IPU display interfaces +- that can be used as video source for this channel. + - fsl,data-mapping : should be "spwg" or "jeida" + This describes how the color bits are laid out in the + serialized LVDS signal. + - fsl,data-width : should be <18> or <24> ++ - port: A port node with endpoint definitions as defined in ++ Documentation/devicetree/bindings/media/video-interfaces.txt. ++ On i.MX6, there should be four ports (port@[0-3]) that correspond ++ to the four LVDS multiplexer inputs. + + example: + +@@ -77,23 +79,33 @@ ldb: ldb@53fa8008 { + + lvds-channel@0 { + reg = <0>; +- crtcs = <&ipu 0>; + fsl,data-mapping = "spwg"; + fsl,data-width = <24>; + + display-timings { + /* ... */ + }; ++ ++ port { ++ lvds0_in: endpoint { ++ remote-endpoint = <&ipu_di0_lvds0>; ++ }; ++ }; + }; + + lvds-channel@1 { + reg = <1>; +- crtcs = <&ipu 1>; + fsl,data-mapping = "spwg"; + fsl,data-width = <24>; + + display-timings { + /* ... */ + }; ++ ++ port { ++ lvds1_in: endpoint { ++ remote-endpoint = <&ipu_di1_lvds1>; ++ }; ++ }; + }; + }; +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0005-staging-imx-drm-Document-imx-hdmi-device-tree-bindin.patch b/patches/imx_drm_dts/0005-staging-imx-drm-Document-imx-hdmi-device-tree-bindin.patch new file mode 100644 index 0000000000000000000000000000000000000000..05e7e110ae48ae69543ecc4d35a4e952ce8263aa --- /dev/null +++ b/patches/imx_drm_dts/0005-staging-imx-drm-Document-imx-hdmi-device-tree-bindin.patch @@ -0,0 +1,76 @@ +From 4158c6de94991fa9f311dc7fae8f92f9c795b058 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <p.zabel@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:06 +0100 +Subject: [PATCH 5/9] staging: imx-drm: Document imx-hdmi device tree bindings + +This patch adds device tree binding documentation for the HDMI transmitter +on i.MX6. + +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + .../devicetree/bindings/staging/imx-drm/hdmi.txt | 53 ++++++++++++++++++++++ + 1 file changed, 53 insertions(+) + create mode 100644 Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt + +diff --git a/Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt b/Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt +new file mode 100644 +index 0000000..7dcd673a +--- /dev/null ++++ b/Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt +@@ -0,0 +1,53 @@ ++Device-Tree bindings for HDMI Transmitter ++ ++HDMI Transmitter ++================ ++ ++The LVDS Display Bridge device tree node contains up to two lvds-channel ++nodes describing each of the two LVDS encoder channels of the bridge. ++ ++Required properties: ++ - #address-cells : should be <1> ++ - #size-cells : should be <0> ++ - compatible : should be "fsl,imx6q-hdmi" or "fsl,imx6dl-hdmi". ++ - gpr : should be <&gpr>. ++ The phandle points to the iomuxc-gpr region containing the HDMI ++ multiplexer control register. ++ - clocks, clock-names : phandles to the HDMI iahb and isrf clocks, as described ++ in Documentation/devicetree/bindings/clock/clock-bindings.txt and ++ Documentation/devicetree/bindings/clock/imx6q-clock.txt. ++ - port@[0-4]: Up to four port nodes with endpoint definitions as defined in ++ Documentation/devicetree/bindings/media/video-interfaces.txt, ++ corresponding to the four inputs to the HDMI multiplexer. ++ ++example: ++ ++ gpr: iomuxc-gpr@020e0000 { ++ /* ... */ ++ }; ++ ++ hdmi: hdmi@0120000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x00120000 0x9000>; ++ interrupts = <0 115 0x04>; ++ gpr = <&gpr>; ++ clocks = <&clks 123>, <&clks 124>; ++ clock-names = "iahb", "isfr"; ++ ++ port@0 { ++ reg = <0>; ++ ++ hdmi_mux_0: endpoint { ++ remote-endpoint = <&ipu1_di0_hdmi>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ hdmi_mux_1: endpoint { ++ remote-endpoint = <&ipu1_di1_hdmi>; ++ }; ++ }; ++ }; +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0006-ARM-dts-imx51-Add-IPU-ports-and-endpoints-move-imx-d.patch b/patches/imx_drm_dts/0006-ARM-dts-imx51-Add-IPU-ports-and-endpoints-move-imx-d.patch new file mode 100644 index 0000000000000000000000000000000000000000..a609cc888c5462d4c43a50db6ec393fac12488de --- /dev/null +++ b/patches/imx_drm_dts/0006-ARM-dts-imx51-Add-IPU-ports-and-endpoints-move-imx-d.patch @@ -0,0 +1,179 @@ +From 3403cbc58467086d9075614674aa5b0e6bcc1a0a Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <p.zabel@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:07 +0100 +Subject: [PATCH 6/9] ARM: dts: imx51: Add IPU ports and endpoints, move + imx-drm node to dtsi + +This patch connects IPU and and parallel display device tree +nodes using the OF graph bindings described in +Documentation/devicetree/bindings/media/video-interfaces.txt + +The IPU ports correspond to the two display interfaces. The +order of endpoints in the ports is arbitrary. + +Since the imx-drm node now only needs to contain links to the +display interfaces, it can be moved to the SoC dtsi level. At +the board level, only connections between the display interface +ports and panels have to be added. + +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + arch/arm/boot/dts/imx51-apf51dev.dts | 11 ++++++++++- + arch/arm/boot/dts/imx51-babbage.dts | 28 ++++++++++++++++++++-------- + arch/arm/boot/dts/imx51.dtsi | 22 +++++++++++++++++++++- + 3 files changed, 51 insertions(+), 10 deletions(-) + +diff --git a/arch/arm/boot/dts/imx51-apf51dev.dts b/arch/arm/boot/dts/imx51-apf51dev.dts +index c29cfa9..c5a9a24 100644 +--- a/arch/arm/boot/dts/imx51-apf51dev.dts ++++ b/arch/arm/boot/dts/imx51-apf51dev.dts +@@ -18,7 +18,6 @@ + + display@di1 { + compatible = "fsl,imx-parallel-display"; +- crtcs = <&ipu 0>; + interface-pix-fmt = "bgr666"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu_disp1>; +@@ -41,6 +40,12 @@ + pixelclk-active = <0>; + }; + }; ++ ++ port { ++ display_in: endpoint { ++ remote-endpoint = <&ipu_di0_disp0>; ++ }; ++ }; + }; + + gpio-keys { +@@ -200,3 +205,7 @@ + }; + }; + }; ++ ++&ipu_di0_disp0 { ++ remote-endpoint = <&display_in>; ++}; +diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts +index ebe6c1d..9e9deb2 100644 +--- a/arch/arm/boot/dts/imx51-babbage.dts ++++ b/arch/arm/boot/dts/imx51-babbage.dts +@@ -23,7 +23,6 @@ + + display0: display@di0 { + compatible = "fsl,imx-parallel-display"; +- crtcs = <&ipu 0>; + interface-pix-fmt = "rgb24"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu_disp1>; +@@ -41,11 +40,16 @@ + vsync-len = <10>; + }; + }; ++ ++ port { ++ display0_in: endpoint { ++ remote-endpoint = <&ipu_di0_disp0>; ++ }; ++ }; + }; + + display1: display@di1 { + compatible = "fsl,imx-parallel-display"; +- crtcs = <&ipu 1>; + interface-pix-fmt = "rgb565"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu_disp2>; +@@ -68,6 +72,12 @@ + pixelclk-active = <0>; + }; + }; ++ ++ port { ++ display1_in: endpoint { ++ remote-endpoint = <&ipu_di1_disp1>; ++ }; ++ }; + }; + + gpio-keys { +@@ -92,12 +102,6 @@ + }; + }; + +- imx-drm { +- compatible = "fsl,imx-drm"; +- crtcs = <&ipu 0>, <&ipu 1>; +- connectors = <&display0>, <&display1>; +- }; +- + sound { + compatible = "fsl,imx51-babbage-sgtl5000", + "fsl,imx-audio-sgtl5000"; +@@ -276,6 +280,14 @@ + }; + }; + ++&ipu_di0_disp0 { ++ remote-endpoint = <&display0_in>; ++}; ++ ++&ipu_di1_disp1 { ++ remote-endpoint = <&display1_in>; ++}; ++ + &ssi2 { + fsl,mode = "i2s-slave"; + status = "okay"; +diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi +index e4b07d1..3528bb6 100644 +--- a/arch/arm/boot/dts/imx51.dtsi ++++ b/arch/arm/boot/dts/imx51.dtsi +@@ -101,6 +101,11 @@ + }; + }; + ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ ports = <&ipu_di0>, <&ipu_di1>; ++ }; ++ + soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -114,7 +119,8 @@ + }; + + ipu: ipu@40000000 { +- #crtc-cells = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; + compatible = "fsl,imx51-ipu"; + reg = <0x40000000 0x20000000>; + interrupts = <11 10>; +@@ -123,6 +129,20 @@ + <&clks IMX5_CLK_IPU_DI1_GATE>; + clock-names = "bus", "di0", "di1"; + resets = <&src 2>; ++ ++ ipu_di0: port@2 { ++ reg = <2>; ++ ++ ipu_di0_disp0: endpoint { ++ }; ++ }; ++ ++ ipu_di1: port@3 { ++ reg = <3>; ++ ++ ipu_di1_disp1: endpoint { ++ }; ++ }; + }; + + aips@70000000 { /* AIPS1 */ +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0007-ARM-dts-imx53-Add-IPU-DI-ports-and-endpoints-move-im.patch b/patches/imx_drm_dts/0007-ARM-dts-imx53-Add-IPU-DI-ports-and-endpoints-move-im.patch new file mode 100644 index 0000000000000000000000000000000000000000..2cba7d9350a015f70d1cd3831a82b435f2f7efa9 --- /dev/null +++ b/patches/imx_drm_dts/0007-ARM-dts-imx53-Add-IPU-DI-ports-and-endpoints-move-im.patch @@ -0,0 +1,274 @@ +From 8dcf3f81055176963a1a8c88be50c55db23a96f1 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <p.zabel@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:08 +0100 +Subject: [PATCH 7/9] ARM: dts: imx53: Add IPU DI ports and endpoints, move + imx-drm node to dtsi + +This patch connects IPU and display encoder (VGA, LVDS) +device tree nodes, as well as parallel displays on the DISP0 +and DISP1 outputs, using the OF graph bindings described in +Documentation/devicetree/bindings/media/video-interfaces.txt + +The IPU ports correspond to the two display interfaces. The +order of endpoints in the ports is arbitrary. + +Since the imx-drm node now only needs to contain links to the +display interfaces, it can be moved to the SoC dtsi level. At +the board level, only connections between the display interface +ports and encoders or panels have to be added. + +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + arch/arm/boot/dts/imx53-m53evk.dts | 17 +++++---- + arch/arm/boot/dts/imx53-mba53.dts | 15 ++++---- + arch/arm/boot/dts/imx53-qsb-common.dtsi | 17 +++++---- + arch/arm/boot/dts/imx53.dtsi | 64 ++++++++++++++++++++++++++++++--- + 4 files changed, 89 insertions(+), 24 deletions(-) + +diff --git a/arch/arm/boot/dts/imx53-m53evk.dts b/arch/arm/boot/dts/imx53-m53evk.dts +index d939ba8..f6d3ac3 100644 +--- a/arch/arm/boot/dts/imx53-m53evk.dts ++++ b/arch/arm/boot/dts/imx53-m53evk.dts +@@ -23,7 +23,6 @@ + soc { + display1: display@di1 { + compatible = "fsl,imx-parallel-display"; +- crtcs = <&ipu 1>; + interface-pix-fmt = "bgr666"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu_disp1>; +@@ -44,6 +43,12 @@ + }; + }; + }; ++ ++ port { ++ display1_in: endpoint { ++ remote-endpoint = <&ipu_di1_disp1>; ++ }; ++ }; + }; + + backlight { +@@ -54,12 +59,6 @@ + power-supply = <®_backlight>; + }; + +- imx-drm { +- compatible = "fsl,imx-drm"; +- crtcs = <&ipu 1>; +- connectors = <&display1>; +- }; +- + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; +@@ -406,6 +405,10 @@ + }; + }; + ++&ipu_di1_disp1 { ++ remote-endpoint = <&display1_in>; ++}; ++ + &nfc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_nand>; +diff --git a/arch/arm/boot/dts/imx53-mba53.dts b/arch/arm/boot/dts/imx53-mba53.dts +index a398745..7cb4cac 100644 +--- a/arch/arm/boot/dts/imx53-mba53.dts ++++ b/arch/arm/boot/dts/imx53-mba53.dts +@@ -30,15 +30,14 @@ + compatible = "fsl,imx-parallel-display"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_disp1_1>; +- crtcs = <&ipu 1>; + interface-pix-fmt = "rgb24"; + status = "disabled"; +- }; + +- imx-drm { +- compatible = "fsl,imx-drm"; +- crtcs = <&ipu 1>; +- connectors = <&disp1>, <&tve>; ++ port { ++ display1_in: endpoint { ++ remote-endpoint = <&ipu_di1_disp1>; ++ }; ++ }; + }; + + regulators { +@@ -154,6 +153,10 @@ + }; + }; + ++&ipu_di1_disp1 { ++ remote-endpoint = <&display1_in>; ++}; ++ + &cspi { + status = "okay"; + }; +diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi +index 50823d0..3f825a6 100644 +--- a/arch/arm/boot/dts/imx53-qsb-common.dtsi ++++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi +@@ -19,7 +19,6 @@ + + display0: display@di0 { + compatible = "fsl,imx-parallel-display"; +- crtcs = <&ipu 0>; + interface-pix-fmt = "rgb565"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu_disp0>; +@@ -42,6 +41,12 @@ + pixelclk-active = <0>; + }; + }; ++ ++ port { ++ display0_in: endpoint { ++ remote-endpoint = <&ipu_di0_disp0>; ++ }; ++ }; + }; + + gpio-keys { +@@ -68,12 +73,6 @@ + }; + }; + +- imx-drm { +- compatible = "fsl,imx-drm"; +- crtcs = <&ipu 0>; +- connectors = <&display0>; +- }; +- + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; +@@ -132,6 +131,10 @@ + status = "okay"; + }; + ++&ipu_di0_disp0 { ++ remote-endpoint = <&display0_in>; ++}; ++ + &ssi2 { + fsl,mode = "i2s-slave"; + status = "okay"; +diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi +index 80615df..25c92a8 100644 +--- a/arch/arm/boot/dts/imx53.dtsi ++++ b/arch/arm/boot/dts/imx53.dtsi +@@ -52,6 +52,11 @@ + }; + }; + ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ ports = <&ipu_di0>, <&ipu_di1>; ++ }; ++ + tzic: tz-interrupt-controller@0fffc000 { + compatible = "fsl,imx53-tzic", "fsl,tzic"; + interrupt-controller; +@@ -103,7 +108,8 @@ + }; + + ipu: ipu@18000000 { +- #crtc-cells = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; + compatible = "fsl,imx53-ipu"; + reg = <0x18000000 0x080000000>; + interrupts = <11 10>; +@@ -112,6 +118,41 @@ + <&clks IMX5_CLK_IPU_DI1_GATE>; + clock-names = "bus", "di0", "di1"; + resets = <&src 2>; ++ ++ ipu_di0: port@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <2>; ++ ++ ipu_di0_disp0: endpoint@0 { ++ reg = <0>; ++ }; ++ ++ ipu_di0_lvds0: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&lvds0_in>; ++ }; ++ }; ++ ++ ipu_di1: port@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <3>; ++ ++ ipu_di1_disp1: endpoint@0 { ++ reg = <0>; ++ }; ++ ++ ipu_di1_lvds1: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&lvds1_in>; ++ }; ++ ++ ipu_di1_tve: endpoint@2 { ++ reg = <2>; ++ remote-endpoint = <&tve_in>; ++ }; ++ }; + }; + + aips@50000000 { /* AIPS1 */ +@@ -374,14 +415,24 @@ + + lvds-channel@0 { + reg = <0>; +- crtcs = <&ipu 0>; + status = "disabled"; ++ ++ port { ++ lvds0_in: endpoint { ++ remote-endpoint = <&ipu_di0_lvds0>; ++ }; ++ }; + }; + + lvds-channel@1 { + reg = <1>; +- crtcs = <&ipu 1>; + status = "disabled"; ++ ++ port { ++ lvds1_in: endpoint { ++ remote-endpoint = <&ipu_di0_lvds0>; ++ }; ++ }; + }; + }; + +@@ -655,8 +706,13 @@ + clocks = <&clks IMX5_CLK_TVE_GATE>, + <&clks IMX5_CLK_IPU_DI1_SEL>; + clock-names = "tve", "di_sel"; +- crtcs = <&ipu 1>; + status = "disabled"; ++ ++ port { ++ tve_in: endpoint { ++ remote-endpoint = <&ipu_di1_tve>; ++ }; ++ }; + }; + + vpu: vpu@63ff4000 { +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0008-ARM-dts-imx6qdl-Add-IPU-DI-ports-and-endpoints-move-.patch b/patches/imx_drm_dts/0008-ARM-dts-imx6qdl-Add-IPU-DI-ports-and-endpoints-move-.patch new file mode 100644 index 0000000000000000000000000000000000000000..1b738eb071bb061bcdc8ea71cb685e7a9d33f30a --- /dev/null +++ b/patches/imx_drm_dts/0008-ARM-dts-imx6qdl-Add-IPU-DI-ports-and-endpoints-move-.patch @@ -0,0 +1,446 @@ +From dd919820508a5d686ac6fbdff674af5f436dc168 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <p.zabel@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:09 +0100 +Subject: [PATCH 8/9] ARM: dts: imx6qdl: Add IPU DI ports and endpoints, move + imx-drm node to dtsi + +This patch connects IPU and display encoder (HDMI, LVDS, MIPI) +device tree nodes, as well as parallel displays on the DISP0 +and DISP1 outputs, using the OF graph bindings described in +Documentation/devicetree/bindings/media/video-interfaces.txt + +The IPU ports correspond to the two display interfaces. The +order of endpoints in the ports is arbitrary. + +Each encoder with an associated input multiplexer has multiple +input ports in the device tree. The order and reg property of +the ports must correspond to the multiplexer input order. + +Since the imx-drm node now only needs to contain links to the +display interfaces, it can be moved to the SoC dtsi level. At +the board level, only connections between the display interface +ports and encoders or panels have to be added. + +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + arch/arm/boot/dts/imx6dl.dtsi | 22 +++--- + arch/arm/boot/dts/imx6q-sabresd.dts | 4 - + arch/arm/boot/dts/imx6q.dtsi | 124 +++++++++++++++++++++++++++-- + arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 6 -- + arch/arm/boot/dts/imx6qdl.dtsi | 138 ++++++++++++++++++++++++++++++++- + 5 files changed, 263 insertions(+), 31 deletions(-) + +diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi +index 3ab8711..082b92d 100644 +--- a/arch/arm/boot/dts/imx6dl.dtsi ++++ b/arch/arm/boot/dts/imx6dl.dtsi +@@ -91,6 +91,15 @@ + }; + }; + }; ++ ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ ports = <&ipu1_di0>, <&ipu1_di1>; ++ }; ++}; ++ ++&hdmi { ++ compatible = "fsl,imx6dl-hdmi"; + }; + + &ldb { +@@ -100,17 +109,4 @@ + clock-names = "di0_pll", "di1_pll", + "di0_sel", "di1_sel", + "di0", "di1"; +- +- lvds-channel@0 { +- crtcs = <&ipu1 0>, <&ipu1 1>; +- }; +- +- lvds-channel@1 { +- crtcs = <&ipu1 0>, <&ipu1 1>; +- }; +-}; +- +-&hdmi { +- compatible = "fsl,imx6dl-hdmi"; +- crtcs = <&ipu1 0>, <&ipu1 1>; + }; +diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts +index 66f220a..9cbdfe7 100644 +--- a/arch/arm/boot/dts/imx6q-sabresd.dts ++++ b/arch/arm/boot/dts/imx6q-sabresd.dts +@@ -20,10 +20,6 @@ + compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; + }; + +-&imx_drm { +- crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; +-}; +- + &sata { + status = "okay"; + }; +diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi +index 6bb1ce9..4e8580c 100644 +--- a/arch/arm/boot/dts/imx6q.dtsi ++++ b/arch/arm/boot/dts/imx6q.dtsi +@@ -146,7 +146,8 @@ + }; + + ipu2: ipu@02800000 { +- #crtc-cells = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; + compatible = "fsl,imx6q-ipu"; + reg = <0x02800000 0x400000>; + interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>, +@@ -154,6 +155,76 @@ + clocks = <&clks 133>, <&clks 134>, <&clks 137>; + clock-names = "bus", "di0", "di1"; + resets = <&src 4>; ++ ++ ipu2_di0: port@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <2>; ++ ++ ipu2_di0_disp0: endpoint@0 { ++ }; ++ ++ ipu2_di0_hdmi: endpoint@1 { ++ remote-endpoint = <&hdmi_mux_2>; ++ }; ++ ++ ipu2_di0_mipi: endpoint@2 { ++ }; ++ ++ ipu2_di0_lvds0: endpoint@3 { ++ remote-endpoint = <&lvds0_mux_2>; ++ }; ++ ++ ipu2_di0_lvds1: endpoint@4 { ++ remote-endpoint = <&lvds1_mux_2>; ++ }; ++ }; ++ ++ ipu2_di1: port@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <3>; ++ ++ ipu2_di1_hdmi: endpoint@1 { ++ remote-endpoint = <&hdmi_mux_3>; ++ }; ++ ++ ipu2_di1_mipi: endpoint@2 { ++ }; ++ ++ ipu2_di1_lvds0: endpoint@3 { ++ remote-endpoint = <&lvds0_mux_3>; ++ }; ++ ++ ipu2_di1_lvds1: endpoint@4 { ++ remote-endpoint = <&lvds1_mux_3>; ++ }; ++ }; ++ }; ++ }; ++ ++ imx-drm { ++ compatible = "fsl,imx-drm"; ++ ports = <&ipu1_di0>, <&ipu1_di1>, <&ipu2_di0>, <&ipu2_di1>; ++ }; ++}; ++ ++&hdmi { ++ compatible = "fsl,imx6q-hdmi"; ++ ++ port@2 { ++ reg = <2>; ++ ++ hdmi_mux_2: endpoint { ++ remote-endpoint = <&ipu2_di0_hdmi>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ ++ hdmi_mux_3: endpoint { ++ remote-endpoint = <&ipu2_di1_hdmi>; + }; + }; + }; +@@ -167,15 +238,56 @@ + "di0", "di1"; + + lvds-channel@0 { +- crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; ++ port@2 { ++ reg = <2>; ++ ++ lvds0_mux_2: endpoint { ++ remote-endpoint = <&ipu2_di0_lvds0>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ ++ lvds0_mux_3: endpoint { ++ remote-endpoint = <&ipu2_di1_lvds0>; ++ }; ++ }; + }; + + lvds-channel@1 { +- crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; ++ port@2 { ++ reg = <2>; ++ ++ lvds1_mux_2: endpoint { ++ remote-endpoint = <&ipu2_di0_lvds1>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ ++ lvds1_mux_3: endpoint { ++ remote-endpoint = <&ipu2_di1_lvds1>; ++ }; ++ }; + }; + }; + +-&hdmi { +- compatible = "fsl,imx6q-hdmi"; +- crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; ++&mipi_dsi { ++ port@2 { ++ reg = <2>; ++ ++ mipi_mux_2: endpoint { ++ remote-endpoint = <&ipu2_di0_mipi>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ ++ mipi_mux_3: endpoint { ++ remote-endpoint = <&ipu2_di1_mipi>; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +index 04487cb..0d816d3 100644 +--- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi ++++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +@@ -79,12 +79,6 @@ + }; + }; + +- imx_drm: imx-drm { +- compatible = "fsl,imx-drm"; +- crtcs = <&ipu1 0>, <&ipu1 1>; +- connectors = <&ldb>; +- }; +- + sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; +diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi +index a2d5c49..a6ae3b1 100644 +--- a/arch/arm/boot/dts/imx6qdl.dtsi ++++ b/arch/arm/boot/dts/imx6qdl.dtsi +@@ -664,23 +664,87 @@ + status = "disabled"; + + lvds-channel@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <0>; + status = "disabled"; ++ ++ port@0 { ++ reg = <0>; ++ ++ lvds0_mux_0: endpoint { ++ remote-endpoint = <&ipu1_di0_lvds0>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ lvds0_mux_1: endpoint { ++ remote-endpoint = <&ipu1_di1_lvds0>; ++ }; ++ }; ++ ++ port@4 { ++ lvds0: endpoint { ++ }; ++ }; + }; + + lvds-channel@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <1>; + status = "disabled"; ++ ++ port@0 { ++ reg = <0>; ++ ++ lvds1_mux_0: endpoint { ++ remote-endpoint = <&ipu1_di0_lvds1>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ lvds1_mux_1: endpoint { ++ remote-endpoint = <&ipu1_di1_lvds1>; ++ }; ++ }; ++ ++ port@4 { ++ lvds1: endpoint { ++ }; ++ }; + }; + }; + + hdmi: hdmi@0120000 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <0x00120000 0x9000>; + interrupts = <0 115 0x04>; + gpr = <&gpr>; + clocks = <&clks 123>, <&clks 124>; + clock-names = "iahb", "isfr"; + status = "disabled"; ++ ++ port@0 { ++ reg = <0>; ++ ++ hdmi_mux_0: endpoint { ++ remote-endpoint = <&ipu1_di0_hdmi>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ hdmi_mux_1: endpoint { ++ remote-endpoint = <&ipu1_di1_hdmi>; ++ }; ++ }; + }; + + dcic1: dcic@020e4000 { +@@ -899,8 +963,27 @@ + reg = <0x021dc000 0x4000>; + }; + +- mipi@021e0000 { /* MIPI-DSI */ ++ mipi_dsi: mipi@021e0000 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <0x021e0000 0x4000>; ++ status = "disabled"; ++ ++ port@0 { ++ reg = <0>; ++ ++ mipi_mux_0: endpoint { ++ remote-endpoint = <&ipu1_di0_mipi>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ mipi_mux_1: endpoint { ++ remote-endpoint = <&ipu1_di1_mipi>; ++ }; ++ }; + }; + + vdoa@021e4000 { +@@ -954,7 +1037,8 @@ + }; + + ipu1: ipu@02400000 { +- #crtc-cells = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; + compatible = "fsl,imx6q-ipu"; + reg = <0x02400000 0x400000>; + interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>, +@@ -962,6 +1046,56 @@ + clocks = <&clks 130>, <&clks 131>, <&clks 132>; + clock-names = "bus", "di0", "di1"; + resets = <&src 2>; ++ ++ ipu1_di0: port@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <2>; ++ ++ ipu1_di0_disp0: endpoint@0 { ++ }; ++ ++ ipu1_di0_hdmi: endpoint@1 { ++ remote-endpoint = <&hdmi_mux_0>; ++ }; ++ ++ ipu1_di0_mipi: endpoint@2 { ++ remote-endpoint = <&mipi_mux_0>; ++ }; ++ ++ ipu1_di0_lvds0: endpoint@3 { ++ remote-endpoint = <&lvds0_mux_0>; ++ }; ++ ++ ipu1_di0_lvds1: endpoint@4 { ++ remote-endpoint = <&lvds1_mux_0>; ++ }; ++ }; ++ ++ ipu1_di1: port@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <3>; ++ ++ ipu1_di0_disp1: endpoint@0 { ++ }; ++ ++ ipu1_di1_hdmi: endpoint@1 { ++ remote-endpoint = <&hdmi_mux_1>; ++ }; ++ ++ ipu1_di1_mipi: endpoint@2 { ++ remote-endpoint = <&mipi_mux_1>; ++ }; ++ ++ ipu1_di1_lvds0: endpoint@3 { ++ remote-endpoint = <&lvds0_mux_1>; ++ }; ++ ++ ipu1_di1_lvds1: endpoint@4 { ++ remote-endpoint = <&lvds1_mux_1>; ++ }; ++ }; + }; + }; + }; +-- +1.8.5.3 + diff --git a/patches/imx_drm_dts/0009-staging-imx-drm-Update-TODO.patch b/patches/imx_drm_dts/0009-staging-imx-drm-Update-TODO.patch new file mode 100644 index 0000000000000000000000000000000000000000..0ed6b1e8fca32d5b4a8885f02c5094c780777c89 --- /dev/null +++ b/patches/imx_drm_dts/0009-staging-imx-drm-Update-TODO.patch @@ -0,0 +1,36 @@ +From e84d2b14bd682829b5960348d6ec35a9d1c99768 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <p.zabel@pengutronix.de> +Date: Tue, 18 Feb 2014 12:36:10 +0100 +Subject: [PATCH 9/9] staging: imx-drm: Update TODO + +The device tree bindings are updated regardless of the common display +framework and in the meantime the HDMI driver was included. + +Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> +--- + drivers/staging/imx-drm/TODO | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/drivers/staging/imx-drm/TODO b/drivers/staging/imx-drm/TODO +index 6a9da94..29636fb 100644 +--- a/drivers/staging/imx-drm/TODO ++++ b/drivers/staging/imx-drm/TODO +@@ -1,15 +1,10 @@ + TODO: + - get DRM Maintainer review for this code +-- Wait for common display framework to hit mainline and update the IPU +- driver to use it. This will most probably make changes to the devicetree +- bindings necessary. +-- Factor out more code to common helper functions + - decide where to put the base driver. It is not specific to a subsystem + and would be used by DRM/KMS and media/V4L2 + + Missing features (not necessarily for moving out of staging): + +-- Add i.MX6 HDMI support + - Add support for IC (Image converter) + - Add support for CSI (CMOS Sensor interface) + - Add support for VDIC (Video Deinterlacer) +-- +1.8.5.3 + diff --git a/patches/imx_video_staging/0002-ARM-dts-imx6qdl-wandboard-Add-HDMI-support.patch b/patches/imx_video_staging/0002-ARM-dts-imx6qdl-wandboard-Add-HDMI-support.patch index 4610d8ea8c59861bdcd181b49b1a24a63cf342a6..782acc374f8a7dd36fc3f9de34c840b98bafaf07 100644 --- a/patches/imx_video_staging/0002-ARM-dts-imx6qdl-wandboard-Add-HDMI-support.patch +++ b/patches/imx_video_staging/0002-ARM-dts-imx6qdl-wandboard-Add-HDMI-support.patch @@ -16,7 +16,7 @@ index abf0107..5e071c0 100644 &i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; -- pinctrl-0 = <&pinctrl_i2c1_1>; +- pinctrl-0 = <&pinctrl_i2c1>; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; +}; diff --git a/version.sh b/version.sh index 9c40794218f7ffe9c3e9309ebe58df2949021189..9509f7f094c3ab6f1ad3e4c42c010c202028c404 100644 --- a/version.sh +++ b/version.sh @@ -29,7 +29,7 @@ toolchain="gcc_linaro_gnueabihf_4_8" #Kernel/Build KERNEL_REL=3.14 KERNEL_TAG=${KERNEL_REL}-rc3 -BUILD=armv7-devel-r64 +BUILD=armv7-devel-r65 #v3.X-rcX + upto SHA KERNEL_SHA="e95003c3f9ccbfa7ab9d265e6eb703ee2fa4cfe7"