Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 3/5] ARM: dts: imx6qdl-sabrelite: add missing USB PHY reset control
From: Gary Bisson @ 2016-10-25 21:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAOMZO5Aok5w=Di0hZpr7E6PZr6rJ1Xg-xkWcB+u=7xKCT5uWvA@mail.gmail.com>

Hi Fabio,

On Tue, Oct 25, 2016 at 11:28 PM, Fabio Estevam <festevam@gmail.com> wrote:
> Hi Gary,
>
> On Tue, Oct 25, 2016 at 7:19 PM, Gary Bisson
> <gary.bisson@boundarydevices.com> wrote:
>> Declared as a regulator since the driver doesn't have a reset-gpios
>> property for this.
>
> Peter Chen is working on adding USB reset-gpio property this. Please
> check his series:
> https://www.spinics.net/lists/arm-kernel/msg536105.html

Thanks, I wasn't aware of this series. Indeed if this power sequence
code gets upstream soon I guess we can drop both patches about the USB
PHY reset.

Note that our Nitrogen6_MAX is using the regulator approach, it will
need to be updated once Peter's series gets merged.

Regards,
Gary

^ permalink raw reply

* [PATCH] ARM: DTS: da850: Add DMA to SPI0
From: David Lechner @ 2016-10-25 23:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add the bindings for DMA on SPI0

Signed-off-by: David Lechner <david@lechnology.com>
---
 arch/arm/boot/dts/da850.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
index 06a5abc..baadebe 100644
--- a/arch/arm/boot/dts/da850.dtsi
+++ b/arch/arm/boot/dts/da850.dtsi
@@ -349,6 +349,8 @@
 			num-cs = <6>;
 			ti,davinci-spi-intr-line = <1>;
 			interrupts = <20>;
