From: Philipp Zabel Date: Mon, 24 Nov 2014 15:33:34 +0000 (+0100) Subject: drm: imx: Move imx-drm driver out of staging X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=6556f7f82b9c;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git drm: imx: Move imx-drm driver out of staging The imx-drm driver was put into staging mostly for the following reasons, all of which have been addressed or superseded: - convert the irq driver to use linear irq domains - work out the device tree bindings, this lead to the common of_graph bindings being used - factor out common helper functions, this mostly resulted in the component framework and drm of_graph helpers. Before adding new fixes, and certainly before adding new features, move it into its proper place below drivers/gpu/drm. Signed-off-by: Philipp Zabel Signed-off-by: Dave Airlie --- diff --git a/Documentation/devicetree/bindings/drm/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/drm/imx/fsl-imx-drm.txt new file mode 100644 index 000000000000..e75f0e549fff --- /dev/null +++ b/Documentation/devicetree/bindings/drm/imx/fsl-imx-drm.txt @@ -0,0 +1,83 @@ +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-display-subsystem" +- ports: Should contain a list of phandles pointing to display interface ports + of IPU devices + +example: + +display-subsystem { + compatible = "fsl,display-subsystem"; + ports = <&ipu_di0>; +}; + + +Freescale i.MX IPUv3 +==================== + +Required properties: +- compatible: Should be "fsl,-ipu" +- reg: should be register base and length as documented in the + datasheet +- interrupts: Should contain sync interrupt and error interrupt, + in this order. +- 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 { + #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 +======================== + +Required properties: +- compatible: Should be "fsl,imx-parallel-display" +Optional properties: +- interface_pix_fmt: How this display is connected to the + display interface. Currently supported types: "rgb24", "rgb565", "bgr666" + and "lvds666". +- 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]; + interface-pix-fmt = "rgb24"; + + port { + display_in: endpoint { + remote-endpoint = <&ipu_di0_disp0>; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/drm/imx/hdmi.txt b/Documentation/devicetree/bindings/drm/imx/hdmi.txt new file mode 100644 index 000000000000..1b756cf9afb0 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/imx/hdmi.txt @@ -0,0 +1,58 @@ +Device-Tree bindings for HDMI Transmitter + +HDMI Transmitter +================ + +The HDMI Transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP +with accompanying PHY IP. + +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. + +Optional properties: + - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + +example: + + gpr: iomuxc-gpr@020e0000 { + /* ... */ + }; + + hdmi: hdmi@0120000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6q-hdmi"; + reg = <0x00120000 0x9000>; + interrupts = <0 115 0x04>; + gpr = <&gpr>; + clocks = <&clks 123>, <&clks 124>; + clock-names = "iahb", "isfr"; + ddc-i2c-bus = <&i2c2>; + + 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>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/drm/imx/ldb.txt b/Documentation/devicetree/bindings/drm/imx/ldb.txt new file mode 100644 index 000000000000..443bcb6134d5 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/imx/ldb.txt @@ -0,0 +1,122 @@ +Device-Tree bindings for LVDS Display Bridge (ldb) + +LVDS Display Bridge +=================== + +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,imx53-ldb" or "fsl,imx6q-ldb". + Both LDB versions are similar, but i.MX6 has an additional + multiplexer in the front to select any of the four IPU display + interfaces as input for each LVDS channel. + - gpr : should be <&gpr> on i.MX53 and i.MX6q. + The phandle points to the iomuxc-gpr region containing the LVDS + control register. +- clocks, clock-names : phandles to the LDB divider and selector clocks and to + the display interface selector clocks, as described in + Documentation/devicetree/bindings/clock/clock-bindings.txt + The following clocks are expected on i.MX53: + "di0_pll" - LDB LVDS channel 0 mux + "di1_pll" - LDB LVDS channel 1 mux + "di0" - LDB LVDS channel 0 gate + "di1" - LDB LVDS channel 1 gate + "di0_sel" - IPU1 DI0 mux + "di1_sel" - IPU1 DI1 mux + On i.MX6q the following additional clocks are needed: + "di2_sel" - IPU2 DI0 mux + "di3_sel" - IPU2 DI1 mux + The needed clock numbers for each are documented in + Documentation/devicetree/bindings/clock/imx5-clock.txt, and in + Documentation/devicetree/bindings/clock/imx6q-clock.txt. + +Optional properties: + - pinctrl-names : should be "default" on i.MX53, not used on i.MX6q + - pinctrl-0 : a phandle pointing to LVDS pin settings on i.MX53, + not used on i.MX6q + - fsl,dual-channel : boolean. if it exists, only LVDS channel 0 should + be configured - one input will be distributed on both outputs in dual + channel mode + +LVDS Channel +============ + +Each LVDS Channel has to contain a display-timings node that describes the +video timings for the connected LVDS display. For detailed information, also +have a look at Documentation/devicetree/bindings/video/display-timing.txt. + +Required properties: + - reg : should be <0> or <1> + - 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.MX5, the internal two-input-multiplexer is used. + Due to hardware limitations, only one port (port@[0,1]) + can be used for each channel (lvds-channel@[0,1], respectively) + On i.MX6, there should be four ports (port@[0-3]) that correspond + to the four LVDS multiplexer inputs. + +example: + +gpr: iomuxc-gpr@53fa8000 { + /* ... */ +}; + +ldb: ldb@53fa8008 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx53-ldb"; + gpr = <&gpr>; + clocks = <&clks 122>, <&clks 120>, + <&clks 115>, <&clks 116>, + <&clks 123>, <&clks 85>; + clock-names = "di0_pll", "di1_pll", + "di0_sel", "di1_sel", + "di0", "di1"; + + lvds-channel@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + fsl,data-mapping = "spwg"; + fsl,data-width = <24>; + + display-timings { + /* ... */ + }; + + port@0 { + reg = <0>; + + lvds0_in: endpoint { + remote-endpoint = <&ipu_di0_lvds0>; + }; + }; + }; + + lvds-channel@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + fsl,data-mapping = "spwg"; + fsl,data-width = <24>; + + display-timings { + /* ... */ + }; + + port@1 { + reg = <1>; + + lvds1_in: endpoint { + remote-endpoint = <&ipu_di1_lvds1>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt deleted file mode 100644 index e75f0e549fff..000000000000 --- a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt +++ /dev/null @@ -1,83 +0,0 @@ -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-display-subsystem" -- ports: Should contain a list of phandles pointing to display interface ports - of IPU devices - -example: - -display-subsystem { - compatible = "fsl,display-subsystem"; - ports = <&ipu_di0>; -}; - - -Freescale i.MX IPUv3 -==================== - -Required properties: -- compatible: Should be "fsl,-ipu" -- reg: should be register base and length as documented in the - datasheet -- interrupts: Should contain sync interrupt and error interrupt, - in this order. -- 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 { - #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 -======================== - -Required properties: -- compatible: Should be "fsl,imx-parallel-display" -Optional properties: -- interface_pix_fmt: How this display is connected to the - display interface. Currently supported types: "rgb24", "rgb565", "bgr666" - and "lvds666". -- 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]; - interface-pix-fmt = "rgb24"; - - port { - display_in: endpoint { - remote-endpoint = <&ipu_di0_disp0>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt b/Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt deleted file mode 100644 index 1b756cf9afb0..000000000000 --- a/Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt +++ /dev/null @@ -1,58 +0,0 @@ -Device-Tree bindings for HDMI Transmitter - -HDMI Transmitter -================ - -The HDMI Transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP -with accompanying PHY IP. - -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. - -Optional properties: - - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing - -example: - - gpr: iomuxc-gpr@020e0000 { - /* ... */ - }; - - hdmi: hdmi@0120000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,imx6q-hdmi"; - reg = <0x00120000 0x9000>; - interrupts = <0 115 0x04>; - gpr = <&gpr>; - clocks = <&clks 123>, <&clks 124>; - clock-names = "iahb", "isfr"; - ddc-i2c-bus = <&i2c2>; - - 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>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt b/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt deleted file mode 100644 index 443bcb6134d5..000000000000 --- a/Documentation/devicetree/bindings/staging/imx-drm/ldb.txt +++ /dev/null @@ -1,122 +0,0 @@ -Device-Tree bindings for LVDS Display Bridge (ldb) - -LVDS Display Bridge -=================== - -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,imx53-ldb" or "fsl,imx6q-ldb". - Both LDB versions are similar, but i.MX6 has an additional - multiplexer in the front to select any of the four IPU display - interfaces as input for each LVDS channel. - - gpr : should be <&gpr> on i.MX53 and i.MX6q. - The phandle points to the iomuxc-gpr region containing the LVDS - control register. -- clocks, clock-names : phandles to the LDB divider and selector clocks and to - the display interface selector clocks, as described in - Documentation/devicetree/bindings/clock/clock-bindings.txt - The following clocks are expected on i.MX53: - "di0_pll" - LDB LVDS channel 0 mux - "di1_pll" - LDB LVDS channel 1 mux - "di0" - LDB LVDS channel 0 gate - "di1" - LDB LVDS channel 1 gate - "di0_sel" - IPU1 DI0 mux - "di1_sel" - IPU1 DI1 mux - On i.MX6q the following additional clocks are needed: - "di2_sel" - IPU2 DI0 mux - "di3_sel" - IPU2 DI1 mux - The needed clock numbers for each are documented in - Documentation/devicetree/bindings/clock/imx5-clock.txt, and in - Documentation/devicetree/bindings/clock/imx6q-clock.txt. - -Optional properties: - - pinctrl-names : should be "default" on i.MX53, not used on i.MX6q - - pinctrl-0 : a phandle pointing to LVDS pin settings on i.MX53, - not used on i.MX6q - - fsl,dual-channel : boolean. if it exists, only LVDS channel 0 should - be configured - one input will be distributed on both outputs in dual - channel mode - -LVDS Channel -============ - -Each LVDS Channel has to contain a display-timings node that describes the -video timings for the connected LVDS display. For detailed information, also -have a look at Documentation/devicetree/bindings/video/display-timing.txt. - -Required properties: - - reg : should be <0> or <1> - - 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.MX5, the internal two-input-multiplexer is used. - Due to hardware limitations, only one port (port@[0,1]) - can be used for each channel (lvds-channel@[0,1], respectively) - On i.MX6, there should be four ports (port@[0-3]) that correspond - to the four LVDS multiplexer inputs. - -example: - -gpr: iomuxc-gpr@53fa8000 { - /* ... */ -}; - -ldb: ldb@53fa8008 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,imx53-ldb"; - gpr = <&gpr>; - clocks = <&clks 122>, <&clks 120>, - <&clks 115>, <&clks 116>, - <&clks 123>, <&clks 85>; - clock-names = "di0_pll", "di1_pll", - "di0_sel", "di1_sel", - "di0", "di1"; - - lvds-channel@0 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - fsl,data-mapping = "spwg"; - fsl,data-width = <24>; - - display-timings { - /* ... */ - }; - - port@0 { - reg = <0>; - - lvds0_in: endpoint { - remote-endpoint = <&ipu_di0_lvds0>; - }; - }; - }; - - lvds-channel@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - fsl,data-mapping = "spwg"; - fsl,data-width = <24>; - - display-timings { - /* ... */ - }; - - port@1 { - reg = <1>; - - lvds1_in: endpoint { - remote-endpoint = <&ipu_di1_lvds1>; - }; - }; - }; -}; diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 37c5a6ea5bdf..24c2d7caedd5 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -202,3 +202,5 @@ source "drivers/gpu/drm/panel/Kconfig" source "drivers/gpu/drm/sti/Kconfig" source "drivers/gpu/drm/amd/amdkfd/Kconfig" + +source "drivers/gpu/drm/imx/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index dd9d35bfa690..47d89869c5df 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ +obj-$(CONFIG_DRM_IMX) += imx/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig new file mode 100644 index 000000000000..82fb758a29bc --- /dev/null +++ b/drivers/gpu/drm/imx/Kconfig @@ -0,0 +1,53 @@ +config DRM_IMX + tristate "DRM Support for Freescale i.MX" + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select VIDEOMODE_HELPERS + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM) + help + enable i.MX graphics support + +config DRM_IMX_FB_HELPER + tristate "provide legacy framebuffer /dev/fb0" + select DRM_KMS_CMA_HELPER + depends on DRM_IMX + help + The DRM framework can provide a legacy /dev/fb0 framebuffer + for your device. This is necessary to get a framebuffer console + and also for applications using the legacy framebuffer API + +config DRM_IMX_PARALLEL_DISPLAY + tristate "Support for parallel displays" + select DRM_PANEL + depends on DRM_IMX + select VIDEOMODE_HELPERS + +config DRM_IMX_TVE + tristate "Support for TV and VGA displays" + depends on DRM_IMX + select REGMAP_MMIO + help + Choose this to enable the internal Television Encoder (TVe) + found on i.MX53 processors. + +config DRM_IMX_LDB + tristate "Support for LVDS displays" + depends on DRM_IMX && MFD_SYSCON + help + Choose this to enable the internal LVDS Display Bridge (LDB) + found on i.MX53 and i.MX6 processors. + +config DRM_IMX_IPUV3 + tristate "DRM Support for i.MX IPUv3" + depends on DRM_IMX + depends on IMX_IPUV3_CORE + help + Choose this if you have a i.MX5 or i.MX6 processor. + +config DRM_IMX_HDMI + tristate "Freescale i.MX DRM HDMI" + depends on DRM_IMX + help + Choose this if you want to use HDMI on i.MX6. diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile new file mode 100644 index 000000000000..582c438d8cbd --- /dev/null +++ b/drivers/gpu/drm/imx/Makefile @@ -0,0 +1,12 @@ + +imxdrm-objs := imx-drm-core.o + +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 + +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 diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c new file mode 100644 index 000000000000..2f8007241734 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -0,0 +1,705 @@ +/* + * Freescale i.MX drm driver + * + * Copyright (C) 2011 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-drm.h" + +#define MAX_CRTC 4 + +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]; + int pipes; + struct drm_fbdev_cma *fbhelper; +}; + +struct imx_drm_crtc { + struct drm_crtc *crtc; + int pipe; + struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; + struct device_node *port; +}; + +static int legacyfb_depth = 16; +module_param(legacyfb_depth, int, 0444); + +int imx_drm_crtc_id(struct imx_drm_crtc *crtc) +{ + return crtc->pipe; +} +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; +#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 + + component_unbind_all(drm->dev, drm); + + drm_vblank_cleanup(drm); + drm_mode_config_cleanup(drm); + + platform_set_drvdata(drm->platformdev, NULL); + + return 0; +} + +static struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) +{ + struct imx_drm_device *imxdrm = crtc->dev->dev_private; + unsigned i; + + for (i = 0; i < MAX_CRTC; i++) + if (imxdrm->crtc[i] && imxdrm->crtc[i]->crtc == crtc) + return imxdrm->crtc[i]; + + return NULL; +} + +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(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(encoder->crtc, + encoder->encoder_type, interface_pix_fmt, + hsync_pin, vsync_pin); + return 0; +} +EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); + +int imx_drm_panel_format(struct drm_encoder *encoder, u32 interface_pix_fmt) +{ + return imx_drm_panel_format_pins(encoder, interface_pix_fmt, 2, 3); +} +EXPORT_SYMBOL_GPL(imx_drm_panel_format); + +int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc) +{ + return drm_vblank_get(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); +} +EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get); + +void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc) +{ + drm_vblank_put(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); +} +EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put); + +void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc) +{ + drm_handle_vblank(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); +} +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 = imxdrm->crtc[crtc]; + int ret; + + if (!imx_drm_crtc) + return -EINVAL; + + if (!imx_drm_crtc->imx_drm_helper_funcs.enable_vblank) + return -ENOSYS; + + ret = imx_drm_crtc->imx_drm_helper_funcs.enable_vblank( + imx_drm_crtc->crtc); + + return ret; +} + +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 = imxdrm->crtc[crtc]; + + if (!imx_drm_crtc) + return; + + if (!imx_drm_crtc->imx_drm_helper_funcs.disable_vblank) + return; + + imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(imx_drm_crtc->crtc); +} + +static void imx_drm_driver_preclose(struct drm_device *drm, + struct drm_file *file) +{ + int i; + + if (!file->is_master) + return; + + for (i = 0; i < MAX_CRTC; i++) + imx_drm_disable_vblank(drm, i); +} + +static const struct file_operations imx_drm_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_gem_cma_mmap, + .poll = drm_poll, + .read = drm_read, + .llseek = noop_llseek, +}; + +void imx_drm_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(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 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, +}; + +/* + * 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) +{ + 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; + + /* + * enable drm irq mode. + * - with irq_enabled = true, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler and + * drivers can well take care of their interrupts + */ + 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); + + ret = drm_vblank_init(drm, MAX_CRTC); + if (ret) + 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) + */ + drm->vblank_disable_allowed = true; + + platform_set_drvdata(drm->platformdev, drm); + + /* Now try and bind all our sub-components */ + ret = component_bind_all(drm->dev, drm); + if (ret) + goto err_vblank; + + /* + * 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_connector_register(connector); + if (ret) { + dev_err(drm->dev, + "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", + connector->base.id, + connector->name, ret); + goto err_unbind; + } + } + + /* + * 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 + + drm_kms_helper_poll_init(drm); + + return 0; + +err_unbind: + component_unbind_all(drm->dev, drm); +err_vblank: + drm_vblank_cleanup(drm); +err_kms: + drm_mode_config_cleanup(drm); + + return ret; +} + +/* + * imx_drm_add_crtc - add a new 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 device_node *port) +{ + struct imx_drm_device *imxdrm = drm->dev_private; + struct imx_drm_crtc *imx_drm_crtc; + int ret; + + /* + * The vblank arrays are dimensioned by MAX_CRTC - we can't + * pass IDs greater than this to those functions. + */ + if (imxdrm->pipes >= MAX_CRTC) + return -EINVAL; + + if (imxdrm->drm->open_count) + return -EBUSY; + + imx_drm_crtc = kzalloc(sizeof(*imx_drm_crtc), GFP_KERNEL); + 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->port = port; + imx_drm_crtc->crtc = crtc; + + imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; + + *new_crtc = imx_drm_crtc; + + ret = drm_mode_crtc_set_gamma_size(imx_drm_crtc->crtc, 256); + if (ret) + goto err_register; + + drm_crtc_helper_add(crtc, + imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs); + + drm_crtc_init(drm, crtc, + imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs); + + return 0; + +err_register: + imxdrm->crtc[imx_drm_crtc->pipe] = NULL; + kfree(imx_drm_crtc); + return ret; +} +EXPORT_SYMBOL_GPL(imx_drm_add_crtc); + +/* + * imx_drm_remove_crtc - remove a crtc + */ +int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) +{ + 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; + + kfree(imx_drm_crtc); + + return 0; +} +EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); + +/* + * 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, + struct device_node *endpoint) +{ + struct device_node *port; + unsigned i; + + port = of_graph_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->port == port) + return drm_crtc_mask(imx_drm_crtc->crtc); + } + + return 0; +} + +static struct device_node *imx_drm_of_get_next_endpoint( + const struct device_node *parent, struct device_node *prev) +{ + struct device_node *node = of_graph_get_next_endpoint(parent, prev); + + of_node_put(prev); + return node; +} + +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; + + for (i = 0; ; i++) { + u32 mask; + + ep = imx_drm_of_get_next_endpoint(np, ep); + if (!ep) + break; + + mask = imx_drm_find_crtc_mask(imxdrm, ep); + + /* + * 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; + } + + of_node_put(ep); + if (i == 0) + return -ENOENT; + + 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); + +/* + * @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 of_endpoint endpoint; + struct device_node *port; + int ret; + + if (!node || !imx_crtc) + return -EINVAL; + + do { + ep = imx_drm_of_get_next_endpoint(node, ep); + if (!ep) + break; + + port = of_graph_get_remote_port(ep); + of_node_put(port); + if (port == imx_crtc->port) { + ret = of_graph_parse_endpoint(ep, &endpoint); + return ret ? ret : endpoint.port; + } + } while (ep); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); + +static const struct drm_ioctl_desc imx_drm_ioctls[] = { + /* none so far */ +}; + +static struct drm_driver imx_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .load = imx_drm_driver_load, + .unload = imx_drm_driver_unload, + .lastclose = imx_drm_driver_lastclose, + .preclose = imx_drm_driver_preclose, + .set_busid = drm_platform_set_busid, + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = imx_drm_enable_vblank, + .disable_vblank = imx_drm_disable_vblank, + .ioctls = imx_drm_ioctls, + .num_ioctls = ARRAY_SIZE(imx_drm_ioctls), + .fops = &imx_drm_driver_fops, + .name = "imx-drm", + .desc = "i.MX DRM graphics", + .date = "20120507", + .major = 1, + .minor = 0, + .patchlevel = 0, +}; + +static int compare_of(struct device *dev, void *data) +{ + struct device_node *np = data; + + /* 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); + } + + return dev->of_node == np; +} + +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 = { + .bind = imx_drm_bind, + .unbind = imx_drm_unbind, +}; + +static int imx_drm_platform_probe(struct platform_device *pdev) +{ + struct device_node *ep, *port, *remote; + struct component_match *match = NULL; + 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; + + component_match_add(&pdev->dev, &match, compare_of, port); + } + + 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 = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote)) { + of_node_put(remote); + continue; + } else if (!of_device_is_available(remote->parent)) { + dev_warn(&pdev->dev, "parent device of %s is not available\n", + remote->full_name); + of_node_put(remote); + continue; + } + + component_match_add(&pdev->dev, &match, compare_of, remote); + of_node_put(remote); + } + of_node_put(port); + } + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + return component_master_add_with_match(&pdev->dev, &imx_drm_ops, match); +} + +static int imx_drm_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &imx_drm_ops); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int imx_drm_suspend(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + /* The drm_dev is NULL before .load hook is called */ + if (drm_dev == NULL) + return 0; + + drm_kms_helper_poll_disable(drm_dev); + + return 0; +} + +static int imx_drm_resume(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + if (drm_dev == NULL) + return 0; + + drm_helper_resume_force_mode(drm_dev); + drm_kms_helper_poll_enable(drm_dev); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(imx_drm_pm_ops, imx_drm_suspend, imx_drm_resume); + +static const struct of_device_id imx_drm_dt_ids[] = { + { .compatible = "fsl,imx-display-subsystem", }, + { /* 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", + .pm = &imx_drm_pm_ops, + .of_match_table = imx_drm_dt_ids, + }, +}; +module_platform_driver(imx_drm_pdrv); + +MODULE_AUTHOR("Sascha Hauer "); +MODULE_DESCRIPTION("i.MX drm driver core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h new file mode 100644 index 000000000000..7453ae00c412 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -0,0 +1,56 @@ +#ifndef _IMX_DRM_H_ +#define _IMX_DRM_H_ + +struct device_node; +struct drm_crtc; +struct drm_connector; +struct drm_device; +struct drm_display_mode; +struct drm_encoder; +struct drm_fbdev_cma; +struct drm_framebuffer; +struct imx_drm_crtc; +struct platform_device; + +int imx_drm_crtc_id(struct imx_drm_crtc *crtc); + +struct imx_drm_crtc_helper_funcs { + int (*enable_vblank)(struct drm_crtc *crtc); + void (*disable_vblank)(struct drm_crtc *crtc); + int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 encoder_type, + u32 pix_fmt, int hsync_pin, int vsync_pin); + const struct drm_crtc_helper_funcs *crtc_helper_funcs; + const struct drm_crtc_funcs *crtc_funcs; +}; + +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 device_node *port); +int imx_drm_remove_crtc(struct imx_drm_crtc *); +int imx_drm_init_drm(struct platform_device *pdev, + int preferred_bpp); +int imx_drm_exit_drm(void); + +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); + +void imx_drm_mode_config_init(struct drm_device *drm); + +struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); + +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); + +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); + +void imx_drm_connector_destroy(struct drm_connector *connector); +void imx_drm_encoder_destroy(struct drm_encoder *encoder); + +#endif /* _IMX_DRM_H_ */ diff --git a/drivers/gpu/drm/imx/imx-hdmi.c b/drivers/gpu/drm/imx/imx-hdmi.c new file mode 100644 index 000000000000..aaec6b2cdf56 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-hdmi.c @@ -0,0 +1,1767 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * SH-Mobile High-Definition Multimedia Interface (HDMI) driver + * for SLISHDMI13T and SLIPHDMIT IP cores + * + * Copyright (C) 2010, Guennadi Liakhovetski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include