+			dmas = <&edma0 14 0>, <&edma0 15 0>;
+			dma-names = "rx", "tx";
 			status = "disabled";
 		};
 		spi1: spi at 30e000 {
-- 
2.7.4

^ permalink raw reply related

* [linux-sunxi] Re: [PATCH v5 1/7] drm: sunxi: Add a basic DRM driver for Allwinner DE2
From: André Przywara @ 2016-10-25 23:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161025161441.6b248efe9229bd80e3f7a33c@free.fr>

On 25/10/16 15:14, Jean-Francois Moine wrote:
> On Mon, 24 Oct 2016 16:04:19 +0200
> Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> 
>> Hi,
> 
> Hi Maxime,
> 
>> On Fri, Oct 21, 2016 at 09:26:18AM +0200, Jean-Francois Moine wrote:
>>> Allwinner's recent SoCs, as A64, A83T and H3, contain a new display
>>> engine, DE2.
>>> This patch adds a DRM video driver for this device.
>>>
>>> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
>>
>> Output from checkpatch:
>> total: 0 errors, 20 warnings, 83 checks, 1799 lines checked
>>
>>> ---
>>>  .../bindings/display/sunxi/sunxi-de2.txt           |  83 +++
>>>  drivers/gpu/drm/Kconfig                            |   2 +
>>>  drivers/gpu/drm/Makefile                           |   1 +
>>>  drivers/gpu/drm/sunxi/Kconfig                      |  21 +
>>>  drivers/gpu/drm/sunxi/Makefile                     |   7 +
>>>  drivers/gpu/drm/sunxi/de2_crtc.c                   | 475 +++++++++++++++++
>>>  drivers/gpu/drm/sunxi/de2_crtc.h                   |  63 +++
>>>  drivers/gpu/drm/sunxi/de2_de.c                     | 591 +++++++++++++++++++++
>>>  drivers/gpu/drm/sunxi/de2_drm.h                    |  47 ++
>>>  drivers/gpu/drm/sunxi/de2_drv.c                    | 378 +++++++++++++
>>>  drivers/gpu/drm/sunxi/de2_plane.c                  | 119 +++++
>>>  11 files changed, 1787 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
>>>  create mode 100644 drivers/gpu/drm/sunxi/Kconfig
>>>  create mode 100644 drivers/gpu/drm/sunxi/Makefile
>>>  create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.c
>>>  create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.h
>>>  create mode 100644 drivers/gpu/drm/sunxi/de2_de.c
>>>  create mode 100644 drivers/gpu/drm/sunxi/de2_drm.h
>>>  create mode 100644 drivers/gpu/drm/sunxi/de2_drv.c
>>>  create mode 100644 drivers/gpu/drm/sunxi/de2_plane.c
>>>
>>> diff --git a/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
>>> new file mode 100644
>>> index 0000000..f9cd67a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
>>> @@ -0,0 +1,83 @@
>>> +Allwinner sunxi Display Engine 2 subsystem
>>> +==========================================
>>> +
>>> +The sunxi DE2 subsystem contains a display controller (DE2),
>>
>> sunxi is a made up name, and doesn't really mean anything. You can
>> call it either sun8i (because it was introduced in that family).
> 
> OK.
> 
>>> +one or two LCD controllers (TCON) and their external interfaces.
>>> +
>>> +Display controller
>>> +==================
>>> +
>>> +Required properties:
>>> +
>>> +- compatible: value should be one of the following
>>> +		"allwinner,sun8i-a83t-display-engine"
>>> +		"allwinner,sun8i-h3-display-engine"
>>> +
>>> +- clocks: must include clock specifiers corresponding to entries in the
>>> +		clock-names property.
>>> +
>>> +- clock-names: must contain
>>> +		"gate": for DE activation
>>> +		"clock": DE clock
>>
>> We've been calling them bus and mod.
> 
> I can understand "bus" (which is better than "apb"), but why "mod"?
> 
>>> +
>>> +- resets: phandle to the reset of the device
>>> +
>>> +- ports: phandle's to the LCD ports
>>
>> Please use the OF graph.
> 
> These ports are references to the graph of nodes. See
> 	http://www.kernelhub.org/?msg=911825&p=2
> 
> 	[snip]
>>> diff --git a/drivers/gpu/drm/sunxi/de2_crtc.c b/drivers/gpu/drm/sunxi/de2_crtc.c
>>> new file mode 100644
>>> index 0000000..dae0fab
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/sunxi/de2_crtc.c
>>> @@ -0,0 +1,475 @@
>>> +/*
>>> + * Allwinner DRM driver - DE2 CRTC
>>> + *
>>> + * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License as
>>> + * published by the Free Software Foundation; either version 2 of
>>> + * the License, or (at your option) any later version.
>>> + */
>>> +
>>> +#include <linux/component.h>
>>> +#include <drm/drm_crtc_helper.h>
>>> +#include <drm/drm_atomic_helper.h>
>>> +#include <asm/io.h>
>>> +#include <linux/of_irq.h>
>>> +
>>> +#include "de2_drm.h"
>>> +#include "de2_crtc.h"
>>> +
>>> +/* I/O map */
>>> +
>>> +struct tcon {
>>> +	u32 gctl;
>>> +#define		TCON_GCTL_TCON_En BIT(31)
>>> +	u32 gint0;
>>> +#define		TCON_GINT0_TCON1_Vb_Int_En BIT(30)
>>> +#define		TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
>>> +	u32 gint1;
>>> +	u32 dum0[13];
>>> +	u32 tcon0_ctl;				/* 0x40 */
>>> +#define		TCON0_CTL_TCON_En BIT(31)
>>> +	u32 dum1[19];
>>> +	u32 tcon1_ctl;				/* 0x90 */
>>> +#define		TCON1_CTL_TCON_En BIT(31)
>>> +#define		TCON1_CTL_Interlace_En BIT(20)
>>> +#define		TCON1_CTL_Start_Delay_SHIFT 4
>>> +#define		TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
>>> +	u32 basic0;			/* XI/YI */
>>> +	u32 basic1;			/* LS_XO/LS_YO */
>>> +	u32 basic2;			/* XO/YO */
>>> +	u32 basic3;			/* HT/HBP */
>>> +	u32 basic4;			/* VT/VBP */
>>> +	u32 basic5;			/* HSPW/VSPW */
>>> +	u32 dum2;
>>> +	u32 ps_sync;				/* 0xb0 */
>>> +	u32 dum3[15];
>>> +	u32 io_pol;				/* 0xf0 */
>>> +#define		TCON1_IO_POL_IO0_inv BIT(24)
>>> +#define		TCON1_IO_POL_IO1_inv BIT(25)
>>> +#define		TCON1_IO_POL_IO2_inv BIT(26)
>>> +	u32 io_tri;
>>> +	u32 dum4[2];
>>> +
>>> +	u32 ceu_ctl;				/* 0x100 */
>>> +#define     TCON_CEU_CTL_ceu_en BIT(31)
>>> +	u32 dum5[3];
>>> +	u32 ceu_rr;
>>> +	u32 ceu_rg;
>>> +	u32 ceu_rb;
>>> +	u32 ceu_rc;
>>> +	u32 ceu_gr;
>>> +	u32 ceu_gg;
>>> +	u32 ceu_gb;
>>> +	u32 ceu_gc;
>>> +	u32 ceu_br;
>>> +	u32 ceu_bg;
>>> +	u32 ceu_bb;
>>> +	u32 ceu_bc;
>>> +	u32 ceu_rv;
>>> +	u32 ceu_gv;
>>> +	u32 ceu_bv;
>>> +	u32 dum6[45];
>>> +
>>> +	u32 mux_ctl;				/* 0x200 */
>>> +	u32 dum7[63];
>>> +
>>> +	u32 fill_ctl;				/* 0x300 */
>>> +	u32 fill_start0;
>>> +	u32 fill_end0;
>>> +	u32 fill_data0;
>>> +};
>>
>> Please use defines instead of the structures.
> 
> I think that structures are more readable.

I think for the kernel we don't use C structs to model register frames.
The main argument against it is that putting them into a structure puts
the actual offset into the hands of the compiler, which is free to
insert padding and alignment - within the rules of the ABI. This happens
to work when we use 32-bit registers and maybe char fillers only. Most
ABIs seem to agree on this part, but there is no guarantee.
In fact all those various ABIs used by the kernel could have subtle
differences between architectures (for instance for alignment of 64-bit
members), so we produce potentially non-portable code.

Also I find it actually harder to read, since the manual refers to
register offset addresses, which makes it hard to match with the code,
especially if we deviate with the register naming. The fact that we have
occasional *comments* to denote the actual offset is a hint that
something is sub-optimal.
Also having these dummy fillers is really error prone once we insert new
registers, as the fill value has to be adjusted accordingly.

So can we stick with what the kernel uses elsewhere and don't pretend
that because the current GCC compiles that fine on ARM it will be good
forever?

Thanks!
Andre.

^ permalink raw reply

* [PATCH] ARM: mvebu: Update comment for main PLL frequency
From: Chris Packham @ 2016-10-25 23:52 UTC (permalink / raw)
  To: linux-arm-kernel

The actual frequency was updated in commit ae142bd99765 ("ARM: mvebu:
Fix the main PLL frequency on Armada 375, 38x and 39x SoCs") but the
comment was not updated. Update it now.

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
 arch/arm/boot/dts/armada-375.dtsi | 2 +-
 arch/arm/boot/dts/armada-38x.dtsi | 2 +-
 arch/arm/boot/dts/armada-39x.dtsi | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi
index cc952cf8ec30..45fa92f9cf5c 100644
--- a/arch/arm/boot/dts/armada-375.dtsi
+++ b/arch/arm/boot/dts/armada-375.dtsi
@@ -65,7 +65,7 @@
 	};
 
 	clocks {
-		/* 2 GHz fixed main PLL */
+		/* 1 GHz fixed main PLL */
 		mainpll: mainpll {
 			compatible = "fixed-clock";
 			#clock-cells = <0>;
diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
index 2d7668848c5a..7450e9fea45d 100644
--- a/arch/arm/boot/dts/armada-38x.dtsi
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -661,7 +661,7 @@
 	};
 
 	clocks {
-		/* 2 GHz fixed main PLL */
+		/* 1 GHz fixed main PLL */
 		mainpll: mainpll {
 			compatible = "fixed-clock";
 			#clock-cells = <0>;
diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi
index 34cba87f9200..de171baffcf6 100644
--- a/arch/arm/boot/dts/armada-39x.dtsi
+++ b/arch/arm/boot/dts/armada-39x.dtsi
@@ -573,7 +573,7 @@
 	};
 
 	clocks {
-		/* 2 GHz fixed main PLL */
+		/* 1 GHz fixed main PLL */
 		mainpll: mainpll {
 			compatible = "fixed-clock";
 			#clock-cells = <0>;
-- 
2.10.1

^ permalink raw reply related

* [RFC PATCH 0/6] media: davinci: VPIF: add DT support
From: Kevin Hilman @ 2016-10-25 23:55 UTC (permalink / raw)
  To: linux-arm-kernel

This series attempts to add DT support to the davinci VPIF capture
driver.

I'm not sure I've completely grasped the proper use of the ports and
endpoints stuff, so this RFC is primarily to get input on whether I'm
on the right track.

The last patch is the one where all my questions are, the rest are
just prep work to ge there.

Tested on da850-lcdk and was able to do basic frame capture from the
composite input.

Series applies on v4.9-rc1

Kevin Hilman (6):
  [media] davinci: add support for DT init
  ARM: davinci: da8xx: VPIF: enable DT init
  ARM: dts: davinci: da850: add VPIF
  ARM: dts: davinci: da850-lcdk: enable VPIF capture
  [media] davinci: vpif_capture: don't lock over s_stream
  [media] davinci: vpif_capture: get subdevs from DT

 arch/arm/boot/dts/da850-lcdk.dts              |  30 ++++++
 arch/arm/boot/dts/da850.dtsi                  |  28 +++++
 arch/arm/mach-davinci/da8xx-dt.c              |  17 +++
 drivers/media/platform/davinci/vpif.c         |   9 ++
 drivers/media/platform/davinci/vpif_capture.c | 150 +++++++++++++++++++++++++-
 include/media/davinci/vpif_types.h            |   9 +-
 6 files changed, 236 insertions(+), 7 deletions(-)

-- 
2.9.3

^ permalink raw reply

* [RFC PATCH 1/6] [media] davinci: add support for DT init
From: Kevin Hilman @ 2016-10-25 23:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161025235536.7342-1-khilman@baylibre.com>

Add basic support for initialization via DT.

Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
 drivers/media/platform/davinci/vpif.c         |  9 +++++++++
 drivers/media/platform/davinci/vpif_capture.c | 14 ++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index 0380cf2e5775..077e328e0281 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -464,8 +464,17 @@ static const struct dev_pm_ops vpif_pm = {
 #define vpif_pm_ops NULL
 #endif
 
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id vpif_of_match[] = {
+	{ .compatible = "ti,vpif", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, vpif_of_match);
+#endif
+
 static struct platform_driver vpif_driver = {
 	.driver = {
+		.of_match_table = of_match_ptr(vpif_of_match),
 		.name	= "vpif",
 		.pm	= vpif_pm_ops,
 	},
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 5104cc0ee40e..79cef74e164f 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -1435,6 +1435,11 @@ static __init int vpif_probe(struct platform_device *pdev)
 	int res_idx = 0;
 	int i, err;
 
+	if (!pdev->dev.platform_data) {
+		dev_warn(&pdev->dev, "Missing platform data.  Giving up.\n");
+		return -EINVAL;
+	}
+
 	vpif_dev = &pdev->dev;
 
 	err = initialize_vpif();
@@ -1618,8 +1623,17 @@ static int vpif_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume);
 
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id vpif_capture_of_match[] = {
+	{ .compatible = "ti,vpif-capture", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, vpif_capture_of_match);
+#endif
+
 static __refdata struct platform_driver vpif_driver = {
 	.driver	= {
+		.of_match_table = of_match_ptr(vpif_capture_of_match),
 		.name	= VPIF_DRIVER_NAME,
 		.pm	= &vpif_pm_ops,
 	},
-- 
2.9.3

^ permalink raw reply related

* [RFC PATCH 2/6] ARM: davinci: da8xx: VPIF: enable DT init
From: Kevin Hilman @ 2016-10-25 23:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161025235536.7342-1-khilman@baylibre.com>

Add basic support for DT initializaion of VPIF (capture) via DT.  Clocks
and mux still need to happen in this file until there are real clock and
pinctrl drivers, but the video nodes and subdevs can all come from DT.

Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
 arch/arm/mach-davinci/da8xx-dt.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c
index c9f7e9274aa8..e1b7d72f9070 100644
--- a/arch/arm/mach-davinci/da8xx-dt.c
+++ b/arch/arm/mach-davinci/da8xx-dt.c
@@ -17,6 +17,7 @@
 #include <mach/common.h>
 #include "cp_intc.h"
 #include <mach/da8xx.h>
+#include <mach/mux.h>
 
 static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("ti,davinci-i2c", 0x01c22000, "i2c_davinci.1", NULL),
@@ -38,14 +39,30 @@ static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = {
 		       NULL),
 	OF_DEV_AUXDATA("ti,da830-mcasp-audio", 0x01d00000, "davinci-mcasp.0", NULL),
 	OF_DEV_AUXDATA("ti,da850-aemif", 0x68000000, "ti-aemif", NULL),
+	OF_DEV_AUXDATA("ti,vpif", 0x01e17000, "vpif", NULL),
 	{}
 };
 
 #ifdef CONFIG_ARCH_DAVINCI_DA850
 
+#if IS_ENABLED(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE)
+static __init void da850_vpif_capture_init(void)
+{
+	int ret;
+	
+	ret = davinci_cfg_reg_list(da850_vpif_capture_pins);
+	if (ret)
+		pr_warn("da850_evm_init: VPIF capture mux setup failed: %d\n",
+			ret);
+}
+#else
+#define da850_vpif_capture_init()
+#endif
+
 static void __init da850_init_machine(void)
 {
 	of_platform_default_populate(NULL, da850_auxdata_lookup, NULL);
+	da850_vpif_capture_init();
 }
 
 static const char *const da850_boards_compat[] __initconst = {
-- 
2.9.3

^ permalink raw reply related

* [RFC PATCH 3/6] ARM: dts: davinci: da850: add VPIF
From: Kevin Hilman @ 2016-10-25 23:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161025235536.7342-1-khilman@baylibre.com>

Add VPIF and VPIF capture nodes to da850.

Note that these are separate nodes because the current media drivers
have two separate drivers for vpif and vpif_capture.

Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
 arch/arm/boot/dts/da850.dtsi | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
index f79e1b91c680..62c5b3e65071 100644
--- a/arch/arm/boot/dts/da850.dtsi
+++ b/arch/arm/boot/dts/da850.dtsi
@@ -399,7 +399,35 @@
 				<&edma0 0 1>;
 			dma-names = "tx", "rx";
 		};
+
+		vpif: video at 0x00217000 {
+			compatible = "ti,vpif";
+			reg = <0x00217000 0x1000>;
+			status = "disabled";
+		};
+
+		vpif_capture: video-capture at 0x00217000 {
+			compatible = "ti,vpif-capture";
+			reg = <0x00217000 0x1000>;
+			interrupts = <92>;
+			status = "disabled";
+
+			/* VPIF capture: input channels */
+			port {
+				vpif_ch0: endpoint at 0 {
+					  reg = <0>;
+					  bus-width = <8>;
+				};
+
+				vpif_ch1: endpoint at 1 {
+					  reg = <1>;
+					  bus-width = <8>;
+					  data-shift = <8>;
+				};
+			};
+		};
 	};
+
 	aemif: aemif at 68000000 {
 		compatible = "ti,da850-aemif";
 		#address-cells = <2>;
-- 
2.9.3

^ permalink raw reply related

* [RFC PATCH 4/6] ARM: dts: davinci: da850-lcdk: enable VPIF capture
From: Kevin Hilman @ 2016-10-25 23:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161025235536.7342-1-khilman@baylibre.com>

Enable video capture via the on-board TVP5147 decoder hooked up to ch0
one of the VPIF capture input.

Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
 arch/arm/boot/dts/da850-lcdk.dts | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/arm/boot/dts/da850-lcdk.dts b/arch/arm/boot/dts/da850-lcdk.dts
index 7b8ab21fed6c..ef3c2aa1b619 100644
--- a/arch/arm/boot/dts/da850-lcdk.dts
+++ b/arch/arm/boot/dts/da850-lcdk.dts
@@ -138,6 +138,24 @@
 		reg = <0x18>;
 		status = "okay";
 	};
+
+	tvp5147 at 5d {
+		compatible = "ti,tvp5147";
+		reg = <0x5d>;
+		status = "okay";
+
+		port {
+			composite: endpoint {
+				hsync-active = <1>;
+				vsync-active = <1>;
+				pclk-sample = <0>;
+
+				/* VPIF channel 0 (lower 8-bits) */
+				remote-endpoint = <&vpif_ch0>;
+				bus-width = <8>;
+			};
+		};		
+	};
 };
 
 &mcasp0 {
@@ -219,3 +237,15 @@
 		};
 	};
 };
+
+&vpif {
+	status = "okay";
+};
+
+&vpif_capture {
+	status = "okay";
+};
+
+&vpif_ch0 {
+	remote-endpoint = <&composite>;
+};
-- 
2.9.3

^ permalink raw reply related

* [RFC PATCH 5/6] [media] davinci: vpif_capture: don't lock over s_stream
From: Kevin Hilman @ 2016-10-25 23:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161025235536.7342-1-khilman@baylibre.com>

Video capture subdevs may be over I2C and may sleep during xfer, so we
cannot do IRQ-disabled locking when calling the subdev.

Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
 drivers/media/platform/davinci/vpif_capture.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 79cef74e164f..becc3e63b472 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -193,12 +193,16 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
 		}
 	}
 
+	spin_unlock_irqrestore(&common->irqlock, flags);
+
 	ret = v4l2_subdev_call(ch->sd, video, s_stream, 1);
 	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
 		vpif_dbg(1, debug, "stream on failed in subdev\n");
 		goto err;
 	}
 
+	spin_lock_irqsave(&common->irqlock, flags);
+
 	/* Call vpif_set_params function to set the parameters and addresses */
 	ret = vpif_set_video_params(vpif, ch->channel_id);
 	if (ret < 0) {
-- 
2.9.3

^ permalink raw reply related

* [RFC PATCH 6/6] [media] davinci: vpif_capture: get subdevs from DT
From: Kevin Hilman @ 2016-10-25 23:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161025235536.7342-1-khilman@baylibre.com>

First pass at getting subdevs from DT ports and endpoints.

The _get_pdata() function was larely inspired by (i.e. stolen from)
am437x-vpfe.c

Questions:
- Legacy board file passes subdev input & output routes via pdata
  (e.g. tvp514x svideo or composite selection.)  How is this supposed
  to be done via DT?

Not-Yet-Signed-off-by: Kevin Hilman <khilman@baylibre.com>
---
 drivers/media/platform/davinci/vpif_capture.c | 132 +++++++++++++++++++++++++-
 include/media/davinci/vpif_types.h            |   9 +-
 2 files changed, 134 insertions(+), 7 deletions(-)

diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index becc3e63b472..df2af5cda37a 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -26,6 +26,8 @@
 #include <linux/slab.h>
 
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-of.h>
+#include <media/i2c/tvp514x.h> /* FIXME: how to pass the INPUT_* OUTPUT* fields? */
 
 #include "vpif.h"
 #include "vpif_capture.h"
@@ -651,6 +653,10 @@ static int vpif_input_to_subdev(
 
 	vpif_dbg(2, debug, "vpif_input_to_subdev\n");
 
+	if (!chan_cfg)
+		return -1;
+	if (input_index >= chan_cfg->input_count)
+		return -1;
 	subdev_name = chan_cfg->inputs[input_index].subdev_name;
 	if (subdev_name == NULL)
 		return -1;
@@ -658,7 +664,7 @@ static int vpif_input_to_subdev(
 	/* loop through the sub device list to get the sub device info */
 	for (i = 0; i < vpif_cfg->subdev_count; i++) {
 		subdev_info = &vpif_cfg->subdev_info[i];
-		if (!strcmp(subdev_info->name, subdev_name))
+		if (subdev_info && !strcmp(subdev_info->name, subdev_name))
 			return i;
 	}
 	return -1;
@@ -1328,13 +1334,25 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier,
 {
 	int i;
 
+	for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) {
+		const struct device_node *node = vpif_obj.config->asd[i]->match.of.node;
+
+		if (node == subdev->of_node) {
+			vpif_obj.sd[i] = subdev;
+			vpif_obj.config->chan_config->inputs[i].subdev_name = subdev->of_node->full_name;
+			vpif_dbg(2, debug, "%s: setting input %d subdev_name = %s\n", __func__,
+				 i, subdev->of_node->full_name);
+			return 0;
+		}
+	}
+
 	for (i = 0; i < vpif_obj.config->subdev_count; i++)
 		if (!strcmp(vpif_obj.config->subdev_info[i].name,
 			    subdev->name)) {
 			vpif_obj.sd[i] = subdev;
 			return 0;
 		}
-
+	
 	return -EINVAL;
 }
 
@@ -1423,6 +1441,113 @@ static int vpif_async_complete(struct v4l2_async_notifier *notifier)
 	return vpif_probe_complete();
 }
 
+static struct vpif_capture_config *
+vpif_capture_get_pdata(struct platform_device *pdev)
+{
+	struct device_node *endpoint = NULL;
+	struct v4l2_of_endpoint bus_cfg;
+	struct vpif_capture_config *pdata;
+	struct vpif_subdev_info *sdinfo;
+	struct vpif_capture_chan_config *chan;
+	unsigned int i;
+
+	dev_dbg(&pdev->dev, "vpif_get_pdata\n");
+
+	if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
+		return pdev->dev.platform_data;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return NULL;
+	pdata->subdev_info = devm_kzalloc(&pdev->dev,
+					  sizeof(*pdata->subdev_info) *
+					  VPIF_CAPTURE_MAX_CHANNELS, GFP_KERNEL);
+	if (!pdata->subdev_info)
+		return NULL;
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	for (i = 0; ; i++) {
+		struct device_node *rem;
+		unsigned int flags;
+		int err;
+		
+		endpoint = of_graph_get_next_endpoint(pdev->dev.of_node,
+						      endpoint);
+		if (!endpoint)
+			break;
+
+		dev_dbg(&pdev->dev, "found endpoint %s, %s\n",
+			endpoint->name, endpoint->full_name);
+
+		sdinfo = &pdata->subdev_info[i];
+		chan = &pdata->chan_config[i];
+		chan->inputs = devm_kzalloc(&pdev->dev,
+					    sizeof(*chan->inputs) *
+					    VPIF_DISPLAY_MAX_CHANNELS,
+					    GFP_KERNEL);
+		
+		/* sdinfo->name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL); */
+		/* snprintf(sdinfo->name, 16, "VPIF input %d", i); */
+		chan->input_count++;
+		chan->inputs[i].input.type = V4L2_INPUT_TYPE_CAMERA;
+		chan->inputs[i].input.std = V4L2_STD_ALL;
+		chan->inputs[i].input.capabilities = V4L2_IN_CAP_STD;
+		
+		/* FIXME: need a new property? ch0:composite ch1: s-video */
+		if (i == 0)
+			chan->inputs[i].input_route = INPUT_CVBS_VI2B;
+		else
+			chan->inputs[i].input_route = INPUT_SVIDEO_VI2C_VI1C;
+		chan->inputs[i].output_route = OUTPUT_10BIT_422_EMBEDDED_SYNC;
+				
+		err = v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+		if (err) {
+			dev_err(&pdev->dev, "Could not parse the endpoint\n");
+			goto done;
+		}
+		dev_dbg(&pdev->dev, "Endpoint %s, bus_width = %d\n",
+			endpoint->full_name, bus_cfg.bus.parallel.bus_width);
+		flags = bus_cfg.bus.parallel.flags;
+
+		if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+			chan->vpif_if.hd_pol = 1;
+
+		if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+			chan->vpif_if.vd_pol = 1;
+
+		chan->vpif_if.if_type = VPIF_IF_BT656;
+		rem = of_graph_get_remote_port_parent(endpoint);
+		if (!rem) {
+			dev_dbg(&pdev->dev, "Remote device at %s not found\n",
+				endpoint->full_name);
+			goto done;
+		}
+
+		dev_dbg(&pdev->dev, "Remote device %s, %s found\n", rem->name, rem->full_name);
+		sdinfo->name = rem->full_name;
+
+		pdata->asd[i] = devm_kzalloc(&pdev->dev,
+					     sizeof(struct v4l2_async_subdev),
+					     GFP_KERNEL);
+		if (!pdata->asd[i]) {
+			of_node_put(rem);
+			pdata = NULL;
+			goto done;
+		}
+
+		pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF;
+		pdata->asd[i]->match.of.node = rem;
+		of_node_put(rem);
+	}
+
+done:
+	pdata->asd_sizes[0] = i;
+	pdata->subdev_count = i;
+	pdata->card_name = "DA850/OMAP-L138 Video Capture";
+
+	return pdata;
+}
+
 /**
  * vpif_probe : This function probes the vpif capture driver
  * @pdev: platform device pointer
@@ -1439,6 +1564,7 @@ static __init int vpif_probe(struct platform_device *pdev)
 	int res_idx = 0;
 	int i, err;
 
+	pdev->dev.platform_data = vpif_capture_get_pdata(pdev);;
 	if (!pdev->dev.platform_data) {
 		dev_warn(&pdev->dev, "Missing platform data.  Giving up.\n");
 		return -EINVAL;
@@ -1481,7 +1607,7 @@ static __init int vpif_probe(struct platform_device *pdev)
 		goto vpif_unregister;
 	}
 
-	if (!vpif_obj.config->asd_sizes) {
+	if (!vpif_obj.config->asd_sizes[0]) {
 		i2c_adap = i2c_get_adapter(1);
 		for (i = 0; i < subdev_count; i++) {
 			subdevdata = &vpif_obj.config->subdev_info[i];
diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h
index 3cb1704a0650..4ee3b41975db 100644
--- a/include/media/davinci/vpif_types.h
+++ b/include/media/davinci/vpif_types.h
@@ -65,14 +65,14 @@ struct vpif_display_config {
 
 struct vpif_input {
 	struct v4l2_input input;
-	const char *subdev_name;
+	char *subdev_name;
 	u32 input_route;
 	u32 output_route;
 };
 
 struct vpif_capture_chan_config {
 	struct vpif_interface vpif_if;
-	const struct vpif_input *inputs;
+	struct vpif_input *inputs;
 	int input_count;
 };
 
@@ -83,7 +83,8 @@ struct vpif_capture_config {
 	struct vpif_subdev_info *subdev_info;
 	int subdev_count;
 	const char *card_name;
-	struct v4l2_async_subdev **asd;	/* Flat array, arranged in groups */
-	int *asd_sizes;		/* 0-terminated array of asd group sizes */
+
+	struct v4l2_async_subdev *asd[VPIF_CAPTURE_MAX_CHANNELS];
+	int asd_sizes[VPIF_CAPTURE_MAX_CHANNELS];
 };
 #endif /* _VPIF_TYPES_H */
-- 
2.9.3

^ permalink raw reply related

* [PATCH v2] modversions: treat symbol CRCs as 32 bit quantities on 64 bit archs
From: Rusty Russell @ 2016-10-26  0:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476956263-1787-1-git-send-email-ard.biesheuvel@linaro.org>

Ard Biesheuvel <ard.biesheuvel@linaro.org> writes:
> The symbol CRCs are emitted as ELF symbols, which allows us to easily
> populate the kcrctab sections by relying on the linker to associate
> each kcrctab slot with the correct value.
>
> This has two downsides:
> - given that the CRCs are treated as pointers, we waste 4 bytes for
>   each CRC on 64 bit architectures,
> - on architectures that support runtime relocation, a relocation entry is
>   emitted for each CRC value, which may take up 24 bytes of __init space
>   (on ELF64 systems)
>
> This comes down to a x8 overhead in [uncompressed] kernel size. In addition,
> each relocation has to be reverted before the CRC value can be used.
>
> Switching to explicit 32 bit values on 64 bit architectures fixes both
> issues, since 32 bit values are not treated as relocatable quantities on
> ELF64 systems, even if the value ultimately resolves to a linker supplied
> value.
>
> So redefine all CRC fields and variables as u32, and redefine the
> __CRC_SYMBOL() macro for 64 bit builds to emit the CRC reference using
> inline assembler (which is necessary since 64-bit C code cannot use
> 32-bit types to hold memory addresses, even if they are ultimately
> resolved using values that do no exceed 0xffffffff).
>
> Also remove the special handling for PPC64, this should no longer be
> required.
>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

This looks good!  Thanks for this, it fixes a nasty wart with the
relocation of crcs.

If the ppc and arm maintainers are happy, I'm happy for Jessica to take
it into her module tree.

Acked-by: Rusty Russell <rusty@rustcorp.com.au>

Thanks,
Rusty.

> ---
> v2: drop the change to struct modversion_info: it affects the layout of the
>     __versions section, which is consumed by userland tools as well, so it is
>     effectively ABI
>
> On an arm64 defconfig build with CONFIG_RELOCATABLE=y, this patch reduces
> the CRC footprint by 24 KB for .rodata, and by 217 KB for .init
>
> Before:
>   [ 9] __kcrctab         PROGBITS         ffff000008b992a8  00b292a8
>        0000000000009440  0000000000000000   A       0     0     8
>   [10] __kcrctab_gpl     PROGBITS         ffff000008ba26e8  00b326e8
>        0000000000008d40  0000000000000000   A       0     0     8
>   ...
>   [22] .rela             RELA             ffff000008c96e20  00c26e20
>        00000000001cc758  0000000000000018   A       0     0     8
>
> After:
>   [ 9] __kcrctab         PROGBITS         ffff000008b728a8  00b028a8
>        0000000000004a20  0000000000000000   A       0     0     1
>   [10] __kcrctab_gpl     PROGBITS         ffff000008b772c8  00b072c8
>        00000000000046a0  0000000000000000   A       0     0     1
>   ...
>   [22] .rela             RELA             ffff000008c66e20  00bf6e20
>        00000000001962d8  0000000000000018   A       0     0     8
>
>  arch/powerpc/include/asm/module.h |  4 --
>  arch/powerpc/kernel/module_64.c   |  8 ----
>  include/linux/export.h            |  8 ++++
>  include/linux/module.h            | 14 +++----
>  kernel/module.c                   | 39 +++++++-------------
>  5 files changed, 29 insertions(+), 44 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
> index cd4ffd86765f..94a7f7aa3ae8 100644
> --- a/arch/powerpc/include/asm/module.h
> +++ b/arch/powerpc/include/asm/module.h
> @@ -94,9 +94,5 @@ struct exception_table_entry;
>  void sort_ex_table(struct exception_table_entry *start,
>  		   struct exception_table_entry *finish);
>  
> -#if defined(CONFIG_MODVERSIONS) && defined(CONFIG_PPC64)
> -#define ARCH_RELOCATES_KCRCTAB
> -#define reloc_start PHYSICAL_START
> -#endif
>  #endif /* __KERNEL__ */
>  #endif	/* _ASM_POWERPC_MODULE_H */
> diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
> index 183368e008cf..be9b2d5ff846 100644
> --- a/arch/powerpc/kernel/module_64.c
> +++ b/arch/powerpc/kernel/module_64.c
> @@ -286,14 +286,6 @@ static void dedotify_versions(struct modversion_info *vers,
>  	for (end = (void *)vers + size; vers < end; vers++)
>  		if (vers->name[0] == '.') {
>  			memmove(vers->name, vers->name+1, strlen(vers->name));
> -#ifdef ARCH_RELOCATES_KCRCTAB
> -			/* The TOC symbol has no CRC computed. To avoid CRC
> -			 * check failing, we must force it to the expected
> -			 * value (see CRC check in module.c).
> -			 */
> -			if (!strcmp(vers->name, "TOC."))
> -				vers->crc = -(unsigned long)reloc_start;
> -#endif
>  		}
>  }
>  
> diff --git a/include/linux/export.h b/include/linux/export.h
> index 2a0f61fbc731..fa51ab2ad190 100644
> --- a/include/linux/export.h
> +++ b/include/linux/export.h
> @@ -41,6 +41,7 @@ extern struct module __this_module;
>  
>  #if defined(__KERNEL__) && !defined(__GENKSYMS__)
>  #ifdef CONFIG_MODVERSIONS
> +#ifndef CONFIG_64BIT
>  /* Mark the CRC weak since genksyms apparently decides not to
>   * generate a checksums for some symbols */
>  #define __CRC_SYMBOL(sym, sec)						\
> @@ -50,6 +51,13 @@ extern struct module __this_module;
>  	__attribute__((section("___kcrctab" sec "+" #sym), used))	\
>  	= (unsigned long) &__crc_##sym;
>  #else
> +#define __CRC_SYMBOL(sym, sec)						\
> +	asm("	.section \"___kcrctab" sec "+" #sym "\", \"a\"	\n"	\
> +	    "	.weak	" VMLINUX_SYMBOL_STR(__crc_##sym) "	\n"	\
> +	    "	.word	" VMLINUX_SYMBOL_STR(__crc_##sym) "	\n"	\
> +	    "	.previous					\n");
> +#endif
> +#else
>  #define __CRC_SYMBOL(sym, sec)
>  #endif
>  
> diff --git a/include/linux/module.h b/include/linux/module.h
> index 0c3207d26ac0..e0067673f5e5 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -346,7 +346,7 @@ struct module {
>  
>  	/* Exported symbols */
>  	const struct kernel_symbol *syms;
> -	const unsigned long *crcs;
> +	const u32 *crcs;
>  	unsigned int num_syms;
>  
>  	/* Kernel parameters. */
> @@ -359,18 +359,18 @@ struct module {
>  	/* GPL-only exported symbols. */
>  	unsigned int num_gpl_syms;
>  	const struct kernel_symbol *gpl_syms;
> -	const unsigned long *gpl_crcs;
> +	const u32 *gpl_crcs;
>  
>  #ifdef CONFIG_UNUSED_SYMBOLS
>  	/* unused exported symbols. */
>  	const struct kernel_symbol *unused_syms;
> -	const unsigned long *unused_crcs;
> +	const u32 *unused_crcs;
>  	unsigned int num_unused_syms;
>  
>  	/* GPL-only, unused exported symbols. */
>  	unsigned int num_unused_gpl_syms;
>  	const struct kernel_symbol *unused_gpl_syms;
> -	const unsigned long *unused_gpl_crcs;
> +	const u32 *unused_gpl_crcs;
>  #endif
>  
>  #ifdef CONFIG_MODULE_SIG
> @@ -382,7 +382,7 @@ struct module {
>  
>  	/* symbols that will be GPL-only in the near future. */
>  	const struct kernel_symbol *gpl_future_syms;
> -	const unsigned long *gpl_future_crcs;
> +	const u32 *gpl_future_crcs;
>  	unsigned int num_gpl_future_syms;
>  
>  	/* Exception table */
> @@ -523,7 +523,7 @@ struct module *find_module(const char *name);
>  
>  struct symsearch {
>  	const struct kernel_symbol *start, *stop;
> -	const unsigned long *crcs;
> +	const u32 *crcs;
>  	enum {
>  		NOT_GPL_ONLY,
>  		GPL_ONLY,
> @@ -539,7 +539,7 @@ struct symsearch {
>   */
>  const struct kernel_symbol *find_symbol(const char *name,
>  					struct module **owner,
> -					const unsigned long **crc,
> +					const u32 **crc,
>  					bool gplok,
>  					bool warn);
>  
> diff --git a/kernel/module.c b/kernel/module.c
> index f57dd63186e6..90ecdad07e1a 100644
> --- a/kernel/module.c
> +++ b/kernel/module.c
> @@ -386,16 +386,16 @@ extern const struct kernel_symbol __start___ksymtab_gpl[];
>  extern const struct kernel_symbol __stop___ksymtab_gpl[];
>  extern const struct kernel_symbol __start___ksymtab_gpl_future[];
>  extern const struct kernel_symbol __stop___ksymtab_gpl_future[];
> -extern const unsigned long __start___kcrctab[];
> -extern const unsigned long __start___kcrctab_gpl[];
> -extern const unsigned long __start___kcrctab_gpl_future[];
> +extern const u32 __start___kcrctab[];
> +extern const u32 __start___kcrctab_gpl[];
> +extern const u32 __start___kcrctab_gpl_future[];
>  #ifdef CONFIG_UNUSED_SYMBOLS
>  extern const struct kernel_symbol __start___ksymtab_unused[];
>  extern const struct kernel_symbol __stop___ksymtab_unused[];
>  extern const struct kernel_symbol __start___ksymtab_unused_gpl[];
>  extern const struct kernel_symbol __stop___ksymtab_unused_gpl[];
> -extern const unsigned long __start___kcrctab_unused[];
> -extern const unsigned long __start___kcrctab_unused_gpl[];
> +extern const u32 __start___kcrctab_unused[];
> +extern const u32 __start___kcrctab_unused_gpl[];
>  #endif
>  
>  #ifndef CONFIG_MODVERSIONS
> @@ -494,7 +494,7 @@ struct find_symbol_arg {
>  
>  	/* Output */
>  	struct module *owner;
> -	const unsigned long *crc;
> +	const u32 *crc;
>  	const struct kernel_symbol *sym;
>  };
>  
> @@ -560,7 +560,7 @@ static bool find_symbol_in_section(const struct symsearch *syms,
>   * (optional) module which owns it.  Needs preempt disabled or module_mutex. */
>  const struct kernel_symbol *find_symbol(const char *name,
>  					struct module **owner,
> -					const unsigned long **crc,
> +					const u32 **crc,
>  					bool gplok,
>  					bool warn)
>  {
> @@ -1257,22 +1257,11 @@ static int try_to_force_load(struct module *mod, const char *reason)
>  }
>  
>  #ifdef CONFIG_MODVERSIONS
> -/* If the arch applies (non-zero) relocations to kernel kcrctab, unapply it. */
> -static unsigned long maybe_relocated(unsigned long crc,
> -				     const struct module *crc_owner)
> -{
> -#ifdef ARCH_RELOCATES_KCRCTAB
> -	if (crc_owner == NULL)
> -		return crc - (unsigned long)reloc_start;
> -#endif
> -	return crc;
> -}
> -
>  static int check_version(Elf_Shdr *sechdrs,
>  			 unsigned int versindex,
>  			 const char *symname,
>  			 struct module *mod,
> -			 const unsigned long *crc,
> +			 const u32 *crc,
>  			 const struct module *crc_owner)
>  {
>  	unsigned int i, num_versions;
> @@ -1294,10 +1283,10 @@ static int check_version(Elf_Shdr *sechdrs,
>  		if (strcmp(versions[i].name, symname) != 0)
>  			continue;
>  
> -		if (versions[i].crc == maybe_relocated(*crc, crc_owner))
> +		if (versions[i].crc == *crc)
>  			return 1;
> -		pr_debug("Found checksum %lX vs module %lX\n",
> -		       maybe_relocated(*crc, crc_owner), versions[i].crc);
> +		pr_debug("Found checksum %X vs module %lX\n",
> +		       *crc, versions[i].crc);
>  		goto bad_version;
>  	}
>  
> @@ -1314,7 +1303,7 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs,
>  					  unsigned int versindex,
>  					  struct module *mod)
>  {
> -	const unsigned long *crc;
> +	const u32 *crc;
>  
>  	/*
>  	 * Since this should be found in kernel (which can't be removed), no
> @@ -1347,7 +1336,7 @@ static inline int check_version(Elf_Shdr *sechdrs,
>  				unsigned int versindex,
>  				const char *symname,
>  				struct module *mod,
> -				const unsigned long *crc,
> +				const u32 *crc,
>  				const struct module *crc_owner)
>  {
>  	return 1;
> @@ -1375,7 +1364,7 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
>  {
>  	struct module *owner;
>  	const struct kernel_symbol *sym;
> -	const unsigned long *crc;
> +	const u32 *crc;
>  	int err;
>  
>  	/*
> -- 
> 2.7.4

^ permalink raw reply

* [PATCH 00/10] arm64: move thread_info off of the task stack
From: Laura Abbott @ 2016-10-26  0:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161024180941.GT15620@leverpostej>

On 10/24/2016 11:09 AM, Mark Rutland wrote:
> On Mon, Oct 24, 2016 at 10:58:10AM -0700, Laura Abbott wrote:
>> On 10/24/2016 10:48 AM, Mark Rutland wrote:
>>> On Mon, Oct 24, 2016 at 10:38:59AM -0700, Laura Abbott wrote:
>>>> On 10/19/2016 12:10 PM, Mark Rutland wrote:
>>>>> [3] https://git.kernel.org/cgit/linux/kernel/git/mark/linux.git/log/?h=arm64/ti-stack-split
>>>
>>>> I pulled the arm64/ti-stack-split branch on top of a Fedora
>>>> tree and ran back-to-back kernel RPM builds for a long weekend.
>>>> It's still going as of this morning so you can take that as a
>>>>
>>>> Tested-by: Laura Abbott <labbott@redhat.com>
>>>
>>> Thanks! That's much appreciated!
>>>
>>> Just to check, did you grab the version with entry.S fixes rolled in
>>> (where the head is 657f54256c427fec)?
>>
>> Ah I did not. That came in after I started the test. I'll start
>> another run with the new version.
>
> Sorry about that; thanks for respinning!
>
> It's really crazy how broken a kernel can be yet still "work"; clearly
> we better tests are needed. :/
>
> Thanks,
> Mark.
>

While not as long running, I gave it another spin and it seemed to
work fine as well.

Thanks,
Laura

^ permalink raw reply

* [RFC PATCH v2 1/1] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
From: Duc Dang @ 2016-10-26  1:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921212258.GE20006@localhost>

PCIe controllers in X-Gene SoCs is not ECAM compliant: software
needs to configure additional controller's register to address
device at bus:dev:function.

This patch depends on "ECAM quirks handling for ARM64 platforms"
series (http://www.spinics.net/lists/arm-kernel/msg530692.html,
the series was also modified by Bjorn) to address the limitation
above for X-Gene PCIe controller.

The quirk will only be applied for X-Gene PCIe MCFG table with
OEM revison 1, 2, 3 or 4 (PCIe controller v1 and v2 on X-Gene SoCs).

Signed-off-by: Duc Dang <dhdang@apm.com>
---
v2 changes:
	1. Get rid of pci-xgene-ecam.c file and fold quirk code into pci-xgene.c
	2. Redefine fixup array for X-Gene
	3. Use devm_ioremap_resource to map csr_base

 drivers/acpi/pci_mcfg.c      |  30 ++++++++
 drivers/pci/host/pci-xgene.c | 165 ++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pci-ecam.h     |   5 ++
 3 files changed, 197 insertions(+), 3 deletions(-)

diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
index bb2c508..9dfc937 100644
--- a/drivers/acpi/pci_mcfg.c
+++ b/drivers/acpi/pci_mcfg.c
@@ -96,6 +96,36 @@ struct mcfg_fixup {
 	THUNDER_ECAM_MCFG(2, 12),
 	THUNDER_ECAM_MCFG(2, 13),
 #endif
+#ifdef CONFIG_PCI_XGENE
+#define XGENE_V1_ECAM_MCFG(rev, seg) \
+	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
+		&xgene_v1_pcie_ecam_ops }
+#define XGENE_V2_1_ECAM_MCFG(rev, seg) \
+	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
+		&xgene_v2_1_pcie_ecam_ops }
+#define XGENE_V2_2_ECAM_MCFG(rev, seg) \
+	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
+		&xgene_v2_2_pcie_ecam_ops }
+
+	/* X-Gene SoC with v1 PCIe controller */
+	XGENE_V1_ECAM_MCFG(1, 0),
+	XGENE_V1_ECAM_MCFG(1, 1),
+	XGENE_V1_ECAM_MCFG(1, 2),
+	XGENE_V1_ECAM_MCFG(1, 3),
+	XGENE_V1_ECAM_MCFG(1, 4),
+	XGENE_V1_ECAM_MCFG(2, 0),
+	XGENE_V1_ECAM_MCFG(2, 1),
+	XGENE_V1_ECAM_MCFG(2, 2),
+	XGENE_V1_ECAM_MCFG(2, 3),
+	XGENE_V1_ECAM_MCFG(2, 4),
+	/* X-Gene SoC with v2.1 PCIe controller */
+	XGENE_V2_1_ECAM_MCFG(3, 0),
+	XGENE_V2_1_ECAM_MCFG(3, 1),
+	/* X-Gene SoC with v2.2 PCIe controller */
+	XGENE_V2_2_ECAM_MCFG(4, 0),
+	XGENE_V2_2_ECAM_MCFG(4, 1),
+	XGENE_V2_2_ECAM_MCFG(4, 2),
+#endif
 };
 
 static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 1de23d7..d6aa642 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -27,6 +27,8 @@
 #include <linux/of_irq.h>
 #include <linux/of_pci.h>
 #include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/pci-ecam.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
@@ -64,6 +66,7 @@
 /* PCIe IP version */
 #define XGENE_PCIE_IP_VER_UNKN		0
 #define XGENE_PCIE_IP_VER_1		1
+#define XGENE_PCIE_IP_VER_2		2
 
 struct xgene_pcie_port {
 	struct device_node	*node;
@@ -97,7 +100,15 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
  */
 static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
 {
-	struct xgene_pcie_port *port = bus->sysdata;
+	struct pci_config_window *cfg;
+	struct xgene_pcie_port *port;
+
+	if (acpi_disabled)
+		port = bus->sysdata;
+	else {
+		cfg = bus->sysdata;
+		port = cfg->priv;
+	}
 
 	if (bus->number >= (bus->primary + 1))
 		return port->cfg_base + AXI_EP_CFG_ACCESS;
@@ -111,10 +122,18 @@ static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
  */
 static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
 {
-	struct xgene_pcie_port *port = bus->sysdata;
+	struct pci_config_window *cfg;
+	struct xgene_pcie_port *port;
 	unsigned int b, d, f;
 	u32 rtdid_val = 0;
 
+	if (acpi_disabled)
+		port = bus->sysdata;
+	else {
+		cfg = bus->sysdata;
+		port = cfg->priv;
+	}
+
 	b = bus->number;
 	d = PCI_SLOT(devfn);
 	f = PCI_FUNC(devfn);
@@ -158,7 +177,15 @@ static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
 static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
 				    int where, int size, u32 *val)
 {
-	struct xgene_pcie_port *port = bus->sysdata;
+	struct pci_config_window *cfg;
+	struct xgene_pcie_port *port;
+
+	if (acpi_disabled)
+		port = bus->sysdata;
+	else {
+		cfg = bus->sysdata;
+		port = cfg->priv;
+	}
 
 	if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
 	    PCIBIOS_SUCCESSFUL)
@@ -189,6 +216,138 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
 	.write = pci_generic_config_write32,
 };
 
+#ifdef CONFIG_ACPI
+static struct resource xgene_v1_csr_res[] = {
+	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
+	[1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
+	[2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
+	[3] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
+	[4] = DEFINE_RES_MEM(0x1f510000UL, SZ_64K),
+};
+
+static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct acpi_device *adev = to_acpi_device(cfg->parent);
+	struct acpi_pci_root *root = acpi_driver_data(adev);
+	struct device *dev = cfg->parent;
+	struct xgene_pcie_port *port;
+	struct resource *csr;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	csr = &xgene_v1_csr_res[root->segment];
+	port->csr_base = devm_ioremap_resource(dev, csr);
+	if (IS_ERR(port->csr_base)) {
+		kfree(port);
+		return -ENOMEM;
+	}
+
+	port->cfg_base = cfg->win;
+	port->version = XGENE_PCIE_IP_VER_1;
+
+	cfg->priv = port;
+
+	return 0;
+}
+
+struct pci_ecam_ops xgene_v1_pcie_ecam_ops = {
+	.bus_shift      = 16,
+	.init           = xgene_v1_pcie_ecam_init,
+	.pci_ops        = {
+		.map_bus        = xgene_pcie_map_bus,
+		.read           = xgene_pcie_config_read32,
+		.write          = pci_generic_config_write,
+	}
+};
+
+static struct resource xgene_v2_1_csr_res[] = {
+	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
+	[1] = DEFINE_RES_MEM(0x1f2c0000UL, SZ_64K),
+};
+
+static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct acpi_device *adev = to_acpi_device(cfg->parent);
+	struct acpi_pci_root *root = acpi_driver_data(adev);
+	struct device *dev = cfg->parent;
+	struct xgene_pcie_port *port;
+	struct resource *csr;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	csr = &xgene_v2_1_csr_res[root->segment];
+	port->csr_base = devm_ioremap_resource(dev, csr);
+	if (IS_ERR(port->csr_base)) {
+		kfree(port);
+		return -ENOMEM;
+	}
+
+	port->cfg_base = cfg->win;
+	port->version = XGENE_PCIE_IP_VER_2;
+
+	cfg->priv = port;
+
+	return 0;
+}
+
+struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = {
+	.bus_shift      = 16,
+	.init           = xgene_v2_1_pcie_ecam_init,
+	.pci_ops        = {
+		.map_bus        = xgene_pcie_map_bus,
+		.read           = xgene_pcie_config_read32,
+		.write          = pci_generic_config_write,
+	}
+};
+
+static struct resource xgene_v2_2_csr_res[] = {
+	[0] = DEFINE_RES_MEM(0x1f2b0000UL, SZ_64K),
+	[1] = DEFINE_RES_MEM(0x1f500000UL, SZ_64K),
+	[2] = DEFINE_RES_MEM(0x1f2d0000UL, SZ_64K),
+};
+
+static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg)
+{
+	struct acpi_device *adev = to_acpi_device(cfg->parent);
+	struct acpi_pci_root *root = acpi_driver_data(adev);
+	struct device *dev = cfg->parent;
+	struct xgene_pcie_port *port;
+	struct resource *csr;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	csr = &xgene_v2_2_csr_res[root->segment];
+	port->csr_base = devm_ioremap_resource(dev, csr);
+	if (IS_ERR(port->csr_base)) {
+		kfree(port);
+		return -ENOMEM;
+	}
+
+	port->cfg_base = cfg->win;
+	port->version = XGENE_PCIE_IP_VER_2;
+
+	cfg->priv = port;
+
+	return 0;
+}
+
+struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = {
+	.bus_shift      = 16,
+	.init           = xgene_v2_2_pcie_ecam_init,
+	.pci_ops        = {
+		.map_bus        = xgene_pcie_map_bus,
+		.read           = xgene_pcie_config_read32,
+		.write          = pci_generic_config_write,
+	}
+};
+#endif
+
 static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
 				  u32 flags, u64 size)
 {
diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
index 35f0e81..40da3e7 100644
--- a/include/linux/pci-ecam.h
+++ b/include/linux/pci-ecam.h
@@ -65,6 +65,11 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
 #ifdef CONFIG_PCI_HOST_THUNDER_ECAM
 extern struct pci_ecam_ops pci_thunder_ecam_ops;
 #endif
+#ifdef CONFIG_PCI_XGENE
+extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops;
+extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops;
+extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops;
+#endif
 
 #ifdef CONFIG_PCI_HOST_GENERIC
 /* for DT-based PCI controllers that support ECAM */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v8 0/8] power: add power sequence library
From: Peter Chen @ 2016-10-26  1:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5513671.XlE8bb9pVt@vostro.rjw.lan>

On Tue, Oct 18, 2016 at 02:35:38AM +0200, Rafael J. Wysocki wrote:
> On Monday, October 17, 2016 09:30:59 AM Peter Chen wrote:
> > On Fri, Oct 14, 2016 at 02:09:31PM +0200, Rafael J. Wysocki wrote:
> > > On Friday, October 14, 2016 10:59:47 AM Peter Chen wrote:
> > > > Hi all,
> > > > 
> > > > This is a follow-up for my last power sequence framework patch set [1].
> > > > According to Rob Herring and Ulf Hansson's comments[2]. The kinds of
> > > > power sequence instances will be added at postcore_initcall, the match
> > > > criteria is compatible string first, if the compatible string is not
> > > > matched between dts and library, it will try to use generic power sequence.
> > > > 	 
> > > > The host driver just needs to call of_pwrseq_on/of_pwrseq_off
> > > > if only one power sequence instance is needed, for more power sequences
> > > > are used, using of_pwrseq_on_list/of_pwrseq_off_list instead (eg, USB hub driver).
> > > > 
> > > > In future, if there are special power sequence requirements, the special
> > > > power sequence library can be created.
> > > > 
> > > > This patch set is tested on i.mx6 sabresx evk using a dts change, I use
> > > > two hot-plug devices to simulate this use case, the related binding
> > > > change is updated at patch [1/6], The udoo board changes were tested
> > > > using my last power sequence patch set.[3]
> > > > 
> > > > Except for hard-wired MMC and USB devices, I find the USB ULPI PHY also
> > > > need to power on itself before it can be found by ULPI bus.
> > > > 
> > > > [1] http://www.spinics.net/lists/linux-usb/msg142755.html
> > > > [2] http://www.spinics.net/lists/linux-usb/msg143106.html
> > > > [3] http://www.spinics.net/lists/linux-usb/msg142815.html
> > > > 
> > > > Changes for v8:
> > > > - Allocate one extra pwrseq instance if pwrseq_get has succeed, it can avoid
> > > >   preallocate instances problem which the number of instance is decided at
> > > >   compile time, thanks for Heiko Stuebner's suggestion [Patch 2/8]
> > > > - Delete pwrseq_compatible_sample.c which is the demo purpose to show compatible
> > > >   match method. [Patch 2/8]
> > > > - Add Maciej S. Szmigiero's tested-by. [Patch 7/8]
> > > > 
> > > > Changes for v7:
> > > > - Create kinds of power sequence instance at postcore_initcall, and match
> > > >   the instance with node using compatible string, the beneit of this is
> > > >   the host driver doesn't need to consider which pwrseq instance needs
> > > >   to be used, and pwrseq core will match it, however, it eats some memories
> > > >   if less power sequence instances are used. [Patch 2/8]
> > > > - Add pwrseq_compatible_sample.c to test match pwrseq using device_id. [Patch 2/8]
> > > > - Fix the comments Vaibhav Hiremath adds for error path for clock and do not
> > > >   use device_node for parameters at pwrseq_on. [Patch 2/8]
> > > > - Simplify the caller to use power sequence, follows Alan's commnets [Patch 4/8]
> > > > - Tested three pwrseq instances together using both specific compatible string and
> > > >   generic libraries.
> > > > 
> > > > Changes for v6:
> > > > - Add Matthias Kaehlcke's Reviewed-by and Tested-by. (patch [2/6])
> > > > - Change chipidea core of_node assignment for coming user. (patch [5/6])
> > > > - Applies Joshua Clayton's three dts changes for two boards,
> > > >   the USB device's reg has only #address-cells, but without #size-cells.
> > > > 
> > > > Changes for v5:
> > > > - Delete pwrseq_register/pwrseq_unregister, which is useless currently
> > > > - Fix the linker error when the pwrseq user is compiled as module
> > > > 
> > > > Changes for v4:
> > > > - Create the patch on next-20160722 
> > > > - Fix the of_node is not NULL after chipidea driver is unbinded [Patch 5/6]
> > > > - Using more friendly wait method for reset gpio [Patch 2/6]
> > > > - Support multiple input clocks [Patch 2/6]
> > > > - Add Rob Herring's ack for DT changes
> > > > - Add Joshua Clayton's Tested-by
> > > > 
> > > > Changes for v3:
> > > > - Delete "power-sequence" property at binding-doc, and change related code
> > > >   at both library and user code.
> > > > - Change binding-doc example node name with Rob's comments
> > > > - of_get_named_gpio_flags only gets the gpio, but without setting gpio flags,
> > > >   add additional code request gpio with proper gpio flags
> > > > - Add Philipp Zabel's Ack and MAINTAINER's entry
> > > > 
> > > > Changes for v2:
> > > > - Delete "pwrseq" prefix and clock-names for properties at dt binding
> > > > - Should use structure not but its pointer for kzalloc
> > > > - Since chipidea core has no of_node, let core's of_node equals glue
> > > >   layer's at core's probe
> > > > 
> > > > Joshua Clayton (2):
> > > >   ARM: dts: imx6qdl: Enable usb node children with <reg>
> > > >   ARM: dts: imx6q-evi: Fix onboard hub reset line
> > > > 
> > > > Peter Chen (6):
> > > >   binding-doc: power: pwrseq-generic: add binding doc for generic power
> > > >     sequence library
> > > >   power: add power sequence library
> > > >   binding-doc: usb: usb-device: add optional properties for power
> > > >     sequence
> > > >   usb: core: add power sequence handling for USB devices
> > > >   usb: chipidea: let chipidea core device of_node equal's glue layer
> > > >     device of_node
> > > >   ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property
> > > > 
> > > >  .../bindings/power/pwrseq/pwrseq-generic.txt       |  48 ++++++
> > > >  .../devicetree/bindings/usb/usb-device.txt         |  10 +-
> > > >  MAINTAINERS                                        |   9 +
> > > >  arch/arm/boot/dts/imx6q-evi.dts                    |  25 +--
> > > >  arch/arm/boot/dts/imx6qdl-udoo.dtsi                |  26 ++-
> > > >  arch/arm/boot/dts/imx6qdl.dtsi                     |   6 +
> > > >  drivers/power/Kconfig                              |   1 +
> > > >  drivers/power/Makefile                             |   1 +
> > > >  drivers/power/pwrseq/Kconfig                       |  19 ++
> > > >  drivers/power/pwrseq/Makefile                      |   2 +
> > > >  drivers/power/pwrseq/core.c                        | 191 +++++++++++++++++++++
> > > >  drivers/power/pwrseq/pwrseq_generic.c              | 183 ++++++++++++++++++++
> > > >  drivers/usb/chipidea/core.c                        |  27 ++-
> > > >  drivers/usb/core/hub.c                             |  41 ++++-
> > > >  drivers/usb/core/hub.h                             |   1 +
> > > >  include/linux/power/pwrseq.h                       |  72 ++++++++
> > > >  16 files changed, 621 insertions(+), 41 deletions(-)
> > > >  create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
> > > >  create mode 100644 drivers/power/pwrseq/Kconfig
> > > >  create mode 100644 drivers/power/pwrseq/Makefile
> > > >  create mode 100644 drivers/power/pwrseq/core.c
> > > >  create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
> > > >  create mode 100644 include/linux/power/pwrseq.h
> > > 
> > > Meta question: Who's the maintainer you are targetting this at?
> > > 
> > 
> > Sebastian Reichel mentioned it is better through your tree.
> > I could be the maintainer for it, and send "GIT PULL" for you
> > through my git
> > (https://git.kernel.org/cgit/linux/kernel/git/peter.chen/usb.git/)
> > Is it ok for you?
> 
> Let me review the series first. :-)
> 

A nice ping..., thanks.

Just see someone is also waiting this series.
http://www.spinics.net/lists/devicetree/msg147655.html

-- 

Best Regards,
Peter Chen

^ permalink raw reply

* [RFC PATCH] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe controller
From: Duc Dang @ 2016-10-26  1:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921212258.GE20006@localhost>

On Wed, Sep 21, 2016 at 2:22 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
> On Mon, Sep 19, 2016 at 06:07:37PM -0700, Duc Dang wrote:
>> On Mon, Sep 19, 2016 at 1:06 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
>> > On Sat, Sep 17, 2016 at 07:24:38AM -0700, Duc Dang wrote:
>
>> This patch only adds the ability for X-Gene PCIe controller to be
>> probable in ACPI boot mode. All the 'quirk' code added is for ECAM to
>> work with X-Gene.
>>
>> > I sort of expected this to also remove, for example, the seemingly
>> > identical xgene_pcie_config_read32() in drivers/pci/host/pci-xgene.c.
>> > Actually, a bunch of this code seems to be duplicated from there.  It
>> > doesn't seem like we should end up with all this duplicated code.
>> >
>> > I'd really like it better if all this could get folded into
>> > pci-xgene.c so we don't end up with more files.
>>
>> I am still debating whether to put this X-Gene ECAM quirk code into
>> drivers/acpi or keep it here in drivers/pci/host. But given the
>> direction of other quirk patches for ThunderX and HiSi, seem like the
>> quirk will stay in drivers/pci/host. I can definitely fold the new
>> quirk code into pci-xgene.c as you suggested and eliminate the
>> identical one.
>
> I like Tomasz's patches, where the MCFG quirk itself is in
> acpi/pci_mcfg.c, and it uses config accessors exported from
> drivers/pci/host.

Yes, I removed the new file and folded the quirk code into pci-xgene.c.

>
> I do not want to end up with duplicate accessors.  The mapping
> functions and accessors should be the same whether we're booting with
> DT or ACPI.
>
> I think a patch to add ACPI support should only contain:
>
>   - acpi/pci_mcfg.c quirks to fix incorrect ACPI MCFG resources or use
>     special accessors,
>
>   - pnp/quirks.c quirks to compensate for missing ACPI _CRS for the
>     ECAM regions, and
>
>   - pci-xgene.c code to derive the csr_base and cfg_base.  Today we
>     get that from DT, but the _CRS producer/consumer mess means we
>     don't have a good way to get it from ACPI, so you'll need some
>     sort of quirk for this.

The new quirk code (v2 patch) follows this direction, but I have not
found a good way to introduce quirk into pnp/quirks.c yet. Just to
clarify a little bit, our ACPI table provides ECAM base address from
_CBA method. The missing piece is the controller register base address
(csr_base) that I need to get from a hard-coded resource array.

I also owe you the rework for Configuration Request Retry Status
workaround, but it will need to be done in a separate patch set for
both DT and ACPI.
>
>> >> +struct xgene_pcie_acpi_root {
>> >> +     void __iomem *csr_base;
>> >> +     u32 version;
>> >> +};
>> >
>> > I think this should be folded into struct xgene_pcie_port so we don't
>> > have to allocate and manage it separately.
>>
>> I will need to look into this more. When booting with ACPI mode, the
>> code in pci-xgene.c is not used (except the cfg read/write functions
>> that are shared with ECAM quirk code), so puting these into
>> xgene_pcie_port will force ECAM quirk code to allocate this structure
>> as well.
>
> This information is needed whether booting with DT or ACPI, so we
> should use the existing xgene_pcie_port.csr_base and initialize it
> differently depending on which we're using.

The new ECAM quirk code will also allocate struct xgene_pcie_port, I
got rid of xgene_pcie_acpi_root struct.

>
>> >> +     default:
>> >> +             return -ENODEV;
>> >> +     }
>> >> +
>> >> +     xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH);
>> >
>> > There should be a request_region() somewhere, too.  Ideal would be to
>> > use devm_ioremap_resource(), but I don't know where this apparent
>> > resource is coming from.
>>
>> Yes, I will use request_region/devm_ioremap_resource here.
>
> We're not *adding* any new resources that need ioremapping; all we're
> doing is changing the *source* of the resource, so we should use the
> same devm_ioremap_resource() you already have in xgene_pcie_map_reg().
> You might have to refactor that slightly so we can lookup the resource
> via either DT or ACPI (you'll probably actually use a quirk since ACPI
> doesn't have a good mechanism for this), and then use the same call to
> devm_ioremap_resource().

I changed to use devm_ioremap_resource to map the csr_base from the
fixed resource array defined for each X-Gene SoC. The 'cat
/proc/iomem' for PCIe port on Mustang board is like following:

1f2b0000-1f2bffff : PNP0A08:00
e040000000-e07fffffff : PCI Bus 0000:00
  e040000000-e0401fffff : PCI Bus 0000:01
    e040000000-e0400fffff : 0000:01:00.0
      e040000000-e0400fffff : mlx4_core
    e040100000-e0401fffff : 0000:01:00.0
e0d0000000-e0dfffffff : PCI ECAM
f000000000-ffffffffff : PCI Bus 0000:00
  f000000000-f001ffffff : PCI Bus 0000:01
    f000000000-f001ffffff : 0000:01:00.0
      f000000000-f001ffffff : mlx4_core

Is this what you expect? Or you are looking for something else?

Regards,
Duc Dabng.

^ permalink raw reply

* [PATCH v2] staging: vc04_services: Replace dmac_map_area with dmac_map_sg
From: Michael Zoran @ 2016-10-26  2:23 UTC (permalink / raw)
  To: linux-arm-kernel

The original arm implementation uses dmac_map_area which is not
portable.  Replace it with an architecture neutral version
which uses dma_map_sg.

As you can see that for larger page sizes, the dma_map_sg
implementation is faster then the original unportable dma_map_area
implementation.

Test                       dmac_map_area   dma_map_page dma_map_sg
vchiq_test -b 4 10000      51us/iter       76us/iter    76us
vchiq_test -b 8 10000      70us/iter       82us/iter    91us
vchiq_test -b 16 10000     94us/iter       118us/iter   121us
vchiq_test -b 32 10000     146us/iter      173us/iter   187us
vchiq_test -b 64 10000     263us/iter      328us/iter   299us
vchiq_test -b 128 10000    529us/iter      631us/iter   595us
vchiq_test -b 256 10000    2285us/iter     2275us/iter  2001us
vchiq_test -b 512 10000    4372us/iter     4616us/iter  4123us

For message sizes >= 64KB, dma_map_sg is faster then dma_map_page.

For message size >= 256KB, the dma_map_sg is the fastest
implementation.

"Normal" messages sizes should be about 1MB which is beyond
the length that this change shows a speed increase.

This is v2 of the patch which includes extra WARN_ONs and
incorporates feedback from Eric Anholt <eric@anholt.net>.

Signed-off-by: Michael Zoran <mzoran@crowfest.net>
---
 .../interface/vchiq_arm/vchiq_2835_arm.c           | 152 +++++++++++++--------
 1 file changed, 93 insertions(+), 59 deletions(-)

diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index 32d12e6..a5afcc5 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -45,13 +45,8 @@
 #include <asm/pgtable.h>
 #include <soc/bcm2835/raspberrypi-firmware.h>
 
-#define dmac_map_area			__glue(_CACHE,_dma_map_area)
-#define dmac_unmap_area 		__glue(_CACHE,_dma_unmap_area)
-
 #define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32)
 
-#define VCHIQ_ARM_ADDRESS(x) ((void *)((char *)x + g_virt_to_bus_offset))
-
 #include "vchiq_arm.h"
 #include "vchiq_2835.h"
 #include "vchiq_connected.h"
@@ -73,7 +68,7 @@ static unsigned int g_fragments_size;
 static char *g_fragments_base;
 static char *g_free_fragments;
 static struct semaphore g_free_fragments_sema;
-static unsigned long g_virt_to_bus_offset;
+static struct device *g_dev;
 
 extern int vchiq_arm_log_level;
 
@@ -84,10 +79,11 @@ vchiq_doorbell_irq(int irq, void *dev_id);
 
 static int
 create_pagelist(char __user *buf, size_t count, unsigned short type,
-                struct task_struct *task, PAGELIST_T ** ppagelist);
+		struct task_struct *task, PAGELIST_T **ppagelist,
+		dma_addr_t *dma_addr);
 
 static void
-free_pagelist(PAGELIST_T *pagelist, int actual);
+free_pagelist(dma_addr_t dma_addr, PAGELIST_T *pagelist, int actual);
 
 int vchiq_platform_init(struct platform_device *pdev, VCHIQ_STATE_T *state)
 {
@@ -101,8 +97,6 @@ int vchiq_platform_init(struct platform_device *pdev, VCHIQ_STATE_T *state)
 	int slot_mem_size, frag_mem_size;
 	int err, irq, i;
 
-	g_virt_to_bus_offset = virt_to_dma(dev, (void *)0);
-
 	(void)of_property_read_u32(dev->of_node, "cache-line-size",
 				   &g_cache_line_size);
 	g_fragments_size = 2 * g_cache_line_size;
@@ -118,7 +112,7 @@ int vchiq_platform_init(struct platform_device *pdev, VCHIQ_STATE_T *state)
 		return -ENOMEM;
 	}
 
-	WARN_ON(((int)slot_mem & (PAGE_SIZE - 1)) != 0);
+	WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0);
 
 	vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size);
 	if (!vchiq_slot_zero)
@@ -170,6 +164,7 @@ int vchiq_platform_init(struct platform_device *pdev, VCHIQ_STATE_T *state)
 		return err ? : -ENXIO;
 	}
 
+	g_dev = dev;
 	vchiq_log_info(vchiq_arm_log_level,
 		"vchiq_init - done (slots %pK, phys %pad)",
 		vchiq_slot_zero, &slot_phys);
@@ -233,6 +228,7 @@ vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle,
 {
 	PAGELIST_T *pagelist;
 	int ret;
+	dma_addr_t dma_addr;
 
 	WARN_ON(memhandle != VCHI_MEM_HANDLE_INVALID);
 
@@ -241,12 +237,14 @@ vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle,
 			? PAGELIST_READ
 			: PAGELIST_WRITE,
 			current,
-			&pagelist);
+			&pagelist,
+			&dma_addr);
+
 	if (ret != 0)
 		return VCHIQ_ERROR;
 
 	bulk->handle = memhandle;
-	bulk->data = VCHIQ_ARM_ADDRESS(pagelist);
+	bulk->data = (void *)(unsigned long)dma_addr;
 
 	/* Store the pagelist address in remote_data, which isn't used by the
 	   slave. */
@@ -259,7 +257,8 @@ void
 vchiq_complete_bulk(VCHIQ_BULK_T *bulk)
 {
 	if (bulk && bulk->remote_data && bulk->actual)
-		free_pagelist((PAGELIST_T *)bulk->remote_data, bulk->actual);
+		free_pagelist((dma_addr_t)(unsigned long)bulk->data,
+			      (PAGELIST_T *)bulk->remote_data, bulk->actual);
 }
 
 void
@@ -353,38 +352,44 @@ vchiq_doorbell_irq(int irq, void *dev_id)
 ** obscuring the new data underneath. We can solve this by transferring the
 ** partial cache lines separately, and allowing the ARM to copy into the
 ** cached area.
-
-** N.B. This implementation plays slightly fast and loose with the Linux
-** driver programming rules, e.g. its use of dmac_map_area instead of
-** dma_map_single, but it isn't a multi-platform driver and it benefits
-** from increased speed as a result.
 */
 
 static int
 create_pagelist(char __user *buf, size_t count, unsigned short type,
-	struct task_struct *task, PAGELIST_T ** ppagelist)
+		struct task_struct *task, PAGELIST_T **ppagelist,
+		dma_addr_t *dma_addr)
 {
 	PAGELIST_T *pagelist;
 	struct page **pages;
-	unsigned long *addrs;
-	unsigned int num_pages, offset, i;
-	char *addr, *base_addr, *next_addr;
-	int run, addridx, actual_pages;
+	u32 *addrs;
+	unsigned int num_pages, offset, i, k;
+	int actual_pages;
         unsigned long *need_release;
+	size_t pagelist_size;
+	struct scatterlist *scatterlist, *sg;
+	int dma_buffers;
+	int dir;
 
-	offset = (unsigned int)buf & (PAGE_SIZE - 1);
+	offset = ((unsigned int)(unsigned long)buf & (PAGE_SIZE - 1));
 	num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE;
 
+	pagelist_size = sizeof(PAGELIST_T) +
+			(num_pages * sizeof(unsigned long)) +
+			sizeof(unsigned long) +
+			(num_pages * sizeof(pages[0]) +
+			(num_pages * sizeof(struct scatterlist)));
+
 	*ppagelist = NULL;
 
+	dir = (type == PAGELIST_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
 	/* Allocate enough storage to hold the page pointers and the page
 	** list
 	*/
-	pagelist = kmalloc(sizeof(PAGELIST_T) +
-                           (num_pages * sizeof(unsigned long)) +
-                           sizeof(unsigned long) +
-                           (num_pages * sizeof(pages[0])),
-                           GFP_KERNEL);
+	pagelist = dma_zalloc_coherent(g_dev,
+				       pagelist_size,
+				       dma_addr,
+				       GFP_KERNEL);
 
 	vchiq_log_trace(vchiq_arm_log_level, "create_pagelist - %pK",
 			pagelist);
@@ -394,10 +399,9 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
 	addrs = pagelist->addrs;
         need_release = (unsigned long *)(addrs + num_pages);
 	pages = (struct page **)(addrs + num_pages + 1);
+	scatterlist = (struct scatterlist *)(pages + num_pages);
 
 	if (is_vmalloc_addr(buf)) {
-		int dir = (type == PAGELIST_WRITE) ?
-			DMA_TO_DEVICE : DMA_FROM_DEVICE;
 		unsigned long length = count;
 		unsigned int off = offset;
 
@@ -410,7 +414,6 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
 			if (bytes > length)
 				bytes = length;
 			pages[actual_pages] = pg;
-			dmac_map_area(page_address(pg) + off, bytes, dir);
 			length -= bytes;
 			off = 0;
 		}
@@ -438,7 +441,8 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
 				actual_pages--;
 				put_page(pages[actual_pages]);
 			}
-			kfree(pagelist);
+			dma_free_coherent(g_dev, pagelist_size,
+					  pagelist, *dma_addr);
 			if (actual_pages == 0)
 				actual_pages = -ENOMEM;
 			return actual_pages;
@@ -450,30 +454,44 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
 	pagelist->type = type;
 	pagelist->offset = offset;
 
-	/* Group the pages into runs of contiguous pages */
+	for (i = 0; i < num_pages; i++)
+		sg_set_page(scatterlist + i, pages[i], PAGE_SIZE, 0);
+
+	dma_buffers = dma_map_sg(g_dev,
+				 scatterlist,
+				 num_pages,
+				 dir);
 
-	base_addr = VCHIQ_ARM_ADDRESS(page_address(pages[0]));
-	next_addr = base_addr + PAGE_SIZE;
-	addridx = 0;
-	run = 0;
+	if (dma_buffers == 0) {
+		dma_free_coherent(g_dev, pagelist_size,
+				  pagelist, *dma_addr);
 
-	for (i = 1; i < num_pages; i++) {
-		addr = VCHIQ_ARM_ADDRESS(page_address(pages[i]));
-		if ((addr == next_addr) && (run < (PAGE_SIZE - 1))) {
-			next_addr += PAGE_SIZE;
-			run++;
+		return -EINTR;
+	}
+
+	/* Combine adjacent blocks for performance */
+	k = 0;
+	for_each_sg(scatterlist, sg, dma_buffers, i) {
+		u32 len = sg_dma_len(sg);
+		u32 addr = sg_dma_address(sg);
+
+		/* Note: addrs is the address + page_count - 1
+		 * The firmware expects the block to be page
+		 * aligned and a multiple of the page size
+		 */
+		WARN_ON(len == 0);
+		WARN_ON(len & ~PAGE_MASK);
+		WARN_ON(addr & ~PAGE_MASK);
+		if (k > 0 &&
+		    ((addrs[k - 1] & PAGE_MASK) |
+			((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT)
+		    == addr) {
+			addrs[k - 1] += (len >> PAGE_SHIFT);
 		} else {
-			addrs[addridx] = (unsigned long)base_addr + run;
-			addridx++;
-			base_addr = addr;
-			next_addr = addr + PAGE_SIZE;
-			run = 0;
+			addrs[k++] = addr | ((len >> PAGE_SHIFT) - 1);
 		}
 	}
 
-	addrs[addridx] = (unsigned long)base_addr + run;
-	addridx++;
-
 	/* Partial cache lines (fragments) require special measures */
 	if ((type == PAGELIST_READ) &&
 		((pagelist->offset & (g_cache_line_size - 1)) ||
@@ -482,7 +500,10 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
 		char *fragments;
 
 		if (down_interruptible(&g_free_fragments_sema) != 0) {
-			kfree(pagelist);
+			dma_unmap_sg(g_dev, scatterlist, num_pages,
+				     DMA_BIDIRECTIONAL);
+			dma_free_coherent(g_dev, pagelist_size,
+					  pagelist, *dma_addr);
 			return -EINTR;
 		}
 
@@ -497,29 +518,42 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
 			(fragments - g_fragments_base) / g_fragments_size;
 	}
 
-	dmac_flush_range(pagelist, addrs + num_pages);
-
 	*ppagelist = pagelist;
 
 	return 0;
 }
 
 static void
-free_pagelist(PAGELIST_T *pagelist, int actual)
+free_pagelist(dma_addr_t dma_addr, PAGELIST_T *pagelist, int actual)
 {
         unsigned long *need_release;
 	struct page **pages;
 	unsigned int num_pages, i;
+	size_t pagelist_size;
+	struct scatterlist *scatterlist;
+	int dir;
 
 	vchiq_log_trace(vchiq_arm_log_level, "free_pagelist - %pK, %d",
 			pagelist, actual);
 
+	dir = (pagelist->type == PAGELIST_WRITE) ? DMA_TO_DEVICE :
+						   DMA_FROM_DEVICE;
+
 	num_pages =
 		(pagelist->length + pagelist->offset + PAGE_SIZE - 1) /
 		PAGE_SIZE;
 
+	pagelist_size = sizeof(PAGELIST_T) +
+			(num_pages * sizeof(unsigned long)) +
+			sizeof(unsigned long) +
+			(num_pages * sizeof(pages[0]) +
+			(num_pages * sizeof(struct scatterlist)));
+
         need_release = (unsigned long *)(pagelist->addrs + num_pages);
 	pages = (struct page **)(pagelist->addrs + num_pages + 1);
+	scatterlist = (struct scatterlist *)(pages + num_pages);
+
+	dma_unmap_sg(g_dev, scatterlist, num_pages, dir);
 
 	/* Deal with any partial cache lines (fragments) */
 	if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) {
@@ -569,8 +603,7 @@ free_pagelist(PAGELIST_T *pagelist, int actual)
 
 				if (bytes > length)
 					bytes = length;
-				dmac_unmap_area(page_address(pg) + offset,
-						bytes, DMA_FROM_DEVICE);
+
 				length -= bytes;
 				offset = 0;
 				set_page_dirty(pg);
@@ -579,5 +612,6 @@ free_pagelist(PAGELIST_T *pagelist, int actual)
 		}
 	}
 
-	kfree(pagelist);
+	dma_free_coherent(g_dev, pagelist_size,
+			  pagelist, dma_addr);
 }
-- 
2.10.1

^ permalink raw reply related

* [PATCH] ARM: imx6: Fix GPC probe error path
From: Guenter Roeck @ 2016-10-26  2:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477416878-30353-1-git-send-email-linux@roeck-us.net>

On 10/25/2016 10:34 AM, Guenter Roeck wrote:
> GPC may fail to instantiate with
>
> imx-gpc: probe of 20dc000.gpc failed with error -22
>
> which is returned from of_genpd_add_provider_onecell(). The error path
> does not call pm_genpd_remove(). This results in the following crash
> later on.
>
> Unhandled fault: page domain fault (0x01b) at 0x00000040
> pgd = c0204000
> [00000040] *pgd=00000000
> Internal error: : 1b [#1] SMP ARM
> Modules linked in:
> CPU: 0 PID: 108 Comm: kworker/0:3 Not tainted 4.9.0-rc2 #8
> Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
> Workqueue: pm genpd_power_off_work_fn
> task: c759ea00 task.stack: c766a000
> PC is at mutex_lock+0xc/0x4c
> LR is at regulator_disable+0x28/0x64
> ...
> [<c0bc2694>] (mutex_lock) from [<c06e4e8c>] (regulator_disable+0x28/0x64)
> [<c06e4e8c>] (regulator_disable) from [<c0323b68>] (imx6q_pm_pu_power_off+0x90/0x98)
> [<c0323b68>] (imx6q_pm_pu_power_off) from [<c07efb04>] (genpd_poweroff+0x114/0x1d4)
> [<c07efb04>] (genpd_poweroff) from [<c07efdc0>] (genpd_power_off_work_fn+0x20/0x2c)
> [<c07efdc0>] (genpd_power_off_work_fn) from [<c0358f70>] (process_one_work+0x138/0x34c)
> [<c0358f70>] (process_one_work) from [<c03591bc>] (worker_thread+0x38/0x510)
> [<c03591bc>] (worker_thread) from [<c035e48c>] (kthread+0xdc/0xf4)
> [<c035e48c>] (kthread) from [<c0307eb8>] (ret_from_fork+0x14/0x3c)
>
> This is seen with multi_v7_defconfig and imx6dl-sabrelite.dtb running in
> qemu (v2.7 patched to fix a qemu related problem). The error return from
> of_genpd_add_provider_onecell() is not seen in v4.8 and may be caused by
> a devicetree change (this is a wild guess only), but that is a different
> problem.
>
> Fixes: 00eb60a8b4f7 ("ARM: imx6: gpc: Add PU power domain for GPU/VPU")
> Cc: Philipp Zabel <p.zabel@pengutronix.de>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> ---
> Several bisect attempts trying to track down "imx-gpc: probe ... failed
> with error -22" point to commit 00e729c93395 ("Merge tag 'armsoc-dt' of
> git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc"). I have not
> been able to track down the real culprit. Part of the problem is that
> CONFIG_REGULATOR_ANATOP must be enabled for the problem to be seen, and
> CONFIG_ARCH_AT91 causes compile errors for some sequence of commits between
> v4.8 and v4.9-rc1. But even after taking this into account, the bisect
> results always point to 00e729c93395. If anyone has an idea how to track
> down that problem, or what might be causing it, please let me know.
>

Looking into this some more, it turns out that of_genpd_add_provider_onecell()
now returns an error if one of the provided power domains does not exist.
In this case, the "ARM" power domain does not exist. I don't see where it is
created, so it may well be that this now fails for all imx6 boards with
multi_v7_defconfig. Looking into kernelci.org test results, this is confirmed
for at least imx6dl-riotboard. Overall I think it is quite safe to assume
that all imx6 boards crash with mainline kernels and multi_v7_defconfig.

The change can be tracked down to commit 0159ec67076 ("PM / Domains: Verify
the PM domain is present when adding a provider"). Adding everyone in the
commit log for feedback.

Guenter

>  arch/arm/mach-imx/gpc.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
> index 0df062d8b2c9..f3f40045b4c9 100644
> --- a/arch/arm/mach-imx/gpc.c
> +++ b/arch/arm/mach-imx/gpc.c
> @@ -409,6 +409,7 @@ static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg)
>  {
>  	struct clk *clk;
>  	int i;
> +	int ret;
>
>  	imx6q_pu_domain.reg = pu_reg;
>
> @@ -431,9 +432,14 @@ static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg)
>  		return 0;
>
>  	pm_genpd_init(&imx6q_pu_domain.base, NULL, false);
> -	return of_genpd_add_provider_onecell(dev->of_node,
> -					     &imx_gpc_onecell_data);
> +	ret = of_genpd_add_provider_onecell(dev->of_node,
> +					    &imx_gpc_onecell_data);
> +	if (ret)
> +		goto genpd_remove;
> +	return 0;
>
> +genpd_remove:
> +	pm_genpd_remove(&imx6q_pu_domain.base);
>  clk_err:
>  	while (i--)
>  		clk_put(imx6q_pu_domain.clk[i]);
>

^ permalink raw reply

* [PATCH v5 0/9] Add DRM driver for Hisilicon Hibmc
From: Rongrong Zou @ 2016-10-26  2:36 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set adds a new drm driver for Hisilicon Hibmc. Hibmc is a
BMC SoC with a display controller intergrated, usually it is used on
server for Out-of-band management purpose. In this patch set, we just
support basic function for Hibmc display subsystem. Hibmc display
subsystem is connected to host CPU by PCIe as blow:

+----------+       +----------+
|          | PCIe  |  Hibmc   |
|host CPU( |<----->| display  |
|arm64,x86)|       |subsystem |
+----------+       +----------+

Hardware Detail for Hibmc display subsystem
-----------

  The display subsystem of Hibmc is show as bellow:
  +----+      +----+      +----+     +--------+
  |    |      |    |      |    |     |        |
  | FB |----->| DE |----->|VDAC|---->|external|
  |    |      |    |      |    |     | VGA    |
  +----+      +----+      +----+     +--------+

  -DE(Display Engine) is the display controller.
  -VDAC(Video Digital-to-Analog converter) converts the RGB diaital data
  stream from DE to VGA analog signals.

Change History
------------

Changes in v5:
  -rebase on v4.9-rc2.
  -replace drm_fb_helper_set_suspend() with 
   drm_fb_helper_set_suspend_unlocked(), remove redundant console_lock() 
   and console_unlock(). 

Changes in v4:
  -remove unused include files, and include header file when it is needed.
  -remove unused FLAG in Kconfig: DRM_GEM_CMA_HELPER/DRM_KMS_CMA_HELPER.
  -remove drm_helper_disable_unused_functions, since we use DRIVER_ATOMIC.

Changes in v3:
  -enable KMS, in v2, only fbdev is enabled.
  -management video memory with ttm.
  -add vblank interrupt.
  -remove drm_connector_register_all() and drm_connector_unregister_all().
  -I have a basic test with igt.

Changes in v2:
  -Remove self-defined macros for bit operations.
  -Remove unused register.
  -Replace those deprecated functions with new version of them.
  -use drm_connector_register_all() to register connector after
   drm_dev_register().

The patch v2 is at
https://lists.freedesktop.org/archives/dri-devel/2016-May/108661.html

Rongrong Zou (9):
  drm/hisilicon/hibmc: Add hisilicon hibmc drm master driver
  drm/hisilicon/hibmc: Add video memory management
  drm/hisilicon/hibmc: Add support for frame buffer
  drm/hisilicon/hibmc: Add plane for DE
  drm/hisilicon/hibmc: Add crtc for DE
  drm/hisilicon/hibmc: Add encoder for VDAC
  drm/hisilicon/hibmc: Add connector for VDAC
  drm/hisilicon/hibmc: Add vblank interruput
  MAINTAINERS: Update HISILICON DRM entries

 MAINTAINERS                                       |   1 +
 drivers/gpu/drm/hisilicon/Kconfig                 |   1 +
 drivers/gpu/drm/hisilicon/Makefile                |   1 +
 drivers/gpu/drm/hisilicon/hibmc/Kconfig           |   9 +
 drivers/gpu/drm/hisilicon/hibmc/Makefile          |   5 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c    | 488 +++++++++++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.h    |  29 ++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c   | 410 ++++++++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h   | 117 +++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c | 256 ++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.c |  85 ++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.h |  28 ++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h  | 212 ++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c  | 165 +++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c       | 563 ++++++++++++++++++++++
 15 files changed, 2370 insertions(+)
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Kconfig
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Makefile
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.h
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.h
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c

-- 
1.9.1

^ permalink raw reply

* [PATCH v5 1/9] drm/hisilicon/hibmc: Add hisilicon hibmc drm master driver
From: Rongrong Zou @ 2016-10-26  2:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477449426-69018-1-git-send-email-zourongrong@gmail.com>

Add DRM master driver for Hisilicon Hibmc SoC which used for
Out-of-band management. Blow is the general hardware connection,
both the Hibmc and the host CPU are on the same mother board.

+----------+       +----------+
|          | PCIe  |  Hibmc   |
|host CPU( |<----->| display  |
|arm64,x86)|       |subsystem |
+----------+       +----------+

Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
 drivers/gpu/drm/hisilicon/Kconfig                 |   1 +
 drivers/gpu/drm/hisilicon/Makefile                |   1 +
 drivers/gpu/drm/hisilicon/hibmc/Kconfig           |   7 +
 drivers/gpu/drm/hisilicon/hibmc/Makefile          |   5 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c   | 269 ++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h   |  35 +++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.c |  85 +++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.h |  28 +++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h  | 212 +++++++++++++++++
 9 files changed, 643 insertions(+)
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Kconfig
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Makefile
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.h
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h

diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig
index 558c61b..2fd2724 100644
--- a/drivers/gpu/drm/hisilicon/Kconfig
+++ b/drivers/gpu/drm/hisilicon/Kconfig
@@ -2,4 +2,5 @@
 # hisilicon drm device configuration.
 # Please keep this list sorted alphabetically
 
+source "drivers/gpu/drm/hisilicon/hibmc/Kconfig"
 source "drivers/gpu/drm/hisilicon/kirin/Kconfig"
diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile
index e3f6d49..c8155bf 100644
--- a/drivers/gpu/drm/hisilicon/Makefile
+++ b/drivers/gpu/drm/hisilicon/Makefile
@@ -2,4 +2,5 @@
 # Makefile for hisilicon drm drivers.
 # Please keep this list sorted alphabetically
 
+obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc/
 obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
new file mode 100644
index 0000000..a9af90d
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
@@ -0,0 +1,7 @@
+config DRM_HISI_HIBMC
+	tristate "DRM Support for Hisilicon Hibmc"
+	depends on DRM && PCI
+
+	help
+	  Choose this option if you have a Hisilicon Hibmc soc chipset.
+	  If M is selected the module will be called hibmc-drm.
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
new file mode 100644
index 0000000..97cf4a0
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -0,0 +1,5 @@
+ccflags-y := -Iinclude/drm
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_power.o
+
+obj-$(CONFIG_DRM_HISI_HIBMC)	+=hibmc-drm.o
+#obj-y	+= hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
new file mode 100644
index 0000000..4669d42
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -0,0 +1,269 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/console.h>
+
+#include "hibmc_drm_drv.h"
+#include "hibmc_drm_regs.h"
+#include "hibmc_drm_power.h"
+
+static const struct file_operations hibmc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= drm_open,
+	.release	= drm_release,
+	.unlocked_ioctl	= drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= drm_compat_ioctl,
+#endif
+	.poll		= drm_poll,
+	.read		= drm_read,
+	.llseek		= no_llseek,
+};
+
+static int hibmc_enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	return 0;
+}
+
+static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+}
+
+static struct drm_driver hibmc_driver = {
+	.fops			= &hibmc_fops,
+	.name			= "hibmc",
+	.date			= "20160828",
+	.desc			= "hibmc drm driver",
+	.major			= 1,
+	.minor			= 0,
+	.get_vblank_counter	= drm_vblank_no_hw_counter,
+	.enable_vblank		= hibmc_enable_vblank,
+	.disable_vblank		= hibmc_disable_vblank,
+};
+
+static int hibmc_pm_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int hibmc_pm_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops hibmc_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(hibmc_pm_suspend,
+				hibmc_pm_resume)
+};
+
+static int hibmc_hw_config(struct hibmc_drm_device *hidev)
+{
+	unsigned int reg;
+
+	/* On hardware reset, power mode 0 is default. */
+	hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+	/* Enable display power gate & LOCALMEM power gate*/
+	reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
+	reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+	reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+	reg |= HIBMC_CURR_GATE_DISPLAY(ON);
+	reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
+
+	hibmc_set_current_gate(hidev, reg);
+
+	/* Reset the memory controller. If the memory controller
+	 * is not reset in chip,the system might hang when sw accesses
+	 * the memory.The memory should be resetted after
+	 * changing the MXCLK.
+	 */
+	reg = readl(hidev->mmio + HIBMC_MISC_CTRL);
+	reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK;
+	reg |= HIBMC_MSCCTL_LOCALMEM_RESET(RESET);
+	writel(reg, hidev->mmio + HIBMC_MISC_CTRL);
+
+	reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK;
+	reg |= HIBMC_MSCCTL_LOCALMEM_RESET(NORMAL);
+
+	writel(reg, hidev->mmio + HIBMC_MISC_CTRL);
+
+	/* We can add more initialization as needed. */
+
+	return 0;
+}
+
+static int hibmc_hw_map(struct hibmc_drm_device *hidev)
+{
+	struct drm_device *dev = hidev->dev;
+	struct pci_dev *pdev = dev->pdev;
+	resource_size_t addr, size, ioaddr, iosize;
+
+	ioaddr = pci_resource_start(pdev, 1);
+	iosize = MB(2);
+
+	hidev->mmio = ioremap_nocache(ioaddr, iosize);
+
+	if (!hidev->mmio) {
+		DRM_ERROR("Cannot map mmio region\n");
+		return -ENOMEM;
+	}
+
+	addr = pci_resource_start(pdev, 0);
+	size = MB(32);
+
+	hidev->fb_map = ioremap(addr, size);
+	if (!hidev->fb_map) {
+		DRM_ERROR("Cannot map framebuffer\n");
+		return -ENOMEM;
+	}
+	hidev->fb_base = addr;
+	hidev->fb_size = size;
+
+	return 0;
+}
+
+static void hibmc_hw_fini(struct hibmc_drm_device *hidev)
+{
+	if (hidev->mmio)
+		iounmap(hidev->mmio);
+	if (hidev->fb_map)
+		iounmap(hidev->fb_map);
+}
+
+static int hibmc_hw_init(struct hibmc_drm_device *hidev)
+{
+	int ret;
+
+	ret = hibmc_hw_map(hidev);
+	if (ret)
+		return ret;
+
+	hibmc_hw_config(hidev);
+
+	return 0;
+}
+
+static int hibmc_unload(struct drm_device *dev)
+{
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	hibmc_hw_fini(hidev);
+	dev->dev_private = NULL;
+	return 0;
+}
+
+static int hibmc_load(struct drm_device *dev, unsigned long flags)
+{
+	struct hibmc_drm_device *hidev;
+	int ret;
+
+	hidev = devm_kzalloc(dev->dev, sizeof(*hidev), GFP_KERNEL);
+	if (!hidev)
+		return -ENOMEM;
+	dev->dev_private = hidev;
+	hidev->dev = dev;
+
+	ret = hibmc_hw_init(hidev);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	hibmc_unload(dev);
+	DRM_ERROR("failed to initialize drm driver.\n");
+	return ret;
+}
+
+static int hibmc_pci_probe(struct pci_dev *pdev,
+			   const struct pci_device_id *ent)
+{
+	struct drm_device *dev;
+	int ret;
+
+	dev = drm_dev_alloc(&hibmc_driver, &pdev->dev);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->pdev = pdev;
+	pci_set_drvdata(pdev, dev);
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		goto err_free;
+
+	ret = hibmc_load(dev, 0);
+	if (ret)
+		goto err_disable;
+
+	ret = drm_dev_register(dev, 0);
+	if (ret)
+		goto err_unload;
+
+	return 0;
+
+err_unload:
+	hibmc_unload(dev);
+err_disable:
+	pci_disable_device(pdev);
+err_free:
+	drm_dev_unref(dev);
+
+	return ret;
+}
+
+static void hibmc_pci_remove(struct pci_dev *pdev)
+{
+	struct drm_device *dev = pci_get_drvdata(pdev);
+
+	drm_dev_unregister(dev);
+	hibmc_unload(dev);
+	drm_dev_unref(dev);
+}
+
+static struct pci_device_id hibmc_pci_table[] = {
+	{0x19e5, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+static struct pci_driver hibmc_pci_driver = {
+	.name =		"hibmc-drm",
+	.id_table =	hibmc_pci_table,
+	.probe =	hibmc_pci_probe,
+	.remove =	hibmc_pci_remove,
+	.driver.pm =    &hibmc_pm_ops,
+};
+
+static int __init hibmc_init(void)
+{
+	return pci_register_driver(&hibmc_pci_driver);
+}
+
+static void __exit hibmc_exit(void)
+{
+	return pci_unregister_driver(&hibmc_pci_driver);
+}
+
+module_init(hibmc_init);
+module_exit(hibmc_exit);
+
+MODULE_DEVICE_TABLE(pci, hibmc_pci_table);
+MODULE_AUTHOR("RongrongZou <zourongrong@huawei.com>");
+MODULE_DESCRIPTION("DRM Driver for Hisilicon Hibmc");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
new file mode 100644
index 0000000..0037341
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -0,0 +1,35 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef HIBMC_DRM_DRV_H
+#define HIBMC_DRM_DRV_H
+
+#include <drm/drmP.h>
+
+struct hibmc_drm_device {
+	/* hw */
+	void __iomem   *mmio;
+	void __iomem   *fb_map;
+	unsigned long  fb_base;
+	unsigned long  fb_size;
+
+	/* drm */
+	struct drm_device  *dev;
+};
+
+#endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.c
new file mode 100644
index 0000000..1036542
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.c
@@ -0,0 +1,85 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include "hibmc_drm_drv.h"
+#include "hibmc_drm_regs.h"
+
+/*
+ * It can operate in one of three modes: 0, 1 or Sleep.
+ */
+void hibmc_set_power_mode(struct hibmc_drm_device *hidev,
+			  unsigned int power_mode)
+{
+	unsigned int control_value = 0;
+	void __iomem   *mmio = hidev->mmio;
+
+	if (power_mode > HIBMC_PW_MODE_CTL_MODE_SLEEP)
+		return;
+
+	control_value = readl(mmio + HIBMC_POWER_MODE_CTRL);
+	control_value &= ~HIBMC_PW_MODE_CTL_MODE_MASK;
+
+	control_value |= HIBMC_PW_MODE_CTL_MODE(power_mode) &
+			 HIBMC_PW_MODE_CTL_MODE_MASK;
+
+    /* Set up other fields in Power Control Register */
+	if (power_mode == HIBMC_PW_MODE_CTL_MODE_SLEEP) {
+		control_value &= ~HIBMC_PW_MODE_CTL_OSC_INPUT_MASK;
+		control_value |= HIBMC_PW_MODE_CTL_OSC_INPUT(0) &
+				 HIBMC_PW_MODE_CTL_OSC_INPUT_MASK;
+	} else {
+		control_value &= ~HIBMC_PW_MODE_CTL_OSC_INPUT_MASK;
+		control_value |= HIBMC_PW_MODE_CTL_OSC_INPUT(1) &
+				 HIBMC_PW_MODE_CTL_OSC_INPUT_MASK;
+	}
+	/* Program new power mode. */
+	writel(control_value, mmio + HIBMC_POWER_MODE_CTRL);
+}
+
+static unsigned int hibmc_get_power_mode(struct hibmc_drm_device *hidev)
+{
+	void __iomem   *mmio = hidev->mmio;
+
+	return (readl(mmio + HIBMC_POWER_MODE_CTRL) &
+		HIBMC_PW_MODE_CTL_MODE_MASK) >> HIBMC_PW_MODE_CTL_MODE_SHIFT;
+}
+
+void hibmc_set_current_gate(struct hibmc_drm_device *hidev, unsigned int gate)
+{
+	unsigned int gate_reg;
+	unsigned int mode;
+	void __iomem   *mmio = hidev->mmio;
+
+	/* Get current power mode. */
+	mode = hibmc_get_power_mode(hidev);
+
+	switch (mode) {
+	case HIBMC_PW_MODE_CTL_MODE_MODE0:
+		gate_reg = HIBMC_MODE0_GATE;
+		break;
+
+	case HIBMC_PW_MODE_CTL_MODE_MODE1:
+		gate_reg = HIBMC_MODE1_GATE;
+		break;
+
+	default:
+		gate_reg = HIBMC_MODE0_GATE;
+		break;
+	}
+	writel(gate, mmio + gate_reg);
+}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.h
new file mode 100644
index 0000000..e20e1aa
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_power.h
@@ -0,0 +1,28 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef HIBMC_DRM_POWER_H
+#define HIBMC_DRM_POWER_H
+
+#include "hibmc_drm_drv.h"
+
+void hibmc_set_power_mode(struct hibmc_drm_device *hidev,
+			  unsigned int power_mode);
+void hibmc_set_current_gate(struct hibmc_drm_device *hidev,
+			    unsigned int gate);
+#endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h
new file mode 100644
index 0000000..9c804ca
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h
@@ -0,0 +1,212 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef HIBMC_DRM_HW_H
+#define HIBMC_DRM_HW_H
+
+#define OFF 0
+#define ON  1
+#define DISABLE               0
+#define ENABLE                1
+
+/* register definition */
+#define HIBMC_MISC_CTRL				0x4
+
+#define HIBMC_MSCCTL_LOCALMEM_RESET(x)		((x) << 6)
+#define HIBMC_MSCCTL_LOCALMEM_RESET_MASK	0x40
+
+#define RESET                0
+#define NORMAL               1
+
+#define HIBMC_CURRENT_GATE			0x000040
+#define HIBMC_CURR_GATE_DISPLAY(x)		((x) << 2)
+#define HIBMC_CURR_GATE_DISPLAY_MASK		0x4
+
+#define HIBMC_CURR_GATE_LOCALMEM(x)		((x) << 1)
+#define HIBMC_CURR_GATE_LOCALMEM_MASK		0x2
+
+#define HIBMC_MODE0_GATE			0x000044
+#define HIBMC_MODE1_GATE			0x000048
+#define HIBMC_POWER_MODE_CTRL			0x00004C
+
+#define HIBMC_PW_MODE_CTL_OSC_INPUT(x)		((x) << 3)
+#define HIBMC_PW_MODE_CTL_OSC_INPUT_MASK	0x8
+
+#define HIBMC_PW_MODE_CTL_MODE(x)		((x) << 0)
+#define HIBMC_PW_MODE_CTL_MODE_MASK		0x03
+#define HIBMC_PW_MODE_CTL_MODE_SHIFT		0
+
+#define HIBMC_PW_MODE_CTL_MODE_MODE0		0
+#define HIBMC_PW_MODE_CTL_MODE_MODE1		1
+#define HIBMC_PW_MODE_CTL_MODE_SLEEP		2
+
+#define HIBMC_PANEL_PLL_CTRL			0x00005C
+#define HIBMC_CRT_PLL_CTRL			0x000060
+
+#define HIBMC_PLL_CTRL_BYPASS(x)		((x) << 18)
+#define HIBMC_PLL_CTRL_BYPASS_MASK		0x40000
+
+#define HIBMC_PLL_CTRL_POWER(x)			((x) << 17)
+#define HIBMC_PLL_CTRL_POWER_MASK		0x20000
+
+#define HIBMC_PLL_CTRL_INPUT(x)			((x) << 16)
+#define HIBMC_PLL_CTRL_INPUT_MASK		0x10000
+
+#define OSC					0
+#define TESTCLK					1
+
+#define HIBMC_PLL_CTRL_POD(x)			((x) << 14)
+#define HIBMC_PLL_CTRL_POD_MASK			0xC000
+
+#define HIBMC_PLL_CTRL_OD(x)			((x) << 12)
+#define HIBMC_PLL_CTRL_OD_MASK			0x3000
+
+#define HIBMC_PLL_CTRL_N(x)			((x) << 8)
+#define HIBMC_PLL_CTRL_N_MASK			0xF00
+
+#define HIBMC_PLL_CTRL_M(x)			((x) << 0)
+#define HIBMC_PLL_CTRL_M_MASK			0xFF
+
+#define HIBMC_CRT_DISP_CTL			0x80200
+
+#define HIBMC_CRT_DISP_CTL_CRTSELECT(x)		((x) << 25)
+#define HIBMC_CRT_DISP_CTL_CRTSELECT_MASK	0x2000000
+
+#define CRTSELECT_VGA                0
+#define CRTSELECT_CRT                1
+
+#define HIBMC_CRT_DISP_CTL_CLOCK_PHASE(x)	((x) << 14)
+#define HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK	0x4000
+
+#define PHASE_ACTIVE_HIGH      0
+#define PHASE_ACTIVE_LOW       1
+
+#define HIBMC_CRT_DISP_CTL_VSYNC_PHASE(x)	((x) << 13)
+#define HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK	0x2000
+
+#define HIBMC_CRT_DISP_CTL_HSYNC_PHASE(x)	((x) << 12)
+#define HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK	0x1000
+
+#define HIBMC_CRT_DISP_CTL_TIMING(x)		((x) << 8)
+#define HIBMC_CRT_DISP_CTL_TIMING_MASK		0x100
+
+#define HIBMC_CRT_DISP_CTL_PLANE(x)		((x) << 2)
+#define HIBMC_CRT_DISP_CTL_PLANE_MASK		4
+
+#define HIBMC_CRT_DISP_CTL_FORMAT(x)		((x) << 0)
+#define HIBMC_CRT_DISP_CTL_FORMAT_MASK		0x03
+
+#define HIBMC_CRT_FB_ADDRESS			0x080204
+
+#define HIBMC_CRT_FB_WIDTH			0x080208
+#define HIBMC_CRT_FB_WIDTH_WIDTH(x)		((x) << 16)
+#define HIBMC_CRT_FB_WIDTH_WIDTH_MASK		0x3FFF0000
+#define HIBMC_CRT_FB_WIDTH_OFFS(x)		((x) << 0)
+#define HIBMC_CRT_FB_WIDTH_OFFS_MASK		0x3FFF
+
+#define HIBMC_CRT_HORZ_TOTAL			0x08020C
+#define HIBMC_CRT_HORZ_TOTAL_TOTAL(x)		((x) << 16)
+#define HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK		0xFFF0000
+
+#define HIBMC_CRT_HORZ_TOTAL_DISPLAY_END(x)	((x) << 0)
+#define HIBMC_CRT_HORZ_TOTAL_DISPLAY_END_MASK	0xFFF
+
+#define HIBMC_CRT_HORZ_SYNC			0x080210
+#define HIBMC_CRT_HORZ_SYNC_WIDTH(x)		((x) << 16)
+#define HIBMC_CRT_HORZ_SYNC_WIDTH_MASK		0xFF0000
+
+#define HIBMC_CRT_HORZ_SYNC_START(x)		((x) << 0)
+#define HIBMC_CRT_HORZ_SYNC_START_MASK		0xFFF
+
+#define HIBMC_CRT_VERT_TOTAL			0x080214
+#define HIBMC_CRT_VERT_TOTAL_TOTAL(x)		((x) << 16)
+#define HIBMC_CRT_VERT_TOTAL_TOTAL_MASK		0x7FFF0000
+
+#define HIBMC_CRT_VERT_TOTAL_DISPLAY_END(x)	((x) << 0)
+#define HIBMC_CRT_VERT_TOTAL_DISPLAY_END_MASK	0x7FF
+
+#define HIBMC_CRT_VERT_SYNC			0x080218
+#define HIBMC_CRT_VERT_SYNC_HEIGHT(x)		((x) << 16)
+#define HIBMC_CRT_VERT_SYNC_HEIGHT_MASK		0x3F0000
+
+#define HIBMC_CRT_VERT_SYNC_START(x)		((x) << 0)
+#define HIBMC_CRT_VERT_SYNC_START_MASK		0x7FF
+
+/* Auto Centering */
+#define HIBMC_CRT_AUTO_CENTERING_TL		0x080280
+#define HIBMC_CRT_AUTO_CENTERING_TL_TOP(x)	((x) << 16)
+#define HIBMC_CRT_AUTO_CENTERING_TL_TOP_MSK	0x7FF0000
+
+#define HIBMC_CRT_AUTO_CENTERING_TL_LEFT(x)	((x) << 0)
+#define HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MSK	0x7FF
+
+#define HIBMC_CRT_AUTO_CENTERING_BR		0x080284
+#define HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(x)	((x) << 16)
+#define HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK	0x7FF0000
+
+#define HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x)	((x) << 0)
+#define HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK	0x7FF
+
+/* register to control panel output */
+#define DISPLAY_CONTROL_HISILE			0x80288
+
+#define HIBMC_RAW_INTERRUPT			0x80290
+#define HIBMC_RAW_INTERRUPT_VBLANK(x)		((x) << 2)
+#define HIBMC_RAW_INTERRUPT_VBLANK_MASK		0x4
+
+#define HIBMC_RAW_INTERRUPT_EN			0x80298
+#define HIBMC_RAW_INTERRUPT_EN_VBLANK(x)	((x) << 2)
+#define HIBMC_RAW_INTERRUPT_EN_VBLANK_MASK	0x4
+
+/* register and values for PLL control */
+#define CRT_PLL1_HS				0x802a8
+#define CRT_PLL1_HS_25MHZ			0x23d40f02
+#define CRT_PLL1_HS_40MHZ			0x23940801
+#define CRT_PLL1_HS_65MHZ			0x23940d01
+#define CRT_PLL1_HS_78MHZ			0x23540F82
+#define CRT_PLL1_HS_74MHZ			0x23941dc2
+#define CRT_PLL1_HS_80MHZ			0x23941001
+#define CRT_PLL1_HS_80MHZ_1152			0x23540fc2
+#define CRT_PLL1_HS_108MHZ			0x23b41b01
+#define CRT_PLL1_HS_162MHZ			0x23480681
+#define CRT_PLL1_HS_148MHZ			0x23541dc2
+#define CRT_PLL1_HS_193MHZ			0x234807c1
+
+#define CRT_PLL2_HS				0x802ac
+#define CRT_PLL2_HS_25MHZ			0x206B851E
+#define CRT_PLL2_HS_40MHZ			0x30000000
+#define CRT_PLL2_HS_65MHZ			0x40000000
+#define CRT_PLL2_HS_78MHZ			0x50E147AE
+#define CRT_PLL2_HS_74MHZ			0x602B6AE7
+#define CRT_PLL2_HS_80MHZ			0x70000000
+#define CRT_PLL2_HS_108MHZ			0x80000000
+#define CRT_PLL2_HS_162MHZ			0xA0000000
+#define CRT_PLL2_HS_148MHZ			0xB0CCCCCD
+#define CRT_PLL2_HS_193MHZ			0xC0872B02
+
+/* Global macros */
+#define RGB(r, g, b) \
+( \
+	(unsigned long)(((r) << 16) | ((g) << 8) | (b)) \
+)
+
+#define PADDING(align, data) (((data) + (align) - 1) & (~((align) - 1)))
+
+#define MB(x) ((x) << 20)
+
+#endif
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 2/9] drm/hisilicon/hibmc: Add video memory management
From: Rongrong Zou @ 2016-10-26  2:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477449426-69018-1-git-send-email-zourongrong@gmail.com>

Hibmc have 32m video memory which can be accessed through PCIe by host,
we use ttm to manage these memory.

Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
 drivers/gpu/drm/hisilicon/hibmc/Kconfig         |   1 +
 drivers/gpu/drm/hisilicon/hibmc/Makefile        |   2 +-
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |  12 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h |  46 +++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c     | 490 ++++++++++++++++++++++++
 5 files changed, 550 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c

diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
index a9af90d..bcb8c18 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig
+++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
@@ -1,6 +1,7 @@
 config DRM_HISI_HIBMC
 	tristate "DRM Support for Hisilicon Hibmc"
 	depends on DRM && PCI
+	select DRM_TTM
 
 	help
 	  Choose this option if you have a Hisilicon Hibmc soc chipset.
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index 97cf4a0..d5c40b8 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,5 +1,5 @@
 ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_power.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_power.o hibmc_ttm.o
 
 obj-$(CONFIG_DRM_HISI_HIBMC)	+=hibmc-drm.o
 #obj-y	+= hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 4669d42..81f4301 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -31,6 +31,7 @@
 #ifdef CONFIG_COMPAT
 	.compat_ioctl	= drm_compat_ioctl,
 #endif
+	.mmap		= hibmc_mmap,
 	.poll		= drm_poll,
 	.read		= drm_read,
 	.llseek		= no_llseek,
@@ -46,6 +47,8 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
 }
 
 static struct drm_driver hibmc_driver = {
+	.driver_features	= DRIVER_GEM,
+
 	.fops			= &hibmc_fops,
 	.name			= "hibmc",
 	.date			= "20160828",
@@ -55,6 +58,10 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
 	.get_vblank_counter	= drm_vblank_no_hw_counter,
 	.enable_vblank		= hibmc_enable_vblank,
 	.disable_vblank		= hibmc_disable_vblank,
+	.gem_free_object_unlocked = hibmc_gem_free_object,
+	.dumb_create            = hibmc_dumb_create,
+	.dumb_map_offset        = hibmc_dumb_mmap_offset,
+	.dumb_destroy           = drm_gem_dumb_destroy,
 };
 
 static int hibmc_pm_suspend(struct device *dev)
@@ -163,6 +170,7 @@ static int hibmc_unload(struct drm_device *dev)
 {
 	struct hibmc_drm_device *hidev = dev->dev_private;
 
+	hibmc_mm_fini(hidev);
 	hibmc_hw_fini(hidev);
 	dev->dev_private = NULL;
 	return 0;
@@ -183,6 +191,10 @@ static int hibmc_load(struct drm_device *dev, unsigned long flags)
 	if (ret)
 		goto err;
 
+	ret = hibmc_mm_init(hidev);
+	if (ret)
+		goto err;
+
 	return 0;
 
 err:
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index 0037341..db8d80e 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -20,6 +20,8 @@
 #define HIBMC_DRM_DRV_H
 
 #include <drm/drmP.h>
+#include <drm/ttm/ttm_bo_driver.h>
+#include <drm/drm_gem.h>
 
 struct hibmc_drm_device {
 	/* hw */
@@ -30,6 +32,50 @@ struct hibmc_drm_device {
 
 	/* drm */
 	struct drm_device  *dev;
+
+	/* ttm */
+	struct {
+		struct drm_global_reference mem_global_ref;
+		struct ttm_bo_global_ref bo_global_ref;
+		struct ttm_bo_device bdev;
+		bool initialized;
+	} ttm;
+
+	bool mm_inited;
 };
 
+struct hibmc_bo {
+	struct ttm_buffer_object bo;
+	struct ttm_placement placement;
+	struct ttm_bo_kmap_obj kmap;
+	struct drm_gem_object gem;
+	struct ttm_place placements[3];
+	int pin_count;
+};
+
+static inline struct hibmc_bo *hibmc_bo(struct ttm_buffer_object *bo)
+{
+	return container_of(bo, struct hibmc_bo, bo);
+}
+
+static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
+{
+	return container_of(gem, struct hibmc_bo, gem);
+}
+
+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
+
+int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel,
+		     struct drm_gem_object **obj);
+
+int hibmc_mm_init(struct hibmc_drm_device *hibmc);
+void hibmc_mm_fini(struct hibmc_drm_device *hibmc);
+int hibmc_bo_pin(struct hibmc_bo *bo, u32 pl_flag, u64 *gpu_addr);
+void hibmc_gem_free_object(struct drm_gem_object *obj);
+int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev,
+		      struct drm_mode_create_dumb *args);
+int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
+			   u32 handle, u64 *offset);
+int hibmc_mmap(struct file *filp, struct vm_area_struct *vma);
+
 #endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
new file mode 100644
index 0000000..0802ebd
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
@@ -0,0 +1,490 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include "hibmc_drm_drv.h"
+#include <ttm/ttm_page_alloc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+static inline struct hibmc_drm_device *
+hibmc_bdev(struct ttm_bo_device *bd)
+{
+	return container_of(bd, struct hibmc_drm_device, ttm.bdev);
+}
+
+static int
+hibmc_ttm_mem_global_init(struct drm_global_reference *ref)
+{
+	return ttm_mem_global_init(ref->object);
+}
+
+static void
+hibmc_ttm_mem_global_release(struct drm_global_reference *ref)
+{
+	ttm_mem_global_release(ref->object);
+}
+
+static int hibmc_ttm_global_init(struct hibmc_drm_device *hibmc)
+{
+	struct drm_global_reference *global_ref;
+	int r;
+
+	global_ref = &hibmc->ttm.mem_global_ref;
+	global_ref->global_type = DRM_GLOBAL_TTM_MEM;
+	global_ref->size = sizeof(struct ttm_mem_global);
+	global_ref->init = &hibmc_ttm_mem_global_init;
+	global_ref->release = &hibmc_ttm_mem_global_release;
+	r = drm_global_item_ref(global_ref);
+	if (r != 0) {
+		DRM_ERROR("Failed setting up TTM memory accounting subsystem.\n"
+			 );
+		return r;
+	}
+
+	hibmc->ttm.bo_global_ref.mem_glob =
+		hibmc->ttm.mem_global_ref.object;
+	global_ref = &hibmc->ttm.bo_global_ref.ref;
+	global_ref->global_type = DRM_GLOBAL_TTM_BO;
+	global_ref->size = sizeof(struct ttm_bo_global);
+	global_ref->init = &ttm_bo_global_init;
+	global_ref->release = &ttm_bo_global_release;
+	r = drm_global_item_ref(global_ref);
+	if (r != 0) {
+		DRM_ERROR("Failed setting up TTM BO subsystem.\n");
+		drm_global_item_unref(&hibmc->ttm.mem_global_ref);
+		return r;
+	}
+	return 0;
+}
+
+static void
+hibmc_ttm_global_release(struct hibmc_drm_device *hibmc)
+{
+	if (!hibmc->ttm.mem_global_ref.release)
+		return;
+
+	drm_global_item_unref(&hibmc->ttm.bo_global_ref.ref);
+	drm_global_item_unref(&hibmc->ttm.mem_global_ref);
+	hibmc->ttm.mem_global_ref.release = NULL;
+}
+
+static void hibmc_bo_ttm_destroy(struct ttm_buffer_object *tbo)
+{
+	struct hibmc_bo *bo;
+
+	bo = container_of(tbo, struct hibmc_bo, bo);
+
+	drm_gem_object_release(&bo->gem);
+	kfree(bo);
+}
+
+static bool hibmc_ttm_bo_is_hibmc_bo(struct ttm_buffer_object *bo)
+{
+	if (bo->destroy == &hibmc_bo_ttm_destroy)
+		return true;
+	return false;
+}
+
+static int
+hibmc_bo_init_mem_type(struct ttm_bo_device *bdev, u32 type,
+		       struct ttm_mem_type_manager *man)
+{
+	switch (type) {
+	case TTM_PL_SYSTEM:
+		man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+		man->available_caching = TTM_PL_MASK_CACHING;
+		man->default_caching = TTM_PL_FLAG_CACHED;
+		break;
+	case TTM_PL_VRAM:
+		man->func = &ttm_bo_manager_func;
+		man->flags = TTM_MEMTYPE_FLAG_FIXED |
+			TTM_MEMTYPE_FLAG_MAPPABLE;
+		man->available_caching = TTM_PL_FLAG_UNCACHED |
+			TTM_PL_FLAG_WC;
+		man->default_caching = TTM_PL_FLAG_WC;
+		break;
+	default:
+		DRM_ERROR("Unsupported memory type %u\n", type);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+void hibmc_ttm_placement(struct hibmc_bo *bo, int domain)
+{
+	u32 c = 0;
+	u32 i;
+
+	bo->placement.placement = bo->placements;
+	bo->placement.busy_placement = bo->placements;
+	if (domain & TTM_PL_FLAG_VRAM)
+		bo->placements[c++].flags = TTM_PL_FLAG_WC |
+		TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+	if (domain & TTM_PL_FLAG_SYSTEM)
+		bo->placements[c++].flags = TTM_PL_MASK_CACHING |
+		TTM_PL_FLAG_SYSTEM;
+	if (!c)
+		bo->placements[c++].flags = TTM_PL_MASK_CACHING |
+		TTM_PL_FLAG_SYSTEM;
+
+	bo->placement.num_placement = c;
+	bo->placement.num_busy_placement = c;
+	for (i = 0; i < c; ++i) {
+		bo->placements[i].fpfn = 0;
+		bo->placements[i].lpfn = 0;
+	}
+}
+
+static void
+hibmc_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
+{
+	struct hibmc_bo *hibmcbo = hibmc_bo(bo);
+
+	if (!hibmc_ttm_bo_is_hibmc_bo(bo))
+		return;
+
+	hibmc_ttm_placement(hibmcbo, TTM_PL_FLAG_SYSTEM);
+	*pl = hibmcbo->placement;
+}
+
+static int hibmc_bo_verify_access(struct ttm_buffer_object *bo,
+				  struct file *filp)
+{
+	struct hibmc_bo *hibmcbo = hibmc_bo(bo);
+
+	return drm_vma_node_verify_access(&hibmcbo->gem.vma_node,
+					  filp->private_data);
+}
+
+static int hibmc_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
+				    struct ttm_mem_reg *mem)
+{
+	struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
+	struct hibmc_drm_device *hibmc = hibmc_bdev(bdev);
+
+	mem->bus.addr = NULL;
+	mem->bus.offset = 0;
+	mem->bus.size = mem->num_pages << PAGE_SHIFT;
+	mem->bus.base = 0;
+	mem->bus.is_iomem = false;
+	if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
+		return -EINVAL;
+	switch (mem->mem_type) {
+	case TTM_PL_SYSTEM:
+		/* system memory */
+		return 0;
+	case TTM_PL_VRAM:
+		mem->bus.offset = mem->start << PAGE_SHIFT;
+		mem->bus.base = pci_resource_start(hibmc->dev->pdev, 0);
+		mem->bus.is_iomem = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void hibmc_ttm_io_mem_free(struct ttm_bo_device *bdev,
+				  struct ttm_mem_reg *mem)
+{
+}
+
+static void hibmc_ttm_backend_destroy(struct ttm_tt *tt)
+{
+	ttm_tt_fini(tt);
+	kfree(tt);
+}
+
+static struct ttm_backend_func hibmc_tt_backend_func = {
+	.destroy = &hibmc_ttm_backend_destroy,
+};
+
+static struct ttm_tt *hibmc_ttm_tt_create(struct ttm_bo_device *bdev,
+					  unsigned long size,
+					  u32 page_flags,
+					  struct page *dummy_read_page)
+{
+	struct ttm_tt *tt;
+
+	tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+	if (!tt)
+		return NULL;
+	tt->func = &hibmc_tt_backend_func;
+	if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) {
+		kfree(tt);
+		return NULL;
+	}
+	return tt;
+}
+
+static int hibmc_ttm_tt_populate(struct ttm_tt *ttm)
+{
+	return ttm_pool_populate(ttm);
+}
+
+static void hibmc_ttm_tt_unpopulate(struct ttm_tt *ttm)
+{
+	ttm_pool_unpopulate(ttm);
+}
+
+struct ttm_bo_driver hibmc_bo_driver = {
+	.ttm_tt_create		= hibmc_ttm_tt_create,
+	.ttm_tt_populate	= hibmc_ttm_tt_populate,
+	.ttm_tt_unpopulate	= hibmc_ttm_tt_unpopulate,
+	.init_mem_type		= hibmc_bo_init_mem_type,
+	.evict_flags		= hibmc_bo_evict_flags,
+	.move			= NULL,
+	.verify_access		= hibmc_bo_verify_access,
+	.io_mem_reserve		= &hibmc_ttm_io_mem_reserve,
+	.io_mem_free		= &hibmc_ttm_io_mem_free,
+	.lru_tail		= &ttm_bo_default_lru_tail,
+	.swap_lru_tail		= &ttm_bo_default_swap_lru_tail,
+};
+
+int hibmc_mm_init(struct hibmc_drm_device *hibmc)
+{
+	int ret;
+	struct drm_device *dev = hibmc->dev;
+	struct ttm_bo_device *bdev = &hibmc->ttm.bdev;
+
+	ret = hibmc_ttm_global_init(hibmc);
+	if (ret)
+		return ret;
+
+	ret = ttm_bo_device_init(&hibmc->ttm.bdev,
+				 hibmc->ttm.bo_global_ref.ref.object,
+				 &hibmc_bo_driver,
+				 dev->anon_inode->i_mapping,
+				 DRM_FILE_PAGE_OFFSET,
+				 true);
+	if (ret) {
+		DRM_ERROR("Error initialising bo driver; %d\n", ret);
+		return ret;
+	}
+
+	ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
+			     hibmc->fb_size >> PAGE_SHIFT);
+	if (ret) {
+		DRM_ERROR("Failed ttm VRAM init: %d\n", ret);
+		return ret;
+	}
+
+	hibmc->mm_inited = true;
+	return 0;
+}
+
+void hibmc_mm_fini(struct hibmc_drm_device *hibmc)
+{
+	if (!hibmc->mm_inited)
+		return;
+
+	ttm_bo_device_release(&hibmc->ttm.bdev);
+	hibmc_ttm_global_release(hibmc);
+	hibmc->mm_inited = false;
+}
+
+int hibmc_bo_create(struct drm_device *dev, int size, int align,
+		    u32 flags, struct hibmc_bo **phibmcbo)
+{
+	struct hibmc_drm_device *hibmc = dev->dev_private;
+	struct hibmc_bo *hibmcbo;
+	size_t acc_size;
+	int ret;
+
+	hibmcbo = kzalloc(sizeof(*hibmcbo), GFP_KERNEL);
+	if (!hibmcbo)
+		return -ENOMEM;
+
+	ret = drm_gem_object_init(dev, &hibmcbo->gem, size);
+	if (ret) {
+		kfree(hibmcbo);
+		return ret;
+	}
+
+	hibmcbo->bo.bdev = &hibmc->ttm.bdev;
+
+	hibmc_ttm_placement(hibmcbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
+
+	acc_size = ttm_bo_dma_acc_size(&hibmc->ttm.bdev, size,
+				       sizeof(struct hibmc_bo));
+
+	ret = ttm_bo_init(&hibmc->ttm.bdev, &hibmcbo->bo, size,
+			  ttm_bo_type_device, &hibmcbo->placement,
+			  align >> PAGE_SHIFT, false, NULL, acc_size,
+			  NULL, NULL, hibmc_bo_ttm_destroy);
+	if (ret)
+		return ret;
+
+	*phibmcbo = hibmcbo;
+	return 0;
+}
+
+static inline u64 hibmc_bo_gpu_offset(struct hibmc_bo *bo)
+{
+	return bo->bo.offset;
+}
+
+int hibmc_bo_pin(struct hibmc_bo *bo, u32 pl_flag, u64 *gpu_addr)
+{
+	int i, ret;
+
+	if (bo->pin_count) {
+		bo->pin_count++;
+		if (gpu_addr)
+			*gpu_addr = hibmc_bo_gpu_offset(bo);
+	}
+
+	hibmc_ttm_placement(bo, pl_flag);
+	for (i = 0; i < bo->placement.num_placement; i++)
+		bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
+	ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+	if (ret)
+		return ret;
+
+	bo->pin_count = 1;
+	if (gpu_addr)
+		*gpu_addr = hibmc_bo_gpu_offset(bo);
+	return 0;
+}
+
+int hibmc_bo_push_sysram(struct hibmc_bo *bo)
+{
+	int i, ret;
+
+	if (!bo->pin_count) {
+		DRM_ERROR("unpin bad %p\n", bo);
+		return 0;
+	}
+	bo->pin_count--;
+	if (bo->pin_count)
+		return 0;
+
+	if (bo->kmap.virtual)
+		ttm_bo_kunmap(&bo->kmap);
+
+	hibmc_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
+	for (i = 0; i < bo->placement.num_placement ; i++)
+		bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
+
+	ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
+	if (ret) {
+		DRM_ERROR("pushing to VRAM failed\n");
+		return ret;
+	}
+	return 0;
+}
+
+int hibmc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct drm_file *file_priv;
+	struct hibmc_drm_device *hibmc;
+
+	if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
+		return -EINVAL;
+
+	file_priv = filp->private_data;
+	hibmc = file_priv->minor->dev->dev_private;
+	return ttm_bo_mmap(filp, vma, &hibmc->ttm.bdev);
+}
+
+int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel,
+		     struct drm_gem_object **obj)
+{
+	struct hibmc_bo *hibmcbo;
+	int ret;
+
+	*obj = NULL;
+
+	size = PAGE_ALIGN(size);
+	if (size == 0)
+		return -EINVAL;
+
+	ret = hibmc_bo_create(dev, size, 0, 0, &hibmcbo);
+	if (ret) {
+		if (ret != -ERESTARTSYS)
+			DRM_ERROR("failed to allocate GEM object\n");
+		return ret;
+	}
+	*obj = &hibmcbo->gem;
+	return 0;
+}
+
+int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev,
+		      struct drm_mode_create_dumb *args)
+{
+	struct drm_gem_object *gobj;
+	u32 handle;
+	int ret;
+
+	args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 16);
+	args->size = args->pitch * args->height;
+
+	ret = hibmc_gem_create(dev, args->size, false,
+			       &gobj);
+	if (ret)
+		return ret;
+
+	ret = drm_gem_handle_create(file, gobj, &handle);
+	drm_gem_object_unreference_unlocked(gobj);
+	if (ret)
+		return ret;
+
+	args->handle = handle;
+	return 0;
+}
+
+static void hibmc_bo_unref(struct hibmc_bo **bo)
+{
+	struct ttm_buffer_object *tbo;
+
+	if ((*bo) == NULL)
+		return;
+
+	tbo = &((*bo)->bo);
+	ttm_bo_unref(&tbo);
+	*bo = NULL;
+}
+
+void hibmc_gem_free_object(struct drm_gem_object *obj)
+{
+	struct hibmc_bo *hibmcbo = gem_to_hibmc_bo(obj);
+
+	hibmc_bo_unref(&hibmcbo);
+}
+
+static u64 hibmc_bo_mmap_offset(struct hibmc_bo *bo)
+{
+	return drm_vma_node_offset_addr(&bo->bo.vma_node);
+}
+
+int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
+			   u32 handle, u64 *offset)
+{
+	struct drm_gem_object *obj;
+	struct hibmc_bo *bo;
+
+	obj = drm_gem_object_lookup(file, handle);
+	if (!obj)
+		return -ENOENT;
+
+	bo = gem_to_hibmc_bo(obj);
+	*offset = hibmc_bo_mmap_offset(bo);
+
+	drm_gem_object_unreference_unlocked(obj);
+	return 0;
+}
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 3/9] drm/hisilicon/hibmc: Add support for frame buffer
From: Rongrong Zou @ 2016-10-26  2:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477449426-69018-1-git-send-email-zourongrong@gmail.com>

Add support for fbdev and kms fb management.

Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
 drivers/gpu/drm/hisilicon/hibmc/Makefile          |   2 +-
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c   |  17 ++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h   |  25 +++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c | 256 ++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c       |  67 ++++++
 5 files changed, 366 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c

diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index d5c40b8..810a37e 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,5 +1,5 @@
 ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_power.o hibmc_ttm.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_fbdev.o hibmc_drm_power.o hibmc_ttm.o
 
 obj-$(CONFIG_DRM_HISI_HIBMC)	+=hibmc-drm.o
 #obj-y	+= hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 81f4301..cddf2bb 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -66,11 +66,23 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
 
 static int hibmc_pm_suspend(struct device *dev)
 {
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct drm_device *drm_dev = pci_get_drvdata(pdev);
+	struct hibmc_drm_device *hidev = drm_dev->dev_private;
+
+	drm_fb_helper_set_suspend_unlocked(&hidev->fbdev.helper, 1);
+
 	return 0;
 }
 
 static int hibmc_pm_resume(struct device *dev)
 {
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct drm_device *drm_dev = pci_get_drvdata(pdev);
+	struct hibmc_drm_device *hidev = drm_dev->dev_private;
+
+	drm_fb_helper_set_suspend_unlocked(&hidev->fbdev.helper, 0);
+
 	return 0;
 }
 
@@ -170,6 +182,7 @@ static int hibmc_unload(struct drm_device *dev)
 {
 	struct hibmc_drm_device *hidev = dev->dev_private;
 
+	hibmc_fbdev_fini(hidev);
 	hibmc_mm_fini(hidev);
 	hibmc_hw_fini(hidev);
 	dev->dev_private = NULL;
@@ -195,6 +208,10 @@ static int hibmc_load(struct drm_device *dev, unsigned long flags)
 	if (ret)
 		goto err;
 
+	ret = hibmc_fbdev_init(hidev);
+	if (ret)
+		goto err;
+
 	return 0;
 
 err:
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index db8d80e..d41138a 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -20,9 +20,23 @@
 #define HIBMC_DRM_DRV_H
 
 #include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/drm_gem.h>
 
+struct hibmc_framebuffer {
+	struct drm_framebuffer fb;
+	struct drm_gem_object *obj;
+	bool is_fbdev_fb;
+};
+
+struct hibmc_fbdev {
+	struct drm_fb_helper helper;
+	struct hibmc_framebuffer fb;
+	int size;
+	bool initialized;
+};
+
 struct hibmc_drm_device {
 	/* hw */
 	void __iomem   *mmio;
@@ -41,9 +55,13 @@ struct hibmc_drm_device {
 		bool initialized;
 	} ttm;
 
+	/* fbdev */
+	struct hibmc_fbdev fbdev;
 	bool mm_inited;
 };
 
+#define to_hibmc_framebuffer(x) container_of(x, struct hibmc_framebuffer, fb)
+
 struct hibmc_bo {
 	struct ttm_buffer_object bo;
 	struct ttm_placement placement;
@@ -65,8 +83,15 @@ static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
 
 #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
 
+int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
+void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);
+
 int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel,
 		     struct drm_gem_object **obj);
+int hibmc_framebuffer_init(struct drm_device *dev,
+			   struct hibmc_framebuffer *gfb,
+			   const struct drm_mode_fb_cmd2 *mode_cmd,
+			   struct drm_gem_object *obj);
 
 int hibmc_mm_init(struct hibmc_drm_device *hibmc);
 void hibmc_mm_fini(struct hibmc_drm_device *hibmc);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
new file mode 100644
index 0000000..de7ca92
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
@@ -0,0 +1,256 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+
+#include "hibmc_drm_drv.h"
+
+/* ---------------------------------------------------------------------- */
+
+static int hibmcfb_create_object(
+				struct hibmc_drm_device *hidev,
+				const struct drm_mode_fb_cmd2 *mode_cmd,
+				struct drm_gem_object **gobj_p)
+{
+	struct drm_gem_object *gobj;
+	struct drm_device *dev = hidev->dev;
+	u32 size;
+	int ret = 0;
+
+	size = mode_cmd->pitches[0] * mode_cmd->height;
+	ret = hibmc_gem_create(dev, size, true, &gobj);
+	if (ret)
+		return ret;
+
+	*gobj_p = gobj;
+	return ret;
+}
+
+static struct fb_ops hibmc_drm_fb_ops = {
+	.owner = THIS_MODULE,
+	.fb_check_var = drm_fb_helper_check_var,
+	.fb_set_par = drm_fb_helper_set_par,
+	.fb_fillrect = drm_fb_helper_sys_fillrect,
+	.fb_copyarea = drm_fb_helper_sys_copyarea,
+	.fb_imageblit = drm_fb_helper_sys_imageblit,
+	.fb_pan_display = drm_fb_helper_pan_display,
+	.fb_blank = drm_fb_helper_blank,
+	.fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int hibmc_drm_fb_create(struct drm_fb_helper *helper,
+			       struct drm_fb_helper_surface_size *sizes)
+{
+	struct hibmc_fbdev *gfbdev =
+		container_of(helper, struct hibmc_fbdev, helper);
+	struct hibmc_drm_device *hidev =
+		container_of(helper, struct hibmc_drm_device, fbdev.helper);
+	struct fb_info *info;
+	struct drm_framebuffer *fb;
+	struct drm_mode_fb_cmd2 mode_cmd;
+	struct drm_gem_object *gobj = NULL;
+	int ret = 0;
+	size_t size;
+	unsigned int bytes_per_pixel;
+	struct hibmc_bo *bo = NULL;
+
+	DRM_DEBUG_DRIVER("surface width(%d), height(%d) and bpp(%d)\n",
+			 sizes->surface_width, sizes->surface_height,
+			 sizes->surface_bpp);
+	sizes->surface_depth = 32;
+
+	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.pitches[0] = mode_cmd.width * bytes_per_pixel;
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+							  sizes->surface_depth);
+
+	size = roundup(mode_cmd.pitches[0] * mode_cmd.height, PAGE_SIZE);
+
+	ret = hibmcfb_create_object(hidev, &mode_cmd, &gobj);
+	if (ret) {
+		DRM_ERROR("failed to create fbcon backing object %d\r\n", ret);
+		return -ENOMEM;
+	}
+
+	bo = gem_to_hibmc_bo(gobj);
+
+	ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
+	if (ret)
+		return ret;
+
+	ret = hibmc_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL);
+	if (ret) {
+		DRM_ERROR("failed to pin fbcon\n");
+		return ret;
+	}
+
+	ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+
+	if (ret) {
+		DRM_ERROR("failed to kmap fbcon\n");
+		ttm_bo_unreserve(&bo->bo);
+		return ret;
+	}
+
+	ttm_bo_unreserve(&bo->bo);
+
+	info = drm_fb_helper_alloc_fbi(helper);
+	if (IS_ERR(info))
+		ret = PTR_ERR(info);
+
+	info->par = gfbdev;
+
+	ret = hibmc_framebuffer_init(hidev->dev, &hidev->fbdev.fb,
+				     &mode_cmd, gobj);
+	if (ret) {
+		drm_fb_helper_release_fbi(helper);
+		return ret;
+	}
+
+	hidev->fbdev.size = size;
+	fb = &gfbdev->fb.fb;
+	if (!fb) {
+		DRM_INFO("fb is NULL\n");
+		return -EINVAL;
+	}
+
+	gfbdev->helper.fb = fb;
+
+	strcpy(info->fix.id, "hibmcdrmfb");
+
+	info->flags = FBINFO_DEFAULT;
+	info->fbops = &hibmc_drm_fb_ops;
+
+	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+	drm_fb_helper_fill_var(info, &hidev->fbdev.helper, sizes->fb_width,
+			       sizes->fb_height);
+
+	info->screen_base = bo->kmap.virtual;
+	info->screen_size = size;
+
+	info->fix.smem_start = bo->bo.mem.bus.offset + bo->bo.mem.bus.base;
+	info->fix.smem_len = size;
+
+	return 0;
+}
+
+static int hibmc_fbdev_destroy(struct drm_device *dev,
+			       struct hibmc_fbdev *fbdev)
+{
+	struct hibmc_framebuffer *gfb = &fbdev->fb;
+	struct drm_fb_helper *fbh = &fbdev->helper;
+
+	DRM_DEBUG_DRIVER("hibmc_fbdev_destroy\n");
+
+	drm_fb_helper_unregister_fbi(fbh);
+	drm_fb_helper_release_fbi(fbh);
+
+	if (gfb->obj) {
+		drm_gem_object_unreference_unlocked(gfb->obj);
+		gfb->obj = NULL;
+	}
+
+	drm_fb_helper_fini(fbh);
+
+	drm_framebuffer_unregister_private(&gfb->fb);
+	drm_framebuffer_cleanup(&gfb->fb);
+
+	return 0;
+}
+
+static const struct drm_fb_helper_funcs hibmc_fbdev_helper_funcs = {
+	.fb_probe = hibmc_drm_fb_create,
+};
+
+int hibmc_fbdev_init(struct hibmc_drm_device *hidev)
+{
+	int ret;
+	struct fb_var_screeninfo *var;
+	struct fb_fix_screeninfo *fix;
+
+	drm_fb_helper_prepare(hidev->dev, &hidev->fbdev.helper,
+			      &hibmc_fbdev_helper_funcs);
+
+	/* Now just one crtc and one channel */
+	ret = drm_fb_helper_init(hidev->dev,
+				 &hidev->fbdev.helper, 1, 1);
+
+	if (ret)
+		return ret;
+
+	ret = drm_fb_helper_single_add_all_connectors(&hidev->fbdev.helper);
+	if (ret)
+		goto fini;
+
+	ret = drm_fb_helper_initial_config(&hidev->fbdev.helper, 16);
+	if (ret)
+		goto fini;
+
+	hidev->fbdev.initialized = true;
+
+	var = &hidev->fbdev.helper.fbdev->var;
+	fix = &hidev->fbdev.helper.fbdev->fix;
+
+	DRM_DEBUG("Member of info->var is :\n"
+		 "xres=%d\n"
+		 "yres=%d\n"
+		 "xres_virtual=%d\n"
+		 "yres_virtual=%d\n"
+		 "xoffset=%d\n"
+		 "yoffset=%d\n"
+		 "bits_per_pixel=%d\n"
+		 "...\n", var->xres, var->yres, var->xres_virtual,
+		 var->yres_virtual, var->xoffset, var->yoffset,
+		 var->bits_per_pixel);
+	DRM_DEBUG("Member of info->fix is :\n"
+		 "smem_start=%lx\n"
+		 "smem_len=%d\n"
+		 "type=%d\n"
+		 "type_aux=%d\n"
+		 "visual=%d\n"
+		 "xpanstep=%d\n"
+		 "ypanstep=%d\n"
+		 "ywrapstep=%d\n"
+		 "line_length=%d\n"
+		 "accel=%d\n"
+		 "capabilities=%d\n"
+		 "...\n", fix->smem_start, fix->smem_len, fix->type,
+		 fix->type_aux, fix->visual, fix->xpanstep,
+		 fix->ypanstep, fix->ywrapstep, fix->line_length,
+		 fix->accel, fix->capabilities);
+
+	return 0;
+
+fini:
+	drm_fb_helper_fini(&hidev->fbdev.helper);
+	return ret;
+}
+
+void hibmc_fbdev_fini(struct hibmc_drm_device *hidev)
+{
+	if (!hidev->fbdev.initialized)
+		return;
+
+	hibmc_fbdev_destroy(hidev->dev, &hidev->fbdev);
+	hidev->fbdev.initialized = false;
+}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
index 0802ebd..e8e4e2c 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
@@ -488,3 +488,70 @@ int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
 	drm_gem_object_unreference_unlocked(obj);
 	return 0;
 }
+
+/* ---------------------------------------------------------------------- */
+
+static void hibmc_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+	struct hibmc_framebuffer *hibmc_fb = to_hibmc_framebuffer(fb);
+
+	drm_gem_object_unreference_unlocked(hibmc_fb->obj);
+	drm_framebuffer_cleanup(fb);
+	kfree(fb);
+}
+
+static const struct drm_framebuffer_funcs hibmc_fb_funcs = {
+	.destroy = hibmc_user_framebuffer_destroy,
+};
+
+int hibmc_framebuffer_init(struct drm_device *dev,
+			   struct hibmc_framebuffer *gfb,
+			   const struct drm_mode_fb_cmd2 *mode_cmd,
+			   struct drm_gem_object *obj)
+{
+	int ret;
+
+	drm_helper_mode_fill_fb_struct(&gfb->fb, mode_cmd);
+	gfb->obj = obj;
+	ret = drm_framebuffer_init(dev, &gfb->fb, &hibmc_fb_funcs);
+	if (ret) {
+		DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static struct drm_framebuffer *
+hibmc_user_framebuffer_create(struct drm_device *dev,
+			      struct drm_file *filp,
+			      const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct drm_gem_object *obj;
+	struct hibmc_framebuffer *hibmc_fb;
+	int ret;
+
+	DRM_DEBUG_DRIVER("%dx%d, format %c%c%c%c\n",
+			 mode_cmd->width, mode_cmd->height,
+			 (mode_cmd->pixel_format) & 0xff,
+			 (mode_cmd->pixel_format >> 8)  & 0xff,
+			 (mode_cmd->pixel_format >> 16) & 0xff,
+			 (mode_cmd->pixel_format >> 24) & 0xff);
+
+	obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
+	if (!obj)
+		return ERR_PTR(-ENOENT);
+
+	hibmc_fb = kzalloc(sizeof(*hibmc_fb), GFP_KERNEL);
+	if (!hibmc_fb) {
+		drm_gem_object_unreference_unlocked(obj);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	ret = hibmc_framebuffer_init(dev, hibmc_fb, mode_cmd, obj);
+	if (ret) {
+		drm_gem_object_unreference_unlocked(obj);
+		kfree(hibmc_fb);
+		return ERR_PTR(ret);
+	}
+	return &hibmc_fb->fb;
+}
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 4/9] drm/hisilicon/hibmc: Add plane for DE
From: Rongrong Zou @ 2016-10-26  2:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477449426-69018-1-git-send-email-zourongrong@gmail.com>

Add plane funcs and helper funcs for DE.

Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
 drivers/gpu/drm/hisilicon/hibmc/Kconfig         |   1 +
 drivers/gpu/drm/hisilicon/hibmc/Makefile        |   2 +-
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c  | 170 ++++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.h  |  29 ++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |  51 ++++++-
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h |   5 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c     |   6 +
 7 files changed, 261 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.h

diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
index bcb8c18..380622a 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig
+++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
@@ -1,6 +1,7 @@
 config DRM_HISI_HIBMC
 	tristate "DRM Support for Hisilicon Hibmc"
 	depends on DRM && PCI
+	select DRM_KMS_HELPER
 	select DRM_TTM
 
 	help
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index 810a37e..72e107e 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,5 +1,5 @@
 ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_fbdev.o hibmc_drm_power.o hibmc_ttm.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_fbdev.o hibmc_drm_power.o hibmc_ttm.o
 
 obj-$(CONFIG_DRM_HISI_HIBMC)	+=hibmc-drm.o
 #obj-y	+= hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
new file mode 100644
index 0000000..9c1a68c
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -0,0 +1,170 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "hibmc_drm_drv.h"
+#include "hibmc_drm_regs.h"
+#include "hibmc_drm_power.h"
+
+/* ---------------------------------------------------------------------- */
+
+static int hibmc_plane_atomic_check(struct drm_plane *plane,
+				    struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc *crtc = state->crtc;
+	struct drm_crtc_state *crtc_state;
+	u32 src_x = state->src_x >> 16;
+	u32 src_y = state->src_y >> 16;
+	u32 src_w = state->src_w >> 16;
+	u32 src_h = state->src_h >> 16;
+	int crtc_x = state->crtc_x;
+	int crtc_y = state->crtc_y;
+	u32 crtc_w = state->crtc_w;
+	u32 crtc_h = state->crtc_h;
+
+	if (!crtc || !fb)
+		return 0;
+
+	crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	if (src_w != crtc_w || src_h != crtc_h) {
+		DRM_ERROR("Scale not support!!!\n");
+		return -EINVAL;
+	}
+
+	if (src_x + src_w > fb->width ||
+	    src_y + src_h > fb->height)
+		return -EINVAL;
+
+	if (crtc_x < 0 || crtc_y < 0)
+		return -EINVAL;
+
+	if (crtc_x + crtc_w > crtc_state->adjusted_mode.hdisplay ||
+	    crtc_y + crtc_h > crtc_state->adjusted_mode.vdisplay)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void hibmc_plane_atomic_update(struct drm_plane *plane,
+				      struct drm_plane_state *old_state)
+{
+	struct drm_plane_state	*state	= plane->state;
+	u32 reg;
+	int ret;
+	u64 gpu_addr = 0;
+	unsigned int line_l;
+	struct hibmc_drm_device *hidev =
+		(struct hibmc_drm_device *)plane->dev->dev_private;
+
+	struct hibmc_framebuffer *hibmc_fb;
+	struct hibmc_bo *bo;
+
+	hibmc_fb = to_hibmc_framebuffer(state->fb);
+	bo = gem_to_hibmc_bo(hibmc_fb->obj);
+	ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
+	if (ret)
+		return;
+
+	hibmc_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
+	if (ret) {
+		ttm_bo_unreserve(&bo->bo);
+		return;
+	}
+
+	ttm_bo_unreserve(&bo->bo);
+
+	writel(gpu_addr, hidev->mmio + HIBMC_CRT_FB_ADDRESS);
+
+	reg = state->fb->width * (state->fb->bits_per_pixel >> 3);
+	/* now line_pad is 16 */
+	reg = PADDING(16, reg);
+
+	line_l = state->fb->width * state->fb->bits_per_pixel / 8;
+	line_l = PADDING(16, line_l);
+	writel((HIBMC_CRT_FB_WIDTH_WIDTH(reg) & HIBMC_CRT_FB_WIDTH_WIDTH_MASK) |
+	       (HIBMC_CRT_FB_WIDTH_OFFS(line_l) & HIBMC_CRT_FB_WIDTH_OFFS_MASK),
+	       hidev->mmio + HIBMC_CRT_FB_WIDTH);
+
+	/* SET PIXEL FORMAT */
+	reg = readl(hidev->mmio + HIBMC_CRT_DISP_CTL);
+	reg = reg & ~HIBMC_CRT_DISP_CTL_FORMAT_MASK;
+	reg = reg | (HIBMC_CRT_DISP_CTL_FORMAT(state->fb->bits_per_pixel >> 4) &
+		     HIBMC_CRT_DISP_CTL_FORMAT_MASK);
+	writel(reg, hidev->mmio + HIBMC_CRT_DISP_CTL);
+}
+
+static void hibmc_plane_atomic_disable(struct drm_plane *plane,
+				       struct drm_plane_state *old_state)
+{
+}
+
+static const u32 channel_formats1[] = {
+	DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888
+};
+
+static struct drm_plane_funcs hibmc_plane_funcs = {
+	.update_plane	= drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.set_property = drm_atomic_helper_plane_set_property,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = {
+	.atomic_check = hibmc_plane_atomic_check,
+	.atomic_update = hibmc_plane_atomic_update,
+	.atomic_disable = hibmc_plane_atomic_disable,
+};
+
+int hibmc_plane_init(struct hibmc_drm_device *hidev)
+{
+	struct drm_device *dev = hidev->dev;
+	struct drm_plane *plane = &hidev->plane;
+	int ret = 0;
+
+	/*
+	 * plane init
+	 * TODO: Now only support primary plane, overlay planes
+	 * need to do.
+	 */
+	ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs,
+				       channel_formats1,
+				       ARRAY_SIZE(channel_formats1),
+				       DRM_PLANE_TYPE_PRIMARY,
+				       NULL);
+	if (ret) {
+		DRM_ERROR("fail to init plane!!!\n");
+		return ret;
+	}
+
+	drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
+	return 0;
+}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.h
new file mode 100644
index 0000000..4ce0d7b
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.h
@@ -0,0 +1,29 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef HIBMC_DRM_DE_H
+#define HIBMC_DRM_DE_H
+
+struct panel_pll {
+	unsigned long M;
+	unsigned long N;
+	unsigned long OD;
+	unsigned long POD;
+};
+
+#endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index cddf2bb..8608ac2 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -18,6 +18,7 @@
 
 #include <linux/module.h>
 #include <linux/console.h>
+#include <drm/drm_crtc_helper.h>
 
 #include "hibmc_drm_drv.h"
 #include "hibmc_drm_regs.h"
@@ -47,8 +48,8 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe)
 }
 
 static struct drm_driver hibmc_driver = {
-	.driver_features	= DRIVER_GEM,
-
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
+				  DRIVER_ATOMIC,
 	.fops			= &hibmc_fops,
 	.name			= "hibmc",
 	.date			= "20160828",
@@ -70,6 +71,7 @@ static int hibmc_pm_suspend(struct device *dev)
 	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 	struct hibmc_drm_device *hidev = drm_dev->dev_private;
 
+	drm_kms_helper_poll_disable(drm_dev);
 	drm_fb_helper_set_suspend_unlocked(&hidev->fbdev.helper, 1);
 
 	return 0;
@@ -81,7 +83,9 @@ static int hibmc_pm_resume(struct device *dev)
 	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 	struct hibmc_drm_device *hidev = drm_dev->dev_private;
 
+	drm_helper_resume_force_mode(drm_dev);
 	drm_fb_helper_set_suspend_unlocked(&hidev->fbdev.helper, 0);
+	drm_kms_helper_poll_enable(drm_dev);
 
 	return 0;
 }
@@ -91,6 +95,41 @@ static int hibmc_pm_resume(struct device *dev)
 				hibmc_pm_resume)
 };
 
+static int hibmc_kms_init(struct hibmc_drm_device *hidev)
+{
+	int ret;
+
+	drm_mode_config_init(hidev->dev);
+	hidev->mode_config_initialized = true;
+
+	hidev->dev->mode_config.min_width = 0;
+	hidev->dev->mode_config.min_height = 0;
+	hidev->dev->mode_config.max_width = 1920;
+	hidev->dev->mode_config.max_height = 1440;
+
+	hidev->dev->mode_config.fb_base = hidev->fb_base;
+	hidev->dev->mode_config.preferred_depth = 24;
+	hidev->dev->mode_config.prefer_shadow = 0;
+
+	hidev->dev->mode_config.funcs = (void *)&hibmc_mode_funcs;
+
+	ret = hibmc_plane_init(hidev);
+	if (ret) {
+		DRM_ERROR("fail to init plane!!!\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void hibmc_kms_fini(struct hibmc_drm_device *hidev)
+{
+	if (hidev->mode_config_initialized) {
+		drm_mode_config_cleanup(hidev->dev);
+		hidev->mode_config_initialized = false;
+	}
+}
+
 static int hibmc_hw_config(struct hibmc_drm_device *hidev)
 {
 	unsigned int reg;
@@ -183,6 +222,7 @@ static int hibmc_unload(struct drm_device *dev)
 	struct hibmc_drm_device *hidev = dev->dev_private;
 
 	hibmc_fbdev_fini(hidev);
+	hibmc_kms_fini(hidev);
 	hibmc_mm_fini(hidev);
 	hibmc_hw_fini(hidev);
 	dev->dev_private = NULL;
@@ -208,6 +248,13 @@ static int hibmc_load(struct drm_device *dev, unsigned long flags)
 	if (ret)
 		goto err;
 
+	ret = hibmc_kms_init(hidev);
+	if (ret)
+		goto err;
+
+	/* reset all the states of crtc/plane/encoder/connector */
+	drm_mode_config_reset(dev);
+
 	ret = hibmc_fbdev_init(hidev);
 	if (ret)
 		goto err;
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index d41138a..28a3663 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -46,6 +46,8 @@ struct hibmc_drm_device {
 
 	/* drm */
 	struct drm_device  *dev;
+	struct drm_plane plane;
+	bool mode_config_initialized;
 
 	/* ttm */
 	struct {
@@ -83,6 +85,7 @@ static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
 
 #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
 
+int hibmc_plane_init(struct hibmc_drm_device *hidev);
 int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
 void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);
 
@@ -103,4 +106,6 @@ int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev,
 			   u32 handle, u64 *offset);
 int hibmc_mmap(struct file *filp, struct vm_area_struct *vma);
 
+extern const struct drm_mode_config_funcs hibmc_mode_funcs;
+
 #endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
index e8e4e2c..01578a7 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c
@@ -555,3 +555,9 @@ int hibmc_framebuffer_init(struct drm_device *dev,
 	}
 	return &hibmc_fb->fb;
 }
+
+const struct drm_mode_config_funcs hibmc_mode_funcs = {
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+	.fb_create = hibmc_user_framebuffer_create,
+};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 5/9] drm/hisilicon/hibmc: Add crtc for DE
From: Rongrong Zou @ 2016-10-26  2:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477449426-69018-1-git-send-email-zourongrong@gmail.com>

Add crtc funcs and helper funcs for DE.

Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c  | 318 ++++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c |   6 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h |   2 +
 3 files changed, 326 insertions(+)

diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
index 9c1a68c..9b5d0d0 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -23,6 +23,7 @@
 
 #include "hibmc_drm_drv.h"
 #include "hibmc_drm_regs.h"
+#include "hibmc_drm_de.h"
 #include "hibmc_drm_power.h"
 
 /* ---------------------------------------------------------------------- */
@@ -168,3 +169,320 @@ int hibmc_plane_init(struct hibmc_drm_device *hidev)
 	drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
 	return 0;
 }
+
+static void hibmc_crtc_enable(struct drm_crtc *crtc)
+{
+	unsigned int reg;
+	/* power mode 0 is default. */
+	struct hibmc_drm_device *hidev = crtc->dev->dev_private;
+
+	hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+	/* Enable display power gate & LOCALMEM power gate*/
+	reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
+	reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+	reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+	reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
+	reg |= HIBMC_CURR_GATE_DISPLAY(ON);
+	hibmc_set_current_gate(hidev, reg);
+	drm_crtc_vblank_on(crtc);
+}
+
+static void hibmc_crtc_disable(struct drm_crtc *crtc)
+{
+	unsigned int reg;
+	struct hibmc_drm_device *hidev = crtc->dev->dev_private;
+
+	drm_crtc_vblank_off(crtc);
+
+	hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_SLEEP);
+
+	/* Enable display power gate & LOCALMEM power gate*/
+	reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
+	reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+	reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+	reg |= HIBMC_CURR_GATE_LOCALMEM(OFF);
+	reg |= HIBMC_CURR_GATE_DISPLAY(OFF);
+	hibmc_set_current_gate(hidev, reg);
+}
+
+static int hibmc_crtc_atomic_check(struct drm_crtc *crtc,
+				   struct drm_crtc_state *state)
+{
+	return 0;
+}
+
+static unsigned int format_pll_reg(void)
+{
+	unsigned int pllreg = 0;
+	struct panel_pll pll = {0};
+
+	/* Note that all PLL's have the same format. Here,
+	 * we just use Panel PLL parameter to work out the bit
+	 * fields in the register.On returning a 32 bit number, the value can
+	 * be applied to any PLL in the calling function.
+	 */
+	pllreg |= HIBMC_PLL_CTRL_BYPASS(OFF) & HIBMC_PLL_CTRL_BYPASS_MASK;
+	pllreg |= HIBMC_PLL_CTRL_POWER(ON) & HIBMC_PLL_CTRL_POWER_MASK;
+	pllreg |= HIBMC_PLL_CTRL_INPUT(OSC) & HIBMC_PLL_CTRL_INPUT_MASK;
+	pllreg |= HIBMC_PLL_CTRL_POD(pll.POD) & HIBMC_PLL_CTRL_POD_MASK;
+	pllreg |= HIBMC_PLL_CTRL_OD(pll.OD) & HIBMC_PLL_CTRL_OD_MASK;
+	pllreg |= HIBMC_PLL_CTRL_N(pll.N) & HIBMC_PLL_CTRL_N_MASK;
+	pllreg |= HIBMC_PLL_CTRL_M(pll.M) & HIBMC_PLL_CTRL_M_MASK;
+
+	return pllreg;
+}
+
+static void set_vclock_hisilicon(struct drm_device *dev, unsigned long pll)
+{
+	unsigned long tmp0, tmp1;
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	/* 1. outer_bypass_n=0 */
+	tmp0 = readl(hidev->mmio + CRT_PLL1_HS);
+	tmp0 &= 0xBFFFFFFF;
+	writel(tmp0, hidev->mmio + CRT_PLL1_HS);
+
+	/* 2. pll_pd=1?inter_bypass=1 */
+	writel(0x21000000, hidev->mmio + CRT_PLL1_HS);
+
+	/* 3. config pll */
+	writel(pll, hidev->mmio + CRT_PLL1_HS);
+
+	/* 4. delay  */
+	mdelay(1);
+
+	/* 5. pll_pd =0 */
+	tmp1 = pll & ~0x01000000;
+	writel(tmp1, hidev->mmio + CRT_PLL1_HS);
+
+	/* 6. delay  */
+	mdelay(1);
+
+	/* 7. inter_bypass=0 */
+	tmp1 &= ~0x20000000;
+	writel(tmp1, hidev->mmio + CRT_PLL1_HS);
+
+	/* 8. delay  */
+	mdelay(1);
+
+	/* 9. outer_bypass_n=1 */
+	tmp1 |= 0x40000000;
+	writel(tmp1, hidev->mmio + CRT_PLL1_HS);
+}
+
+/* This function takes care the extra registers and bit fields required to
+ *setup a mode in board.
+ *Explanation about Display Control register:
+ *FPGA only supports 7 predefined pixel clocks, and clock select is
+ *in bit 4:0 of new register 0x802a8.
+ */
+static unsigned int display_ctrl_adjust(struct drm_device *dev,
+					struct drm_display_mode *mode,
+					unsigned int ctrl)
+{
+	unsigned long x, y;
+	unsigned long pll1; /* bit[31:0] of PLL */
+	unsigned long pll2; /* bit[63:32] of PLL */
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	x = mode->hdisplay;
+	y = mode->vdisplay;
+
+	/* Hisilicon has to set up a new register for PLL control
+	 *(CRT_PLL1_HS & CRT_PLL2_HS).
+	 */
+	if (x == 800 && y == 600) {
+		pll1 = CRT_PLL1_HS_40MHZ;
+		pll2 = CRT_PLL2_HS_40MHZ;
+	} else if (x == 1024 && y == 768) {
+		pll1 = CRT_PLL1_HS_65MHZ;
+		pll2 = CRT_PLL2_HS_65MHZ;
+	} else if (x == 1152 && y == 864) {
+		pll1 = CRT_PLL1_HS_80MHZ_1152;
+		pll2 = CRT_PLL2_HS_80MHZ;
+	} else if (x == 1280 && y == 768) {
+		pll1 = CRT_PLL1_HS_80MHZ;
+		pll2 = CRT_PLL2_HS_80MHZ;
+	} else if (x == 1280 && y == 720) {
+		pll1 = CRT_PLL1_HS_74MHZ;
+		pll2 = CRT_PLL2_HS_74MHZ;
+	} else if (x == 1280 && y == 960) {
+		pll1 = CRT_PLL1_HS_108MHZ;
+		pll2 = CRT_PLL2_HS_108MHZ;
+	} else if (x == 1280 && y == 1024) {
+		pll1 = CRT_PLL1_HS_108MHZ;
+		pll2 = CRT_PLL2_HS_108MHZ;
+	} else if (x == 1600 && y == 1200) {
+		pll1 = CRT_PLL1_HS_162MHZ;
+		pll2 = CRT_PLL2_HS_162MHZ;
+	} else if (x == 1920 && y == 1080) {
+		pll1 = CRT_PLL1_HS_148MHZ;
+		pll2 = CRT_PLL2_HS_148MHZ;
+	} else if (x == 1920 && y == 1200) {
+		pll1 = CRT_PLL1_HS_193MHZ;
+		pll2 = CRT_PLL2_HS_193MHZ;
+	} else /* default to VGA clock */ {
+		pll1 = CRT_PLL1_HS_25MHZ;
+		pll2 = CRT_PLL2_HS_25MHZ;
+	}
+
+	writel(pll2, hidev->mmio + CRT_PLL2_HS);
+	set_vclock_hisilicon(dev, pll1);
+
+	/* Hisilicon has to set up the top-left and bottom-right
+	 * registers as well.
+	 * Note that normal chip only use those two register for
+	 * auto-centering mode.
+	 */
+	writel((HIBMC_CRT_AUTO_CENTERING_TL_TOP(0) &
+		HIBMC_CRT_AUTO_CENTERING_TL_TOP_MSK) |
+	       (HIBMC_CRT_AUTO_CENTERING_TL_LEFT(0) &
+		HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MSK),
+	       hidev->mmio + HIBMC_CRT_AUTO_CENTERING_TL);
+
+	writel((HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(y - 1) &
+		HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK) |
+	       (HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x - 1) &
+		HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK),
+		hidev->mmio + HIBMC_CRT_AUTO_CENTERING_BR);
+
+	/* Assume common fields in ctrl have been properly set before
+	 * calling this function.
+	 * This function only sets the extra fields in ctrl.
+	 */
+
+	/* Set bit 25 of display controller: Select CRT or VGA clock */
+	ctrl &= ~HIBMC_CRT_DISP_CTL_CRTSELECT_MASK;
+	ctrl &= ~HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK;
+
+	ctrl |= HIBMC_CRT_DISP_CTL_CRTSELECT(CRTSELECT_CRT);
+
+	/*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL, CRTSELECT, CRT);*/
+
+	/* Set bit 14 of display controller */
+	/*ctrl &= FIELD_CLEAR(HIBMC_CRT_DISP_CTL, CLOCK_PHASE);*/
+
+	/* clock_phase_polarity is 0 */
+	ctrl |= HIBMC_CRT_DISP_CTL_CLOCK_PHASE(PHASE_ACTIVE_HIGH);
+	/*ctrl = FIELD_SET(ctrl, HIBMC_CRT_DISP_CTL,*/
+	/*CLOCK_PHASE, ACTIVE_HIGH);*/
+
+	writel(ctrl, hidev->mmio + HIBMC_CRT_DISP_CTL);
+
+	return ctrl;
+}
+
+static void hibmc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	unsigned int val;
+	struct drm_display_mode *mode = &crtc->state->mode;
+	struct drm_device *dev = crtc->dev;
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	writel(format_pll_reg(), hidev->mmio + HIBMC_CRT_PLL_CTRL);
+	writel((HIBMC_CRT_HORZ_TOTAL_TOTAL(mode->htotal - 1) &
+		HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK) |
+		(HIBMC_CRT_HORZ_TOTAL_DISPLAY_END(mode->hdisplay - 1) &
+		HIBMC_CRT_HORZ_TOTAL_DISPLAY_END_MASK),
+		hidev->mmio + HIBMC_CRT_HORZ_TOTAL);
+
+	writel((HIBMC_CRT_HORZ_SYNC_WIDTH(mode->hsync_end - mode->hsync_start)
+		& HIBMC_CRT_HORZ_SYNC_WIDTH_MASK) |
+		(HIBMC_CRT_HORZ_SYNC_START(mode->hsync_start - 1)
+		& HIBMC_CRT_HORZ_SYNC_START_MASK),
+		hidev->mmio + HIBMC_CRT_HORZ_SYNC);
+
+	writel((HIBMC_CRT_VERT_TOTAL_TOTAL(mode->vtotal - 1) &
+		HIBMC_CRT_VERT_TOTAL_TOTAL_MASK) |
+		(HIBMC_CRT_VERT_TOTAL_DISPLAY_END(mode->vdisplay - 1) &
+		HIBMC_CRT_VERT_TOTAL_DISPLAY_END_MASK),
+		hidev->mmio + HIBMC_CRT_VERT_TOTAL);
+
+	writel((HIBMC_CRT_VERT_SYNC_HEIGHT(mode->vsync_end - mode->vsync_start)
+		& HIBMC_CRT_VERT_SYNC_HEIGHT_MASK) |
+	       (HIBMC_CRT_VERT_SYNC_START(mode->vsync_start - 1) &
+		HIBMC_CRT_VERT_SYNC_START_MASK),
+		hidev->mmio + HIBMC_CRT_VERT_SYNC);
+
+	val = HIBMC_CRT_DISP_CTL_VSYNC_PHASE(0) &
+	      HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK;
+	val |= HIBMC_CRT_DISP_CTL_HSYNC_PHASE(0) &
+	       HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK;
+	val |= HIBMC_CRT_DISP_CTL_TIMING(ENABLE);
+	val |= HIBMC_CRT_DISP_CTL_PLANE(ENABLE);
+
+	display_ctrl_adjust(dev, mode, val);
+}
+
+static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc,
+				    struct drm_crtc_state *old_state)
+{
+	unsigned int reg;
+	struct drm_device *dev = crtc->dev;
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	hibmc_set_power_mode(hidev, HIBMC_PW_MODE_CTL_MODE_MODE0);
+
+	/* Enable display power gate & LOCALMEM power gate*/
+	reg = readl(hidev->mmio + HIBMC_CURRENT_GATE);
+	reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK;
+	reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK;
+	reg |= HIBMC_CURR_GATE_DISPLAY(ON);
+	reg |= HIBMC_CURR_GATE_LOCALMEM(ON);
+	hibmc_set_current_gate(hidev, reg);
+
+	/* We can add more initialization as needed. */
+}
+
+static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc,
+				    struct drm_crtc_state *old_state)
+
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&crtc->dev->event_lock, flags);
+	if (crtc->state->event)
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+	crtc->state->event = NULL;
+
+	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+}
+
+/* These provide the minimum set of functions required to handle a CRTC */
+static const struct drm_crtc_funcs hibmc_crtc_funcs = {
+	.page_flip = drm_atomic_helper_page_flip,
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = drm_crtc_cleanup,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state =  drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = {
+	.enable		= hibmc_crtc_enable,
+	.disable	= hibmc_crtc_disable,
+	.mode_set_nofb	= hibmc_crtc_mode_set_nofb,
+	.atomic_check	= hibmc_crtc_atomic_check,
+	.atomic_begin	= hibmc_crtc_atomic_begin,
+	.atomic_flush	= hibmc_crtc_atomic_flush,
+};
+
+int hibmc_crtc_init(struct hibmc_drm_device *hidev)
+{
+	struct drm_device *dev = hidev->dev;
+	struct drm_crtc *crtc = &hidev->crtc;
+	struct drm_plane *plane = &hidev->plane;
+	int ret;
+
+	ret = drm_crtc_init_with_planes(dev, crtc, plane,
+					NULL, &hibmc_crtc_funcs, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init crtc.\n");
+		return ret;
+	}
+
+	drm_mode_crtc_set_gamma_size(crtc, 256);
+	drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs);
+	return 0;
+}
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 8608ac2..b6e61db 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -119,6 +119,12 @@ static int hibmc_kms_init(struct hibmc_drm_device *hidev)
 		return ret;
 	}
 
+	ret = hibmc_crtc_init(hidev);
+	if (ret) {
+		DRM_ERROR("failed to init crtc.\n");
+		return ret;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index 28a3663..3308b21 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -47,6 +47,7 @@ struct hibmc_drm_device {
 	/* drm */
 	struct drm_device  *dev;
 	struct drm_plane plane;
+	struct drm_crtc crtc;
 	bool mode_config_initialized;
 
 	/* ttm */
@@ -86,6 +87,7 @@ static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
 #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
 
 int hibmc_plane_init(struct hibmc_drm_device *hidev);
+int hibmc_crtc_init(struct hibmc_drm_device *hidev);
 int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
 void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 6/9] drm/hisilicon/hibmc: Add encoder for VDAC
From: Rongrong Zou @ 2016-10-26  2:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477449426-69018-1-git-send-email-zourongrong@gmail.com>

Add encoder funcs and helpers for VDAC.

Signed-off-by: Rongrong Zou <zourongrong@gmail.com>
---
 drivers/gpu/drm/hisilicon/hibmc/Makefile         |  2 +-
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c  |  6 ++
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h  |  2 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 89 ++++++++++++++++++++++++
 4 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c

diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index 72e107e..e04f114 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,5 +1,5 @@
 ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_fbdev.o hibmc_drm_power.o hibmc_ttm.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_fbdev.o hibmc_drm_power.o hibmc_ttm.o
 
 obj-$(CONFIG_DRM_HISI_HIBMC)	+=hibmc-drm.o
 #obj-y	+= hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index b6e61db..3fc915a 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -125,6 +125,12 @@ static int hibmc_kms_init(struct hibmc_drm_device *hidev)
 		return ret;
 	}
 
+	ret = hibmc_encoder_init(hidev);
+	if (ret) {
+		DRM_ERROR("failed to init encoder\n");
+		return ret;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index 3308b21..f005499 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -48,6 +48,7 @@ struct hibmc_drm_device {
 	struct drm_device  *dev;
 	struct drm_plane plane;
 	struct drm_crtc crtc;
+	struct drm_encoder encoder;
 	bool mode_config_initialized;
 
 	/* ttm */
@@ -88,6 +89,7 @@ static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem)
 
 int hibmc_plane_init(struct hibmc_drm_device *hidev);
 int hibmc_crtc_init(struct hibmc_drm_device *hidev);
+int hibmc_encoder_init(struct hibmc_drm_device *hidev);
 int hibmc_fbdev_init(struct hibmc_drm_device *hidev);
 void hibmc_fbdev_fini(struct hibmc_drm_device *hidev);
 
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
new file mode 100644
index 0000000..953f659
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
@@ -0,0 +1,89 @@
+/* Hisilicon Hibmc SoC drm driver
+ *
+ * Based on the bochs drm driver.
+ *
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * Author:
+ *	Rongrong Zou <zourongrong@huawei.com>
+ *	Rongrong Zou <zourongrong@gmail.com>
+ *	Jianhua Li <lijianhua@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "hibmc_drm_drv.h"
+#include "hibmc_drm_regs.h"
+
+static int defx = 800;
+static int defy = 600;
+
+module_param(defx, int, 0444);
+module_param(defy, int, 0444);
+MODULE_PARM_DESC(defx, "default x resolution");
+MODULE_PARM_DESC(defy, "default y resolution");
+
+static void hibmc_encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static void hibmc_encoder_enable(struct drm_encoder *encoder)
+{
+}
+
+static void hibmc_encoder_mode_set(struct drm_encoder *encoder,
+				   struct drm_display_mode *mode,
+				   struct drm_display_mode *adj_mode)
+{
+	u32 reg;
+	struct drm_device *dev = encoder->dev;
+	struct hibmc_drm_device *hidev = dev->dev_private;
+
+	/* just open DISPLAY_CONTROL_HISILE register bit 3:0*/
+	reg = readl(hidev->mmio + DISPLAY_CONTROL_HISILE);
+	reg |= 0xf;
+	writel(reg, hidev->mmio + DISPLAY_CONTROL_HISILE);
+}
+
+static int hibmc_encoder_atomic_check(struct drm_encoder *encoder,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	return 0;
+}
+
+static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = {
+	.mode_set = hibmc_encoder_mode_set,
+	.disable = hibmc_encoder_disable,
+	.enable = hibmc_encoder_enable,
+	.atomic_check = hibmc_encoder_atomic_check,
+};
+
+static const struct drm_encoder_funcs hibmc_encoder_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+int hibmc_encoder_init(struct hibmc_drm_device *hidev)
+{
+	struct drm_device *dev = hidev->dev;
+	struct drm_encoder *encoder = &hidev->encoder;
+	int ret;
+
+	encoder->possible_crtcs = 0x1;
+	ret = drm_encoder_init(dev, encoder, &hibmc_encoder_encoder_funcs,
+			       DRM_MODE_ENCODER_DAC, NULL);
+	if (ret) {
+		DRM_ERROR("failed to init encoder\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs);
+	return 0;
+}
-- 
1.9.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox