* [PATCH v3 4/5] ARM: dts: exynos: add support for ISP power domain to exynos4x12 clocks device
From: Marek Szyprowski @ 2016-10-24 12:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477311130-6534-1-git-send-email-m.szyprowski@samsung.com>
Exynos4412 clock controller contains some additional clocks for FIMC-ISP
(Camera ISP) subsystem. Registers for those clocks are partially located
in the SOC area, which belongs to ISP power domain.
This patch implements integration of ISP clocks with ISP power domain.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
arch/arm/boot/dts/exynos4x12.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi
index 0074f566cd3b..5667fd33dc98 100644
--- a/arch/arm/boot/dts/exynos4x12.dtsi
+++ b/arch/arm/boot/dts/exynos4x12.dtsi
@@ -74,6 +74,11 @@
compatible = "samsung,exynos4412-clock";
reg = <0x10030000 0x20000>;
#clock-cells = <1>;
+
+ isp-clock-controller {
+ compatible = "samsung,exynos4412-isp-clock";
+ power-domains = <&pd_isp>;
+ };
};
mct at 10050000 {
--
1.9.1
^ permalink raw reply related
* [PATCH v3 5/5] clocks: exynos5433: add runtime pm support
From: Marek Szyprowski @ 2016-10-24 12:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477311130-6534-1-git-send-email-m.szyprowski@samsung.com>
Add runtime pm support for all clock controller units (CMU), which belongs
to power domains and require special handling during on/off operations.
Typically special values has to be written to MUX registers to change
internal clocks parents to OSC clock before turning power off. During such
operation all clocks, which enters CMU has to be enabled to let MUX to
stabilize. Also for each CMU there is one special parent clock, which has
to be enabled all the time when any access to CMU registers is done.
This patch solves most of the mysterious external abort and freeze issues
caused by a lack of proper parent CMU clock enabled or incorrect turn off
procedure.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
drivers/clk/samsung/clk-exynos5433.c | 390 ++++++++++++++++++++++++++++-------
drivers/clk/samsung/clk.h | 6 +
2 files changed, 319 insertions(+), 77 deletions(-)
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index ea1608682d7f..40531652a5a6 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,14 @@
* Common Clock Framework support for Exynos5443 SoC.
*/
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#include <dt-bindings/clock/exynos5433.h>
@@ -2333,6 +2338,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
};
+static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
+ { MUX_SEL_G2D0, 0 },
+};
+
/* list of all parent clock list */
PNAME(mout_aclk_g2d_266_user_p) = { "oscclk", "aclk_g2d_266", };
PNAME(mout_aclk_g2d_400_user_p) = { "oscclk", "aclk_g2d_400", };
@@ -2418,16 +2427,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
.nr_clk_ids = G2D_NR_CLK,
.clk_regs = g2d_clk_regs,
.nr_clk_regs = ARRAY_SIZE(g2d_clk_regs),
+ .suspend_regs = g2d_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(g2d_suspend_regs),
+ .clk_name = "aclk_g2d_400",
};
-static void __init exynos5433_cmu_g2d_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &g2d_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
- exynos5433_cmu_g2d_init);
-
/*
* Register offset definitions for CMU_DISP
*/
@@ -2492,6 +2496,14 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
CLKOUT_CMU_DISP_DIV_STAT,
};
+static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
+ { MUX_SEL_DISP0, 0 },
+ { MUX_SEL_DISP1, 0 },
+ { MUX_SEL_DISP2, 0 },
+ { MUX_SEL_DISP3, 0 },
+ { MUX_SEL_DISP4, 0 },
+};
+
/* list of all parent clock list */
PNAME(mout_disp_pll_p) = { "oscclk", "fout_disp_pll", };
PNAME(mout_sclk_dsim1_user_p) = { "oscclk", "sclk_dsim1_disp", };
@@ -2837,16 +2849,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
.nr_clk_ids = DISP_NR_CLK,
.clk_regs = disp_clk_regs,
.nr_clk_regs = ARRAY_SIZE(disp_clk_regs),
+ .suspend_regs = disp_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(disp_suspend_regs),
+ .clk_name = "aclk_disp_333",
};
-static void __init exynos5433_cmu_disp_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &disp_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
- exynos5433_cmu_disp_init);
-
/*
* Register offset definitions for CMU_AUD
*/
@@ -2881,6 +2888,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
ENABLE_IP_AUD1,
};
+static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
+ { MUX_SEL_AUD0, 0 },
+ { MUX_SEL_AUD1, 0 },
+};
+
/* list of all parent clock list */
PNAME(mout_aud_pll_user_aud_p) = { "oscclk", "fout_aud_pll", };
PNAME(mout_sclk_aud_pcm_p) = { "mout_aud_pll_user", "ioclk_audiocdclk0",};
@@ -3007,16 +3019,10 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
.nr_clk_ids = AUD_NR_CLK,
.clk_regs = aud_clk_regs,
.nr_clk_regs = ARRAY_SIZE(aud_clk_regs),
+ .suspend_regs = aud_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(aud_suspend_regs),
};
-static void __init exynos5433_cmu_aud_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &aud_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
- exynos5433_cmu_aud_init);
-
-
/*
* Register offset definitions for CMU_BUS{0|1|2}
*/
@@ -3218,6 +3224,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
CLK_STOPCTRL,
};
+static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
+ { MUX_SEL_G3D, 0 },
+};
+
/* list of all parent clock list */
PNAME(mout_aclk_g3d_400_p) = { "mout_g3d_pll", "aclk_g3d_400", };
PNAME(mout_g3d_pll_p) = { "oscclk", "fout_g3d_pll", };
@@ -3291,15 +3301,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
.nr_clk_ids = G3D_NR_CLK,
.clk_regs = g3d_clk_regs,
.nr_clk_regs = ARRAY_SIZE(g3d_clk_regs),
+ .suspend_regs = g3d_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(g3d_suspend_regs),
+ .clk_name = "aclk_g3d_400",
};
-static void __init exynos5433_cmu_g3d_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &g3d_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
- exynos5433_cmu_g3d_init);
-
/*
* Register offset definitions for CMU_GSCL
*/
@@ -3338,6 +3344,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
};
+static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
+ { MUX_SEL_GSCL, 0 },
+ { ENABLE_ACLK_GSCL, 0xfff },
+ { ENABLE_PCLK_GSCL, 0xff },
+};
+
/* list of all parent clock list */
PNAME(aclk_gscl_111_user_p) = { "oscclk", "aclk_gscl_111", };
PNAME(aclk_gscl_333_user_p) = { "oscclk", "aclk_gscl_333", };
@@ -3432,15 +3444,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
.nr_clk_ids = GSCL_NR_CLK,
.clk_regs = gscl_clk_regs,
.nr_clk_regs = ARRAY_SIZE(gscl_clk_regs),
+ .suspend_regs = gscl_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(gscl_suspend_regs),
+ .clk_name = "aclk_gscl_111",
};
-static void __init exynos5433_cmu_gscl_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &gscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
- exynos5433_cmu_gscl_init);
-
/*
* Register offset definitions for CMU_APOLLO
*/
@@ -3966,6 +3974,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
};
+static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
+ { MUX_SEL_MSCL0, 0 },
+ { MUX_SEL_MSCL1, 0 },
+};
+
/* list of all parent clock list */
PNAME(mout_sclk_jpeg_user_p) = { "oscclk", "sclk_jpeg_mscl", };
PNAME(mout_aclk_mscl_400_user_p) = { "oscclk", "aclk_mscl_400", };
@@ -4078,15 +4091,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
.nr_clk_ids = MSCL_NR_CLK,
.clk_regs = mscl_clk_regs,
.nr_clk_regs = ARRAY_SIZE(mscl_clk_regs),
+ .suspend_regs = mscl_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(mscl_suspend_regs),
+ .clk_name = "aclk_mscl_400",
};
-static void __init exynos5433_cmu_mscl_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &mscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
- exynos5433_cmu_mscl_init);
-
/*
* Register offset definitions for CMU_MFC
*/
@@ -4116,6 +4125,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
ENABLE_IP_MFC_SECURE_SMMU_MFC,
};
+static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
+ { MUX_SEL_MFC, 0 },
+};
+
PNAME(mout_aclk_mfc_400_user_p) = { "oscclk", "aclk_mfc_400", };
static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
@@ -4186,15 +4199,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
.nr_clk_ids = MFC_NR_CLK,
.clk_regs = mfc_clk_regs,
.nr_clk_regs = ARRAY_SIZE(mfc_clk_regs),
+ .suspend_regs = mfc_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(mfc_suspend_regs),
+ .clk_name = "aclk_mfc_400",
};
-static void __init exynos5433_cmu_mfc_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &mfc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
- exynos5433_cmu_mfc_init);
-
/*
* Register offset definitions for CMU_HEVC
*/
@@ -4224,6 +4233,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
};
+static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
+ { MUX_SEL_HEVC, 0 },
+};
+
PNAME(mout_aclk_hevc_400_user_p) = { "oscclk", "aclk_hevc_400", };
static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
@@ -4296,15 +4309,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
.nr_clk_ids = HEVC_NR_CLK,
.clk_regs = hevc_clk_regs,
.nr_clk_regs = ARRAY_SIZE(hevc_clk_regs),
+ .suspend_regs = hevc_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(hevc_suspend_regs),
+ .clk_name = "aclk_hevc_400",
};
-static void __init exynos5433_cmu_hevc_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &hevc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
- exynos5433_cmu_hevc_init);
-
/*
* Register offset definitions for CMU_ISP
*/
@@ -4338,6 +4347,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
ENABLE_IP_ISP3,
};
+static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
+ { MUX_SEL_ISP, 0 },
+};
+
PNAME(mout_aclk_isp_dis_400_user_p) = { "oscclk", "aclk_isp_dis_400", };
PNAME(mout_aclk_isp_400_user_p) = { "oscclk", "aclk_isp_400", };
@@ -4549,15 +4562,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
.nr_clk_ids = ISP_NR_CLK,
.clk_regs = isp_clk_regs,
.nr_clk_regs = ARRAY_SIZE(isp_clk_regs),
+ .suspend_regs = isp_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(isp_suspend_regs),
+ .clk_name = "aclk_isp_400",
};
-static void __init exynos5433_cmu_isp_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &isp_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
- exynos5433_cmu_isp_init);
-
/*
* Register offset definitions for CMU_CAM0
*/
@@ -4621,6 +4630,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
ENABLE_IP_CAM02,
ENABLE_IP_CAM03,
};
+
+static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
+ { MUX_SEL_CAM00, 0 },
+ { MUX_SEL_CAM01, 0 },
+ { MUX_SEL_CAM02, 0 },
+ { MUX_SEL_CAM03, 0 },
+ { MUX_SEL_CAM04, 0 },
+};
+
PNAME(mout_aclk_cam0_333_user_p) = { "oscclk", "aclk_cam0_333", };
PNAME(mout_aclk_cam0_400_user_p) = { "oscclk", "aclk_cam0_400", };
PNAME(mout_aclk_cam0_552_user_p) = { "oscclk", "aclk_cam0_552", };
@@ -5026,15 +5044,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
.nr_clk_ids = CAM0_NR_CLK,
.clk_regs = cam0_clk_regs,
.nr_clk_regs = ARRAY_SIZE(cam0_clk_regs),
+ .suspend_regs = cam0_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(cam0_suspend_regs),
+ .clk_name = "aclk_cam0_400",
};
-static void __init exynos5433_cmu_cam0_init(struct device_node *np)
-{
- samsung_cmu_register_one(np, &cam0_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
- exynos5433_cmu_cam0_init);
-
/*
* Register offset definitions for CMU_CAM1
*/
@@ -5081,6 +5095,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
ENABLE_IP_CAM12,
};
+static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
+ { MUX_SEL_CAM10, 0 },
+ { MUX_SEL_CAM11, 0 },
+ { MUX_SEL_CAM12, 0 },
+};
+
PNAME(mout_sclk_isp_uart_user_p) = { "oscclk", "sclk_isp_uart_cam1", };
PNAME(mout_sclk_isp_spi1_user_p) = { "oscclk", "sclk_isp_spi1_cam1", };
PNAME(mout_sclk_isp_spi0_user_p) = { "oscclk", "sclk_isp_spi0_cam1", };
@@ -5399,11 +5419,227 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
.nr_clk_ids = CAM1_NR_CLK,
.clk_regs = cam1_clk_regs,
.nr_clk_regs = ARRAY_SIZE(cam1_clk_regs),
+ .suspend_regs = cam1_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(cam1_suspend_regs),
+ .clk_name = "aclk_cam1_400",
+};
+
+
+struct exynos5433_cmu_data {
+ struct samsung_clk_provider ctx;
+
+ struct samsung_clk_reg_dump *clk_save;
+ unsigned int nr_clk_save;
+ const struct samsung_clk_reg_dump *clk_suspend;
+ unsigned int nr_clk_suspend;
+
+ struct clk *clk;
+ struct clk **pclks;
+ int nr_pclks;
+};
+
+static int exynos5433_cmu_suspend(struct device *dev)
+{
+ struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+ int i;
+
+ samsung_clk_save(data->ctx.reg_base, data->clk_save,
+ data->nr_clk_save);
+
+ for (i = 0; i < data->nr_pclks; i++)
+ clk_enable(data->pclks[i]);
+
+ samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
+ data->nr_clk_suspend);
+
+ for (i = 0; i < data->nr_pclks; i++)
+ clk_disable(data->pclks[i]);
+
+ clk_disable(data->clk);
+
+ return 0;
+}
+
+static int exynos5433_cmu_resume(struct device *dev)
+{
+ struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+ int i;
+
+ clk_enable(data->clk);
+
+ for (i = 0; i < data->nr_pclks; i++)
+ clk_enable(data->pclks[i]);
+
+ samsung_clk_restore(data->ctx.reg_base, data->clk_save,
+ data->nr_clk_save);
+
+ for (i = 0; i < data->nr_pclks; i++)
+ clk_disable(data->pclks[i]);
+
+ return 0;
+}
+
+static int __init exynos5433_cmu_probe(struct platform_device *pdev)
+{
+ const struct samsung_cmu_info *info;
+ struct exynos5433_cmu_data *data;
+ struct samsung_clk_provider *ctx;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *reg_base;
+ struct clk **clk_table;
+ int i;
+
+ info = of_device_get_match_data(dev);
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ ctx = &data->ctx;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg_base = devm_ioremap_resource(dev, res);
+ if (!reg_base) {
+ dev_err(dev, "failed to map registers\n");
+ return -ENOMEM;
+ }
+
+ clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!clk_table)
+ return -ENOMEM;
+
+ for (i = 0; i < info->nr_clk_ids; ++i)
+ clk_table[i] = ERR_PTR(-ENOENT);
+
+ ctx->clk_data.clks = clk_table;
+ ctx->clk_data.clk_num = info->nr_clk_ids;
+ ctx->reg_base = reg_base;
+ ctx->dev = dev;
+ spin_lock_init(&ctx->lock);
+
+ data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
+ info->nr_clk_regs);
+ data->nr_clk_save = info->nr_clk_regs;
+ data->clk_suspend = info->suspend_regs;
+ data->nr_clk_suspend = info->nr_suspend_regs;
+ data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
+ "#clock-cells");
+ if (data->nr_pclks > 0) {
+ data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
+ data->nr_pclks, GFP_KERNEL);
+
+ for (i = 0; i < data->nr_pclks; i++) {
+ struct clk *clk = of_clk_get(dev->of_node, i);
+
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ data->pclks[i] = clk;
+ }
+ }
+
+ /*
+ * Prepare all parent clocks here to avoid potential deadlock caused
+ * by global clock "prepare lock" grabbed by runtime pm callbacks
+ * from pm workers.
+ */
+ for (i = 0; i < data->nr_pclks; i++)
+ clk_prepare(data->pclks[i]);
+
+ if (info->clk_name)
+ data->clk = clk_get(dev, info->clk_name);
+ clk_prepare_enable(data->clk);
+
+ platform_set_drvdata(pdev, data);
+
+ /*
+ * Enable runtime pm here, so clock core with use runtime pm for all
+ * registered clocks.
+ */
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ if (info->pll_clks)
+ samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
+ reg_base);
+ if (info->mux_clks)
+ samsung_clk_register_mux(ctx, info->mux_clks,
+ info->nr_mux_clks);
+ if (info->div_clks)
+ samsung_clk_register_div(ctx, info->div_clks,
+ info->nr_div_clks);
+ if (info->gate_clks)
+ samsung_clk_register_gate(ctx, info->gate_clks,
+ info->nr_gate_clks);
+ if (info->fixed_clks)
+ samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
+ info->nr_fixed_clks);
+ if (info->fixed_factor_clks)
+ samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
+ info->nr_fixed_factor_clks);
+
+ samsung_clk_of_add_provider(dev->of_node, ctx);
+
+ return 0;
+}
+
+static const struct of_device_id exynos5433_cmu_of_match[] = {
+ {
+ .compatible = "samsung,exynos5433-cmu-aud",
+ .data = &aud_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-cam0",
+ .data = &cam0_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-cam1",
+ .data = &cam1_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-disp",
+ .data = &disp_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-g2d",
+ .data = &g2d_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-g3d",
+ .data = &g3d_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-gscl",
+ .data = &gscl_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-mfc",
+ .data = &mfc_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-hevc",
+ .data = &hevc_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-isp",
+ .data = &isp_cmu_info,
+ }, {
+ .compatible = "samsung,exynos5433-cmu-mscl",
+ .data = &mscl_cmu_info,
+ }, {
+ },
+};
+
+static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
+ SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
+ NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver exynos5433_cmu_driver __refdata = {
+ .driver = {
+ .name = "exynos5433-cmu",
+ .of_match_table = exynos5433_cmu_of_match,
+ .suppress_bind_attrs = true,
+ .pm = &exynos5433_cmu_pm_ops,
+ },
+ .probe = exynos5433_cmu_probe,
};
-static void __init exynos5433_cmu_cam1_init(struct device_node *np)
+static int __init exynos5433_cmu_init(void)
{
- samsung_cmu_register_one(np, &cam1_cmu_info);
+ return platform_driver_register(&exynos5433_cmu_driver);
}
-CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
- exynos5433_cmu_cam1_init);
+core_initcall(exynos5433_cmu_init);
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 9263d8a27c6b..664020cb4794 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -354,6 +354,12 @@ struct samsung_cmu_info {
/* list and number of clocks registers */
const unsigned long *clk_regs;
unsigned int nr_clk_regs;
+
+ /* list and number of clocks registers to set before suspend */
+ const struct samsung_clk_reg_dump *suspend_regs;
+ unsigned int nr_suspend_regs;
+ /* name of the parent clock needed for CMU register access */
+ const char *clk_name;
};
extern struct samsung_clk_provider *__init samsung_clk_init(
--
1.9.1
^ permalink raw reply related
* [PATCH v4 1/2] ARM: dts: imx6ul: Add DTS for liteSOM module
From: Shawn Guo @ 2016-10-24 12:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161021150717.27573-1-m.niestroj@grinn-global.com>
On Fri, Oct 21, 2016 at 05:07:16PM +0200, Marcin Niestroj wrote:
> This is a SOM (System on Module), so it will be part of another boards.
> Hence, this is a "dtsi" file that will be included from another device
> tree files.
>
> Hardware specification:
> * Freescale i.MX6UL SoC
> * up to 512 MB RAM
> * eMMC on uSDHC2
>
> Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
> Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
Applied both, thanks.
^ permalink raw reply
* [PATCH v5 02/23] of: device: Export of_device_{get_modalias, uvent_modalias} to modules
From: Chen-Yu Tsai @ 2016-10-24 12:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161018015636.11701-3-stephen.boyd@linaro.org>
Hi,
On Tue, Oct 18, 2016 at 9:56 AM, Stephen Boyd <stephen.boyd@linaro.org> wrote:
> The ULPI bus can be built as a module, and it will soon be
> calling these functions when it supports probing devices from DT.
> Export them so they can be used by the ULPI module.
>
> Acked-by: Rob Herring <robh@kernel.org>
> Cc: <devicetree@vger.kernel.org>
> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
> ---
> drivers/of/device.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/of/device.c b/drivers/of/device.c
> index 8a22a253a830..6719ab35b62e 100644
> --- a/drivers/of/device.c
> +++ b/drivers/of/device.c
> @@ -225,6 +225,7 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
>
> return tsize;
> }
> +EXPORT_SYMBOL_GPL(of_device_get_modalias);
>
> int of_device_request_module(struct device *dev)
> {
> @@ -290,6 +291,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
> }
> mutex_unlock(&of_mutex);
> }
> +EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
This is trailing the wrong function.
ChenYu
>
> int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
> {
> --
> 2.10.0.297.gf6727b0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] usb: gadget: udc: atmel: fix endpoint name
From: Alexandre Belloni @ 2016-10-24 12:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <935cce30-cf9d-4605-1f3e-ac8e934993e6@atmel.com>
On 24/10/2016 at 13:59:47 +0200, Nicolas Ferre wrote :
> > Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com>
> >
> > If you prefer to pick it up as a pull request, I already have the patch
> > in my 'fixes' branch, just need to tag it and send it to you.
>
> Felipe,
>
> We agreed to delay this patch after the merge window is closed. But I
> feel that it's beginning to be urgent to make this patch go forward:
> actual users of our kernel are facing the issue:
>
> https://lkml.org/lkml/2016/10/17/493
>
> We're already at -rc2 and I don't see USB-fixes included...
>
It is in Greg's branch:
http://git.kernel.org/cgit/linux/kernel/git/gregkh/usb.git/log/?h=usb-linus
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [PATCH V3 0/8] IOMMU probe deferral support
From: Sricharan @ 2016-10-24 12:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <b9e4e81f-3b3e-951f-df62-d640275aae71@samsung.com>
Hi Marek,
>>>> Initial post from Laurent Pinchart[1]. This is
>>>> series calls the dma ops configuration for the devices
>>>> at a generic place so that it works for all busses.
>>>> The dma_configure_ops for a device is now called during
>>>> the device_attach callback just before the probe of the
>>>> bus/driver is called. Similarly dma_deconfigure is called during
>>>> device/driver_detach path.
>>>>
>>>>
>>>> pci_bus_add_devices (platform/amba)(_device_create/driver_register)
>>>> | |
>>>> pci_bus_add_device (device_add/driver_register)
>>>> | |
>>>> device_attach device_initial_probe
>>>> | |
>>>> __device_attach_driver __device_attach_driver
>>>> |
>>>> driver_probe_device
>>>> |
>>>> really_probe
>>>> |
>>>> dma_configure
>>>>
>>>> Similarly on the device/driver_unregister path __device_release_driver is
>>>> called which inturn calls dma_deconfigure.
>>>>
>>>> If the ACPI bus code follows the same, we can add acpi_dma_configure
>>>> at the same place as of_dma_configure.
>>>>
>>>> This series is based on the recently merged Generic DT bindings for
>>>> PCI IOMMUs and ARM SMMU from Robin Murphy robin.murphy at arm.com [2]
>>>>
>>>> This time tested this with platform and pci device for probe deferral
>>>> and reprobe on arm64 based platform. There is an issue on the cleanup
>>>> path for arm64 though, where there is WARN_ON if the dma_ops is reset while
>>>> device is attached to an domain in arch_teardown_dma_ops.
>>>> But with iommu_groups created from the iommu driver, the device is always
>>>> attached to a domain/default_domain. So so the WARN has to be removed/handled
>>>> probably.
>>> Thanks for continuing work on this feature! Your can add my:
>>>
>>> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
>>>
>> Thanks for testing this. So the for the below fix, the remove_device callback
>> gets called on the dma_ops cleanup path, so would it be easy to remove the
>> data for the device there ?
>
>I assumed that IOMMU driver cannot be removed reliably, so all
>structures that it
>creates are permanent. I didn't use device_add()/device_remove()
>callbacks, because
>in current implementation device_add() is called too late (after
>dma-mapping glue
>triggers device_attach_iommu()).
>
>Maybe once your patchset is merged, I will move creation and management
>of the all
>IOMMU related structures to device_add/remove callbacks.
>
ok understand it.
Regards,
Sricharan
^ permalink raw reply
* [PATCH] ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031
From: Fabio Estevam @ 2016-10-24 12:32 UTC (permalink / raw)
To: linux-arm-kernel
From: Fabio Estevam <fabio.estevam@nxp.com>
AR8031 and AR8035 have the same PHY ID mask of 0xffffffef.
So fix it and make it match with the PHY ID mask definition
at drivers/net/phy/at803x.c.
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
---
arch/arm/mach-imx/mach-imx6q.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 97fd251..45801b2 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -173,7 +173,7 @@ static void __init imx6q_enet_phy_init(void)
ksz9021rn_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_KSZ9031, MICREL_PHY_ID_MASK,
ksz9031rn_phy_fixup);
- phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff,
+ phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffef,
ar8031_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_AR8035, 0xffffffef,
ar8035_phy_fixup);
--
2.7.4
^ permalink raw reply related
* [linux-sunxi] [PATCH v5 4/7] ASoC: sunxi: Add sun8i I2S driver
From: Maxime Ripard @ 2016-10-24 12:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023094503.fbb22dd0701e3f2785828609@free.fr>
Hi,
On Sun, Oct 23, 2016 at 09:45:03AM +0200, Jean-Francois Moine wrote:
> On Sun, 23 Oct 2016 09:33:16 +0800
> Chen-Yu Tsai <wens@csie.org> wrote:
>
> > > Note: This driver is closed to the sun4i-i2s except that:
> > > - it handles the H3
> >
> > If it's close to sun4i-i2s, you should probably rework that one to support
> > the newer SoCs.
> >
> > > - it creates the sound card (with sun4i-i2s, the sound card is created
> > > by the CODECs)
> >
> > I think this is wrong. I2S is only the DAI. You typically have a separate
> > platform driver for the whole card, or just use simple-card.
>
> An other device is not needed. The layout is simple:
> I2S_controller (CPU DAI) <-> HDMI_CODEC (CODEC DAI)
> The HDMI CODEC is supported by the HDMI video driver (only one device),
> so, it cannot be the card device.
> ASoC does not use the CPU DAI device (I2S_controller), so, it is
> natural to use it to handle the card.
Still, duplicating the driver is not the solution. I agree with
Chen-Yu that we want to leverage the driver that is already there.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/953e2f06/attachment.sig>
^ permalink raw reply
* [PATCH 1/3] arm64: arch_timer: Add device tree binding for hisilicon-161x01 erratum
From: Ding Tianhong @ 2016-10-24 12:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161024111608.GG15620@leverpostej>
On 2016/10/24 19:16, Mark Rutland wrote:
> On Sun, Oct 23, 2016 at 11:21:10AM +0800, Ding Tianhong wrote:
>> +- hisilicon,erratum-161x01 : A boolean property. Indicates the presence of
>> + QorIQ erratum 161201, which says that reading the counter is
>> + unreliable unless the small range of value is returned by back-to-back reads.
>> + This also affects writes to the tval register, due to the implicit
>> + counter read.
>
> Is "161x01" the *exact* erratum number, or is the 'x' a wildcard? Please
> use the *exact* erratum number, even if that means we have to list
> several.
>
Hi Mark:
The 'x' is a wildcard, it will cover 161001 to 161601 several numbers, I will discuss to
the chip develop and get a exact erratum number.
Thanks.
Ding
> Is "161x01" an *erratum* number, or the *part* number of affected
> devices?
>
> Thanks,
> Mark.
>
> .
>
^ permalink raw reply
* [PATCH v3 3/3] mtd: s3c2410: parse the device configuration from OF node
From: Boris Brezillon @ 2016-10-24 13:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476999766-32526-4-git-send-email-sergio.prado@e-labworks.com>
On Thu, 20 Oct 2016 19:42:46 -0200
Sergio Prado <sergio.prado@e-labworks.com> wrote:
> Allows configuring Samsung's s3c2410 memory controller using a
> devicetree.
>
> Signed-off-by: Sergio Prado <sergio.prado@e-labworks.com>
> ---
> drivers/mtd/nand/s3c2410.c | 158 ++++++++++++++++++++++---
> include/linux/platform_data/mtd-nand-s3c2410.h | 1 +
> 2 files changed, 143 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
> index 371db0d48135..ec170be881bc 100644
> --- a/drivers/mtd/nand/s3c2410.c
> +++ b/drivers/mtd/nand/s3c2410.c
> @@ -39,6 +39,8 @@
> #include <linux/slab.h>
> #include <linux/clk.h>
> #include <linux/cpufreq.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
>
> #include <linux/mtd/mtd.h>
> #include <linux/mtd/nand.h>
> @@ -185,6 +187,22 @@ struct s3c2410_nand_info {
> #endif
> };
>
> +struct s3c24XX_nand_devtype_data {
> + enum s3c_cpu_type type;
> +};
> +
> +static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = {
> + .type = TYPE_S3C2410,
> +};
> +
> +static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = {
> + .type = TYPE_S3C2412,
> +};
> +
> +static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = {
> + .type = TYPE_S3C2440,
> +};
> +
> /* conversion functions */
>
> static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
> @@ -794,6 +812,30 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
> return -ENODEV;
> }
>
> +static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd,
> + const struct nand_data_interface *conf,
> + bool check_only)
> +{
> + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
> + struct s3c2410_platform_nand *pdata = info->platform;
> + const struct nand_sdr_timings *timings;
> + int tacls;
> +
> + timings = nand_get_sdr_timings(conf);
> + if (IS_ERR(timings))
> + return -ENOTSUPP;
> +
> + tacls = timings->tCLS_min - timings->tWP_min;
> + if (tacls < 0)
> + tacls = 0;
> +
> + pdata->tacls = DIV_ROUND_UP(tacls, 1000);
> + pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000);
> + pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000);
You seem to only apply the timings in s3c2410_nand_setrate(), which is
only called at probe time or on a cpufreq even, but the core can change
timings at runtime (this is what happens each time you reset the chip).
To support that you have 2 options:
- apply the timings in ->select_chip()
- apply the timings here
> +
> + return 0;
> +}
> +
> /**
> * s3c2410_nand_init_chip - initialise a single instance of an chip
> * @info: The base NAND controller the chip is on.
> @@ -808,9 +850,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
> struct s3c2410_nand_mtd *nmtd,
> struct s3c2410_nand_set *set)
> {
> + struct device_node *np = info->device->of_node;
> struct nand_chip *chip = &nmtd->chip;
> void __iomem *regs = info->regs;
>
> + nand_set_flash_node(chip, set->of_node);
> +
> chip->write_buf = s3c2410_nand_write_buf;
> chip->read_buf = s3c2410_nand_read_buf;
> chip->select_chip = s3c2410_nand_select_chip;
> @@ -819,6 +864,13 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
> chip->options = set->options;
> chip->controller = &info->controller;
>
> + /*
> + * let's keep behavior unchanged for legacy boards booting via pdata and
> + * auto-detect timings only when booting with a device tree.
> + */
> + if (np)
> + chip->setup_data_interface = s3c2410_nand_setup_data_interface;
> +
> switch (info->cpu_type) {
> case TYPE_S3C2410:
> chip->IO_ADDR_W = regs + S3C2410_NFDATA;
> @@ -859,12 +911,9 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
> chip->ecc.mode = info->platform->ecc_mode;
>
> /* If you use u-boot BBT creation code, specifying this flag will
> - * let the kernel fish out the BBT from the NAND, and also skip the
> - * full NAND scan that can take 1/2s or so. Little things... */
> - if (set->flash_bbt) {
> + * let the kernel fish out the BBT from the NAND */
> + if (set->flash_bbt)
> chip->bbt_options |= NAND_BBT_USE_FLASH;
> - chip->options |= NAND_SKIP_BBTSCAN;
> - }
> }
>
> /**
> @@ -943,6 +992,77 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
> return -EINVAL;
> }
>
> + if (chip->bbt_options & NAND_BBT_USE_FLASH)
> + chip->options |= NAND_SKIP_BBTSCAN;
> +
> + return 0;
> +}
> +
> +static const struct of_device_id s3c24xx_nand_dt_ids[] = {
> + {
> + .compatible = "samsung,s3c2410-nand",
> + .data = &s3c2410_nand_devtype_data,
> + }, {
> + .compatible = "samsung,s3c2412-nand", /* also compatible with s3c6400 */
> + .data = &s3c2412_nand_devtype_data,
> + }, {
> + .compatible = "samsung,s3c2440-nand",
> + .data = &s3c2440_nand_devtype_data,
> + },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids);
> +
> +static int s3c24xx_nand_probe_dt(struct platform_device *pdev)
> +{
> + const struct s3c24XX_nand_devtype_data *devtype_data;
> + struct s3c2410_platform_nand *pdata;
> + struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
> + struct device_node *np = pdev->dev.of_node, *child;
> + struct s3c2410_nand_set *sets;
> +
> + devtype_data = of_device_get_match_data(&pdev->dev);
> + if (!devtype_data)
> + return -ENODEV;
> +
> + info->cpu_type = devtype_data->type;
> +
> + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
> + if (!pdata)
> + return -ENOMEM;
> +
> + pdev->dev.platform_data = pdata;
> +
> + pdata->nr_sets = of_get_child_count(np);
> + if (!pdata->nr_sets)
> + return 0;
> +
> + sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets, GFP_KERNEL);
> + if (!sets)
> + return -ENOMEM;
> +
> + pdata->sets = sets;
> +
> + for_each_available_child_of_node(np, child) {
> +
> + sets->name = (char *)child->name;
> + sets->of_node = child;
> + sets->nr_chips = 1;
> +
> + of_node_get(child);
> +
> + sets++;
> + }
> +
> + return 0;
> +}
> +
> +static int s3c24xx_nand_probe_pdata(struct platform_device *pdev)
> +{
> + struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
> +
> + info->cpu_type = platform_get_device_id(pdev)->driver_data;
> +
> return 0;
> }
>
> @@ -955,8 +1075,7 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
> */
> static int s3c24xx_nand_probe(struct platform_device *pdev)
> {
> - struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
> - enum s3c_cpu_type cpu_type;
> + struct s3c2410_platform_nand *plat;
> struct s3c2410_nand_info *info;
> struct s3c2410_nand_mtd *nmtd;
> struct s3c2410_nand_set *sets;
> @@ -966,8 +1085,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
> int nr_sets;
> int setno;
>
> - cpu_type = platform_get_device_id(pdev)->driver_data;
> -
> info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
> if (info == NULL) {
> err = -ENOMEM;
> @@ -989,6 +1106,16 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
>
> s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
>
> + if (pdev->dev.of_node)
> + err = s3c24xx_nand_probe_dt(pdev);
> + else
> + err = s3c24xx_nand_probe_pdata(pdev);
> +
> + if (err)
> + goto exit_error;
> +
> + plat = to_nand_plat(pdev);
> +
> /* allocate and map the resource */
>
> /* currently we assume we have the one resource */
> @@ -997,7 +1124,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
>
> info->device = &pdev->dev;
> info->platform = plat;
> - info->cpu_type = cpu_type;
>
> info->regs = devm_ioremap_resource(&pdev->dev, res);
> if (IS_ERR(info->regs)) {
> @@ -1007,12 +1133,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
>
> dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
>
> - /* initialise the hardware */
> -
> - err = s3c2410_nand_inithw(info);
> - if (err != 0)
> - goto exit_error;
> -
> sets = (plat != NULL) ? plat->sets : NULL;
> nr_sets = (plat != NULL) ? plat->nr_sets : 1;
>
> @@ -1056,6 +1176,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
> sets++;
> }
>
> + /* initialise the hardware */
> + err = s3c2410_nand_inithw(info);
> + if (err != 0)
> + goto exit_error;
> +
> err = s3c2410_nand_cpufreq_register(info);
> if (err < 0) {
> dev_err(&pdev->dev, "failed to init cpufreq support\n");
> @@ -1156,6 +1281,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
> .id_table = s3c24xx_driver_ids,
> .driver = {
> .name = "s3c24xx-nand",
> + .of_match_table = s3c24xx_nand_dt_ids,
> },
> };
>
> diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h
> index 729af13d1773..f01659026b26 100644
> --- a/include/linux/platform_data/mtd-nand-s3c2410.h
> +++ b/include/linux/platform_data/mtd-nand-s3c2410.h
> @@ -40,6 +40,7 @@ struct s3c2410_nand_set {
> char *name;
> int *nr_map;
> struct mtd_partition *partitions;
> + struct device_node *of_node;
> };
>
> struct s3c2410_platform_nand {
^ permalink raw reply
* [PATCH 0/7] ARM: dts: support I2SE Duckbill device
From: Shawn Guo @ 2016-10-24 13:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477164150-13553-1-git-send-email-mhei@heimpold.de>
On Sat, Oct 22, 2016 at 09:22:23PM +0200, Michael Heimpold wrote:
> This series updates/adds Device Tree support for I2SE's Duckbill
> device family.
>
> The Duckbill devices are small, pen-drive sized boards based on
> NXP's i.MX28 SoC. While the initial variants (Duckbill series) were
> equipped with a micro SD card slot only, the latest generation
> (Duckbill 2 series) have an additional internal eMMC onboard.
>
> Both device generations consists of four "family members":
>
> - Duckbill/Duckbill 2: generic board, intented to be used as
> baseboard for custom designs and/or as development board
>
> - Duckbill EnOcean/Duckbill 2 EnOcean: come with an EnOcean
> daugther board equipped with the popular TCM310 module
>
> - Duckbill 485/Duckbill 2 485: as the name implies, these
> devices are intended to be used as Ethernet - RS485 converters
>
> - Duckbill SPI/Duckbill 2 SPI: not sold separately, but used
> in I2SE's development kits for Green PHY HomePlug Powerline
> communication
So we basically need to maintain 8 dts/dtb files for imx28-duckbill
board. This is not pleasant. Can you please investigate if Device Tree
overlay is going to help here?
Shawn
[1] http://free-electrons.com/blog/dt-overlay-uboot-libfdt/
> Since all devices are very similar and only differ in few
> aspects, the following patch series introduces first common
> device tree snippets which are then included by the real
> devices. For better understanding, I tried to illustrate the
> hierarchy:
>
> +--------------------+ +----------------------+
> | imx28-duckbill.dts | | imx28-duckbill-2.dts |
> +--------------------+ +----------------------+
> ^ ^
> | +----------------------------+ | +------------------------------+
> | | imx28-duckbill-enocean.dts | | | imx28-duckbill-2-enocean.dts |
> | +----------------------------+ | +------------------------------+
> | ^ | ^
> | | +------------------------+ | | +--------------------------+
> | | | imx28-duckbill-485.dts | | | | imx28-duckbill-2-485.dts |
> | | +------------------------+ | | +--------------------------+
> | | ^ | | ^
> | | | +------------------------+ | | | +--------------------------+
> | | | | imx28-duckbill-spi.dts | | | | | imx28-duckbill-2-spi.dts |
> | | | +------------------------+ | | | +--------------------------+
> | | | ^ | | | ^
> | | | | | | | |
> | | | | | | | |
> +---------------------------------------+ +-----------------------------------------+
> | imx28-duckbill-common.dtsi | | imx28-duckbill-2-common.dtsi |
> +---------------------------------------+ +-----------------------------------------+
> ^ ^
> | |
> +----------------------------+
> | imx28-duckbill-base.dtsi |
> +----------------------------+
> ^
> |
> +------------+
> | imx28.dtsi |
> +------------+
>
> Michael Heimpold (7):
> ARM: dts: imx28: add alternative pinmuxing for mmc2
> ARM: dts: imx28: rename mmc2_sck_cfg to prepare for an alternative
> muxing setup
> ARM: dts: imx28: add alternative muxing for mmc2_sck_cfg
> ARM: dts: add I2SE Duckbill common definitions
> ARM: dts: duckbill: simplify DT and use common definitions
> ARM: dts: add support for remaining members of Duckbill series
> ARM: dts: add support for Duckbill 2 series devices
>
> arch/arm/boot/dts/Makefile | 7 ++
> arch/arm/boot/dts/imx28-duckbill-2-485.dts | 70 ++++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-2-common.dtsi | 110 +++++++++++++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-2-enocean.dts | 100 ++++++++++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-2-spi.dts | 63 ++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-2.dts | 46 +++++++++++
> arch/arm/boot/dts/imx28-duckbill-485.dts | 60 ++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-base.dtsi | 88 ++++++++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-common.dtsi | 80 ++++++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-enocean.dts | 90 ++++++++++++++++++++
> arch/arm/boot/dts/imx28-duckbill-spi.dts | 64 ++++++++++++++
> arch/arm/boot/dts/imx28-duckbill.dts | 99 +++-------------------
> arch/arm/boot/dts/imx28-m28cu3.dts | 2 +-
> arch/arm/boot/dts/imx28.dtsi | 28 ++++++-
> 14 files changed, 817 insertions(+), 90 deletions(-)
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-2-485.dts
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-2-common.dtsi
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-2-enocean.dts
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-2-spi.dts
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-2.dts
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-485.dts
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-base.dtsi
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-common.dtsi
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-enocean.dts
> create mode 100644 arch/arm/boot/dts/imx28-duckbill-spi.dts
>
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031
From: Shawn Guo @ 2016-10-24 13:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477312332-2027-1-git-send-email-festevam@gmail.com>
On Mon, Oct 24, 2016 at 10:32:12AM -0200, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> AR8031 and AR8035 have the same PHY ID mask of 0xffffffef.
>
> So fix it and make it match with the PHY ID mask definition
> at drivers/net/phy/at803x.c.
It was working well with a wrong ID?
Shawn
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
> ---
> arch/arm/mach-imx/mach-imx6q.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
> index 97fd251..45801b2 100644
> --- a/arch/arm/mach-imx/mach-imx6q.c
> +++ b/arch/arm/mach-imx/mach-imx6q.c
> @@ -173,7 +173,7 @@ static void __init imx6q_enet_phy_init(void)
> ksz9021rn_phy_fixup);
> phy_register_fixup_for_uid(PHY_ID_KSZ9031, MICREL_PHY_ID_MASK,
> ksz9031rn_phy_fixup);
> - phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff,
> + phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffef,
> ar8031_phy_fixup);
> phy_register_fixup_for_uid(PHY_ID_AR8035, 0xffffffef,
> ar8035_phy_fixup);
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 1/3] arm64: arch_timer: Add device tree binding for hisilicon-161x01 erratum
From: Mark Rutland @ 2016-10-24 13:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <7e839df8-f8f7-3b16-8321-4ff45b6c5884@huawei.com>
On Mon, Oct 24, 2016 at 08:40:01PM +0800, Ding Tianhong wrote:
> On 2016/10/24 19:16, Mark Rutland wrote:
> > Is "161x01" the *exact* erratum number, or is the 'x' a wildcard?
>
> The 'x' is a wildcard, it will cover 161001 to 161601 several numbers,
Given you're using a wildcard, I take it that this is a *part* number?
Thanks,
Mark.
^ permalink raw reply
* [PATCH v5 3/7] drm: sunxi: add DE2 HDMI support
From: Maxime Ripard @ 2016-10-24 13:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <36bd5454897c8ab77749e0294e4a4ecc2450dd12.1477142934.git.moinejf@free.fr>
Hi,
On Fri, Oct 21, 2016 at 10:08:06AM +0200, Jean-Francois Moine wrote:
> This patch adds a HDMI driver to the DE2 based Allwinner's SoCs
> as A83T and H3.
> Audio and video are supported.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Output from checkpatch:
total: 26 errors, 31 warnings, 70 checks, 1458 lines checked
Please run checkpatch before sending your patches.
Apart from that, this looks an awful lot like the designware HDMI
controller, for which a driver already exists. Any reason to create a
new driver from scratch?
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/aa53ff74/attachment-0001.sig>
^ permalink raw reply
* [PATCH] ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031
From: Fabio Estevam @ 2016-10-24 13:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161024131434.GL30578@tiger>
On Mon, Oct 24, 2016 at 11:14 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> On Mon, Oct 24, 2016 at 10:32:12AM -0200, Fabio Estevam wrote:
>> From: Fabio Estevam <fabio.estevam@nxp.com>
>>
>> AR8031 and AR8035 have the same PHY ID mask of 0xffffffef.
>>
>> So fix it and make it match with the PHY ID mask definition
>> at drivers/net/phy/at803x.c.
>
> It was working well with a wrong ID?
Yes, it was working well even with the wrong PHY ID mask.
^ permalink raw reply
* [PATCH] drm: convert DT component matching to component_match_add_release()
From: Jyri Sarha @ 2016-10-24 13:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <E1bwo6l-0005Io-Q1@rmk-PC.armlinux.org.uk>
On 10/19/16 13:28, Russell King wrote:
> Convert DT component matching to use component_match_add_release().
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Acked-by: Jyri Sarha <jsarha@ti.com>
Reviewed-by: Jyri Sarha <jsarha@ti.com>
However, the patch description is a bit brief. It took me this mail
thread to fully understand why simple comparison of pointer values may
cause problems if the of_node ref count goes to zero.
BR,
Jyri
> ---
> Can we please get this patch from May merged into the drm-misc or
> whatever trees so that we don't end up with conflicts? I've no idea
> who looks after drm-misc, as they have _still_ failed to add
> themselves to MAINTAINERS.
>
> drivers/gpu/drm/arm/hdlcd_drv.c | 3 ++-
> drivers/gpu/drm/arm/malidp_drv.c | 4 +++-
> drivers/gpu/drm/armada/armada_drv.c | 2 +-
> drivers/gpu/drm/drm_of.c | 28 +++++++++++++++++++++++--
> drivers/gpu/drm/etnaviv/etnaviv_drv.c | 5 +++--
> drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 7 ++++---
> drivers/gpu/drm/mediatek/mtk_drm_drv.c | 4 +++-
> drivers/gpu/drm/msm/msm_drv.c | 12 ++++++-----
> drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 6 ++++--
> drivers/gpu/drm/sti/sti_drv.c | 5 +++--
> drivers/gpu/drm/sun4i/sun4i_drv.c | 3 ++-
> drivers/gpu/drm/tilcdc/tilcdc_external.c | 4 +++-
> include/drm/drm_of.h | 12 +++++++++++
> 13 files changed, 73 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
> index fb6a418ce6be..6477d1a65266 100644
> --- a/drivers/gpu/drm/arm/hdlcd_drv.c
> +++ b/drivers/gpu/drm/arm/hdlcd_drv.c
> @@ -453,7 +453,8 @@ static int hdlcd_probe(struct platform_device *pdev)
> return -EAGAIN;
> }
>
> - component_match_add(&pdev->dev, &match, compare_dev, port);
> + drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
> + of_node_put(port);
>
> return component_master_add_with_match(&pdev->dev, &hdlcd_master_ops,
> match);
> diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
> index 9280358b8f15..9f4739452a25 100644
> --- a/drivers/gpu/drm/arm/malidp_drv.c
> +++ b/drivers/gpu/drm/arm/malidp_drv.c
> @@ -493,7 +493,9 @@ static int malidp_platform_probe(struct platform_device *pdev)
> return -EAGAIN;
> }
>
> - component_match_add(&pdev->dev, &match, malidp_compare_dev, port);
> + drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev,
> + port);
> + of_node_put(port);
> return component_master_add_with_match(&pdev->dev, &malidp_master_ops,
> match);
> }
> diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> index 1e0e68f608e4..94e46da9a758 100644
> --- a/drivers/gpu/drm/armada/armada_drv.c
> +++ b/drivers/gpu/drm/armada/armada_drv.c
> @@ -254,7 +254,7 @@ static void armada_add_endpoints(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, match, compare_of, remote);
> + drm_of_component_match_add(dev, match, compare_of, remote);
> of_node_put(remote);
> }
> }
> diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
> index bc98bb94264d..47848ed8ca48 100644
> --- a/drivers/gpu/drm/drm_of.c
> +++ b/drivers/gpu/drm/drm_of.c
> @@ -6,6 +6,11 @@
> #include <drm/drm_crtc.h>
> #include <drm/drm_of.h>
>
> +static void drm_release_of(struct device *dev, void *data)
> +{
> + of_node_put(data);
> +}
> +
> /**
> * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node
> * @dev: DRM device
> @@ -64,6 +69,24 @@ uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
> EXPORT_SYMBOL(drm_of_find_possible_crtcs);
>
> /**
> + * drm_of_component_match_add - Add a component helper OF node match rule
> + * @master: master device
> + * @matchptr: component match pointer
> + * @compare: compare function used for matching component
> + * @node: of_node
> + */
> +void drm_of_component_match_add(struct device *master,
> + struct component_match **matchptr,
> + int (*compare)(struct device *, void *),
> + struct device_node *node)
> +{
> + of_node_get(node);
> + component_match_add_release(master, matchptr, drm_release_of,
> + compare, node);
> +}
> +EXPORT_SYMBOL_GPL(drm_of_component_match_add);
> +
> +/**
> * drm_of_component_probe - Generic probe function for a component based master
> * @dev: master device containing the OF node
> * @compare_of: compare function used for matching components
> @@ -101,7 +124,7 @@ int drm_of_component_probe(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, &match, compare_of, port);
> + drm_of_component_match_add(dev, &match, compare_of, port);
> of_node_put(port);
> }
>
> @@ -140,7 +163,8 @@ int drm_of_component_probe(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, &match, compare_of, remote);
> + drm_of_component_match_add(dev, &match, compare_of,
> + remote);
> of_node_put(remote);
> }
> of_node_put(port);
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> index aa687669e22b..0dee6acbd880 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> @@ -16,6 +16,7 @@
>
> #include <linux/component.h>
> #include <linux/of_platform.h>
> +#include <drm/drm_of.h>
>
> #include "etnaviv_drv.h"
> #include "etnaviv_gpu.h"
> @@ -629,8 +630,8 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
> if (!core_node)
> break;
>
> - component_match_add(&pdev->dev, &match, compare_of,
> - core_node);
> + drm_of_component_match_add(&pdev->dev, &match,
> + compare_of, core_node);
> of_node_put(core_node);
> }
> } else if (dev->platform_data) {
> diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
> index 90377a609c98..e88fde18c946 100644
> --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
> +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
> @@ -24,6 +24,7 @@
> #include <drm/drm_fb_cma_helper.h>
> #include <drm/drm_atomic_helper.h>
> #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
>
> #include "kirin_drm_drv.h"
>
> @@ -260,14 +261,13 @@ static struct device_node *kirin_get_remote_node(struct device_node *np)
> DRM_ERROR("no valid endpoint node\n");
> return ERR_PTR(-ENODEV);
> }
> - of_node_put(endpoint);
>
> remote = of_graph_get_remote_port_parent(endpoint);
> + of_node_put(endpoint);
> if (!remote) {
> DRM_ERROR("no valid remote node\n");
> return ERR_PTR(-ENODEV);
> }
> - of_node_put(remote);
>
> if (!of_device_is_available(remote)) {
> DRM_ERROR("not available for remote node\n");
> @@ -294,7 +294,8 @@ static int kirin_drm_platform_probe(struct platform_device *pdev)
> if (IS_ERR(remote))
> return PTR_ERR(remote);
>
> - component_match_add(dev, &match, compare_of, remote);
> + drm_of_component_match_add(dev, &match, compare_of, remote);
> + of_node_put(remote);
>
> return component_master_add_with_match(dev, &kirin_drm_ops, match);
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index cf83f6507ec8..9c5430fb82a2 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -18,6 +18,7 @@
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_gem.h>
> #include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> #include <linux/component.h>
> #include <linux/iommu.h>
> #include <linux/of_address.h>
> @@ -415,7 +416,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
> comp_type == MTK_DPI) {
> dev_info(dev, "Adding component match for %s\n",
> node->full_name);
> - component_match_add(dev, &match, compare_of, node);
> + drm_of_component_match_add(dev, &match, compare_of,
> + node);
> } else {
> struct mtk_ddp_comp *comp;
>
> diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
> index fb5c0b0a7594..84d38eaea585 100644
> --- a/drivers/gpu/drm/msm/msm_drv.c
> +++ b/drivers/gpu/drm/msm/msm_drv.c
> @@ -15,6 +15,8 @@
> * this program. If not, see <http://www.gnu.org/licenses/>.
> */
>
> +#include <drm/drm_of.h>
> +
> #include "msm_drv.h"
> #include "msm_debugfs.h"
> #include "msm_fence.h"
> @@ -919,8 +921,8 @@ static int add_components_mdp(struct device *mdp_dev,
> continue;
> }
>
> - component_match_add(master_dev, matchptr, compare_of, intf);
> -
> + drm_of_component_match_add(master_dev, matchptr, compare_of,
> + intf);
> of_node_put(intf);
> of_node_put(ep_node);
> }
> @@ -962,8 +964,8 @@ static int add_display_components(struct device *dev,
> put_device(mdp_dev);
>
> /* add the MDP component itself */
> - component_match_add(dev, matchptr, compare_of,
> - mdp_dev->of_node);
> + drm_of_component_match_add(dev, matchptr, compare_of,
> + mdp_dev->of_node);
> } else {
> /* MDP4 */
> mdp_dev = dev;
> @@ -996,7 +998,7 @@ static int add_gpu_components(struct device *dev,
> if (!np)
> return 0;
>
> - component_match_add(dev, matchptr, compare_of, np);
> + drm_of_component_match_add(dev, matchptr, compare_of, np);
>
> of_node_put(np);
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 8c8cbe837e61..6fe161192bb4 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -20,6 +20,7 @@
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_fb_helper.h>
> #include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> #include <linux/dma-mapping.h>
> #include <linux/pm_runtime.h>
> #include <linux/module.h>
> @@ -388,7 +389,7 @@ static void rockchip_add_endpoints(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, match, compare_of, remote);
> + drm_of_component_match_add(dev, match, compare_of, remote);
> of_node_put(remote);
> }
> }
> @@ -437,7 +438,8 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
> }
>
> of_node_put(iommu);
> - component_match_add(dev, &match, compare_of, port->parent);
> + drm_of_component_match_add(dev, &match, compare_of,
> + port->parent);
> of_node_put(port);
> }
>
> diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
> index 2784919a7366..5e819876e642 100644
> --- a/drivers/gpu/drm/sti/sti_drv.c
> +++ b/drivers/gpu/drm/sti/sti_drv.c
> @@ -17,6 +17,7 @@
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_gem_cma_helper.h>
> #include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_of.h>
>
> #include "sti_crtc.h"
> #include "sti_drv.h"
> @@ -423,8 +424,8 @@ static int sti_platform_probe(struct platform_device *pdev)
> child_np = of_get_next_available_child(node, NULL);
>
> while (child_np) {
> - component_match_add(dev, &match, compare_of, child_np);
> - of_node_put(child_np);
> + drm_of_component_match_add(dev, &match, compare_of,
> + child_np);
> child_np = of_get_next_available_child(node, child_np);
> }
>
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 0da9862ad8ed..b3c4ad605e81 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -18,6 +18,7 @@
> #include <drm/drm_fb_cma_helper.h>
> #include <drm/drm_gem_cma_helper.h>
> #include <drm/drm_fb_helper.h>
> +#include <drm/drm_of.h>
>
> #include "sun4i_crtc.h"
> #include "sun4i_drv.h"
> @@ -239,7 +240,7 @@ static int sun4i_drv_add_endpoints(struct device *dev,
> /* Add current component */
> DRM_DEBUG_DRIVER("Adding component %s\n",
> of_node_full_name(node));
> - component_match_add(dev, match, compare_of, node);
> + drm_of_component_match_add(dev, match, compare_of, node);
> count++;
> }
>
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> index 68e895021005..06a4c584f3cb 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> @@ -10,6 +10,7 @@
>
> #include <linux/component.h>
> #include <linux/of_graph.h>
> +#include <drm/drm_of.h>
>
> #include "tilcdc_drv.h"
> #include "tilcdc_external.h"
> @@ -160,7 +161,8 @@ int tilcdc_get_external_components(struct device *dev,
>
> dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
> if (match)
> - component_match_add(dev, match, dev_match_of, node);
> + drm_of_component_match_add(dev, match, dev_match_of,
> + node);
> of_node_put(node);
> count++;
> }
> diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h
> index 3fd87b386ed7..d6b4c5587bbe 100644
> --- a/include/drm/drm_of.h
> +++ b/include/drm/drm_of.h
> @@ -4,6 +4,7 @@
> #include <linux/of_graph.h>
>
> struct component_master_ops;
> +struct component_match;
> struct device;
> struct drm_device;
> struct drm_encoder;
> @@ -12,6 +13,10 @@ struct device_node;
> #ifdef CONFIG_OF
> extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
> struct device_node *port);
> +extern void drm_of_component_match_add(struct device *master,
> + struct component_match **matchptr,
> + int (*compare)(struct device *, void *),
> + struct device_node *node);
> extern int drm_of_component_probe(struct device *dev,
> int (*compare_of)(struct device *, void *),
> const struct component_master_ops *m_ops);
> @@ -25,6 +30,13 @@ static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
> return 0;
> }
>
> +static void drm_of_component_match_add(struct device *master,
> + struct component_match **matchptr,
> + int (*compare)(struct device *, void *),
> + struct device_node *node)
> +{
> +}
> +
> static inline int
> drm_of_component_probe(struct device *dev,
> int (*compare_of)(struct device *, void *),
>
^ permalink raw reply
* [PATCH 1/3] arm64: arch_timer: Add device tree binding for hisilicon-161x01 erratum
From: Ding Tianhong @ 2016-10-24 13:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161024131617.GJ15620@leverpostej>
On 2016/10/24 21:16, Mark Rutland wrote:
> On Mon, Oct 24, 2016 at 08:40:01PM +0800, Ding Tianhong wrote:
>> On 2016/10/24 19:16, Mark Rutland wrote:
>>> Is "161x01" the *exact* erratum number, or is the 'x' a wildcard?
>>
>> The 'x' is a wildcard, it will cover 161001 to 161601 several numbers,
>
> Given you're using a wildcard, I take it that this is a *part* number?
>
Yes, I was doubt how to fix this, should I choose a better erratum number?
> Thanks,
> Mark.
>
> .
>
^ permalink raw reply
* [PATCH] ARM: DT: STM32: add dma for usart3 on F429
From: Alexandre TORGUE @ 2016-10-24 13:31 UTC (permalink / raw)
To: linux-arm-kernel
Add DMA support for USART3 on STM32F429 MCU.
Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 11e8a69..0596d60 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -122,6 +122,9 @@
interrupts = <39>;
clocks = <&rcc 0 146>;
status = "disabled";
+ dmas = <&dma1 1 4 0x400 0x0>,
+ <&dma1 3 4 0x400 0x0>;
+ dma-names = "rx", "tx";
};
usart4: serial at 40004c00 {
--
1.9.1
^ permalink raw reply related
* [PATCH v3 3/8] PM / Domains: Allow domain power states to be read from DT
From: Sudeep Holla @ 2016-10-24 13:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476467276-75094-4-git-send-email-lina.iyer@linaro.org>
On 14/10/16 18:47, Lina Iyer wrote:
> This patch allows domains to define idle states in the DT. SoC's can
> define domain idle states in DT using the "domain-idle-states" property
> of the domain provider. Add API to read the idle states from DT that can
> be set in the genpd object.
>
> This patch is based on the original patch by Marc Titinger.
>
> Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
> drivers/base/power/domain.c | 94 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pm_domain.h | 8 ++++
> 2 files changed, 102 insertions(+)
>
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index 37ab7f1..9af75ba 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -1916,6 +1916,100 @@ out:
> return ret ? -EPROBE_DEFER : 0;
> }
> EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
> +
> +static const struct of_device_id idle_state_match[] = {
> + { .compatible = "arm,idle-state", },
> + { }
> +};
> +
I still think it's better to have another compatible to serve this
purpose. We don't want to end up creating genpd domains just because
they are "arm,idle-state" compatible IMO ?
I agree you can prevent it checking for OSC mode support in the
firmware. But I want to understand if you have any strong reasons for
avoiding that approach.
--
Regards,
Sudeep
^ permalink raw reply
* [PATCH 1/3] arm64: arch_timer: Add device tree binding for hisilicon-161x01 erratum
From: Mark Rutland @ 2016-10-24 13:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1dcfb21a-7417-282e-f187-425d2c148672@huawei.com>
On Mon, Oct 24, 2016 at 09:23:10PM +0800, Ding Tianhong wrote:
> On 2016/10/24 21:16, Mark Rutland wrote:
> > On Mon, Oct 24, 2016 at 08:40:01PM +0800, Ding Tianhong wrote:
> >> On 2016/10/24 19:16, Mark Rutland wrote:
> >>> Is "161x01" the *exact* erratum number, or is the 'x' a wildcard?
> >>
> >> The 'x' is a wildcard, it will cover 161001 to 161601 several numbers,
> >
> > Given you're using a wildcard, I take it that this is a *part* number?
>
> Yes, I was doubt how to fix this, should I choose a better erratum number?
Typically, we expect that each vendor has some central database of their
errata, with each having a unique ID.
If Huawei do not have such a database, I do not think that we should
invent an erratum number here.
Thanks,
Mark.
^ permalink raw reply
* [PATCH] ARM: sti: stih410-clocks: Add PROC_STFE as a critical clock
From: Peter Griffin @ 2016-10-24 13:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161024085304.GE14477@dell>
Hi Lee,
On Mon, 24 Oct 2016, Lee Jones wrote:
> On Tue, 18 Oct 2016, Peter Griffin wrote:
>
> > Once the ST frontend demux HW IP has been enabled, the clock can't
> > be disabled otherwise the system will hang and the board will
> > be unserviceable.
> >
> > To allow balanced clock enable/disable calls in the driver we use
> > the critical clock infrastructure to take an extra reference on the
> > clock so the clock will never actually be disabled.
>
> This is an abuse of the critical-clocks framework, and is exactly the
> type of hack I promised the clk guys I'd try to prevent.
I expect the best way to do this would be to write some documentation on the
clock-critical DT binding and/or CRITICAL_CLK flag. The only documentation I can
find currently is with the initial patch series [1] and the comment in
clk-provider.h of
#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */
Or the patch decription
"Critical clocks are those which must not be gated, else undefined
or catastrophic failure would occur. Here we have chosen to
ensure the prepare/enable counts are correctly incremented, so as
not to confuse users with enabled clocks with no visible users."
Which is the functionality I want for this clock.
> If this, or
> any other IP has some quirks (i.e. once enabled, if this clock is
> subsequently disabled it will have a catastrophic effect on the
> platform), then they should be worked around in the driver.
>
> The correct thing to do here is craft a clk-keep-on flag and ensure it
> is set to true for the effected platform(s)' platform data.
I'm always wary of creating a driver specific flag, especially when its
purpose is to do the same thing as an existing mechanism provided by the
subsystem of not gating the clock.
I can see a couple of problems with what you propose:
1) You have to put the clk-keep-on flag in every driver which consumes the
clock. IMO it is much better to have this knowledge in the SoC's
clock driver so every consumer of the clock automatically benefits.
2) You don't benefit from the CLK_IS_CRITICAL reference counting logic in
clk.c. So then each driver has to also work around that to get sensible reference
counts. e.g.
if (!__clk_is_enabled(clk) && pdata->clk-keep-on)
clk_enable(clk)
Which seems to me to be fighting against the subsystem. Given that the only use of
_clk_is_enabled() outside drivers/clk is in an old arch/arm/mach-omap2/pm24xx.c
driver I suspect its use is frowned upon, and it shouldn't really be an EXPORTED_SYMBOL.
regards,
Peter.
[1] https://lkml.org/lkml/2016/1/18/272
^ permalink raw reply
* [PATCH] arm64: SMMU-v2: Workaround for Cavium ThunderX erratum 28168
From: Marc Zyngier @ 2016-10-24 13:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477112061-12868-1-git-send-email-gakula@caviumnetworks.com>
Geetha,
On 22/10/16 05:54, Geetha sowjanya wrote:
> From: Tirumalesh Chalamarla <Tirumalesh.Chalamarla@cavium.com>
>
> This patch implements Cavium ThunderX erratum 28168.
>
> PCI requires stores complete in order. Due to erratum #28168
> PCI-inbound MSI-X store to the interrupt controller are delivered
> to the interrupt controller before older PCI-inbound memory stores
> are committed.
> Doing a sync on SMMU will make sure all prior transactions are
> completed.
>
> Signed-off-by: Tirumalesh Chalamarla <Tirumalesh.Chalamarla@cavium.com>
> Signed-off-by: Geetha sowjanya <gakula@caviumnetworks.com>
> ---
> arch/arm64/Kconfig | 11 +++++++++++
> drivers/iommu/arm-smmu.c | 38 ++++++++++++++++++++++++++++++++++++++
> drivers/irqchip/irq-gic-common.h | 1 +
> drivers/irqchip/irq-gic-v3-its.c | 22 ++++++++++++++++++++++
> kernel/irq/chip.c | 4 ++++
Thanks Robin for looping me in. Geetha, please use get_maintainers.pl to
keep the relevant people on CC, specially as you're touching some of the
core infrastructure.
> 5 files changed, 76 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 30398db..57f5c9b 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -474,6 +474,17 @@ config CAVIUM_ERRATUM_27456
>
> If unsure, say Y.
>
> +config CAVIUM_ERRATUM_28168
> + bool "Cavium erratum 28168: Make sure ITS and SMMU TLB are in sync"
> + default y
> + help
> + Due to erratum #28168 PCI-inbound MSI-X store to the interrupt
> + controller are delivered to the interrupt controller before older
> + PCI-inbound memory stores are committed. Doing a sync on SMMU
> + will make sure all prior transactions are completed.
> +
> + If unsure, say Y.
Please add an entry to Documentation/arm64/silicon-errata.txt.
> +
> endmenu
>
>
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index 9740846..20a61c6 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
I'll skip the SMMU code on which Robin has commented already, and move
to the irq part, which is equally entertaining.
[...]
> diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h
> index 205e5fd..0228ba0 100644
> --- a/drivers/irqchip/irq-gic-common.h
> +++ b/drivers/irqchip/irq-gic-common.h
> @@ -38,4 +38,5 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
>
> void gic_set_kvm_info(const struct gic_kvm_info *info);
>
> +void cavium_smmu_tlb_sync(void *iommu);
> #endif /* _IRQ_GIC_COMMON_H */
> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> index 003495d..88e9958 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -112,6 +112,7 @@ struct its_device {
> struct its_node *its;
> struct event_lpi_map event_map;
> void *itt;
> + struct device *dev;
This doesn't work in the presence of anything that will multiplex
multiple RequesterIDs onto a single DeviceID (non transparent PCI
bridge, for example).
> u32 nr_ites;
> u32 device_id;
> };
> @@ -664,10 +665,29 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
> iommu_dma_map_msi_msg(d->irq, msg);
> }
>
> +/**
> + * Due to erratum in ThunderX,
> + * we need to make sure SMMU is in sync with ITS translations.
> + **/
> +static void its_ack_irq(struct irq_data *d)
> +{
> + struct its_device *its_dev = irq_data_get_irq_chip_data(d);
> + struct pci_dev *pdev;
> +
> + if (!dev_is_pci(its_dev->dev))
> + return;
How about non PCI devices?
> +
> + pdev = to_pci_dev(its_dev->dev);
> + if (pdev->vendor != 0x177d)
> + cavium_smmu_tlb_sync(its_dev->dev);
What makes Cavium devices so special that they do not need to respect
the PCI memory ordering with respect to MSI delivery?
> +
> +}
> +
> static struct irq_chip its_irq_chip = {
> .name = "ITS",
> .irq_mask = its_mask_irq,
> .irq_unmask = its_unmask_irq,
> + .irq_ack = its_ack_irq,
Nice try, but no, thank you. If you really want to go down that road,
have a look at CONFIG_IRQ_PREFLOW_FASTEOI, and make this workaround a
per interrupt thing. At least, you won't pollute the core code with
another hack.
Also, it would be good to find a way for that hack to be confined to the
SMMU driver, since that's where the oddity is being handled. Something
that would occur when the device is mapping memory is probably on good spot.
> .irq_eoi = irq_chip_eoi_parent,
> .irq_set_affinity = its_set_affinity,
> .irq_compose_msi_msg = its_irq_compose_msi_msg,
> @@ -1422,6 +1442,8 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
> if (!its_dev)
> return -ENOMEM;
>
> + its_dev->dev = dev;
> +
> pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
> out:
> info->scratchpad[0].ptr = its_dev;
> diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
> index be3c34e..6add8da 100644
> --- a/kernel/irq/chip.c
> +++ b/kernel/irq/chip.c
> @@ -585,6 +585,10 @@ void handle_fasteoi_irq(struct irq_desc *desc)
> goto out;
> }
>
> +#ifdef CONFIG_CAVIUM_ERRATUM_28168
> + if (chip->irq_ack)
> + chip->irq_ack(&desc->irq_data);
> +#endif
> kstat_incr_irqs_this_cpu(desc);
> if (desc->istate & IRQS_ONESHOT)
> mask_irq(desc);
>
Overall, this workaround is not acceptable as it is. You need to find
ways to make it less invasive, and hopefully the above pointers will
help. Please keep the current distribution list posted once you update
this patch.
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH v5 1/7] drm: sunxi: Add a basic DRM driver for Allwinner DE2
From: Maxime Ripard @ 2016-10-24 14:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8afc5e020c5767face34fe3a9ab300ce9e67ba00.1477142934.git.moinejf@free.fr>
Hi,
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).
> +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.
> +
> +- resets: phandle to the reset of the device
> +
> +- ports: phandle's to the LCD ports
Please use the OF graph.
> +
> +LCD controller
> +==============
> +
> +Required properties:
> +
> +- compatible: value should be one of the following
> + "allwinner,sun8i-a83t-lcd"
> +
> +- clocks: must include clock specifiers corresponding to entries in the
> + clock-names property.
> +
> +- clock-names: must contain
> + "gate": for LCD activation
> + "clock": pixel clock
> +
> +- resets: phandle to the reset of the device
> +
> +- port: port node with endpoint definitions as defined in
> + Documentation/devicetree/bindings/media/video-interfaces.txt
> +
> +Example:
> +
> + de: de-controller at 01000000 {
> + compatible = "allwinner,sun8i-h3-display-engine";
> + ...
> + clocks = <&&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
> + clock-names = "gate", "clock";
> + resets = <&ccu RST_BUS_DE>;
> + ports = <&lcd0_p>;
> + };
> +
> + lcd0: lcd-controller at 01c0c000 {
> + compatible = "allwinner,sun8i-a83t-lcd";
> + ...
> + clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
> + clock-names = "gate", "clock";
> + resets = <&ccu RST_BUS_TCON0>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + lcd0_p: port {
> + lcd0_ep: endpoint {
> + remote-endpoint = <&hdmi_ep>;
> + };
> + };
> + };
> +
> + hdmi: hdmi at 01ee0000 {
> + ...
> + #address-cells = <1>;
> + #size-cells = <0>;
> + port {
> + type = "video";
> + hdmi_ep: endpoint {
> + remote-endpoint = <&lcd0_ep>;
> + };
> + };
> + };
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 483059a..afd576f 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -187,6 +187,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
>
> source "drivers/gpu/drm/sun4i/Kconfig"
>
> +source "drivers/gpu/drm/sunxi/Kconfig"
> +
> source "drivers/gpu/drm/omapdrm/Kconfig"
>
> source "drivers/gpu/drm/tilcdc/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 25c7204..120d0bf 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
> obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
> obj-y += omapdrm/
> obj-$(CONFIG_DRM_SUN4I) += sun4i/
> +obj-$(CONFIG_DRM_SUNXI) += sunxi/
> obj-y += tilcdc/
> obj-$(CONFIG_DRM_QXL) += qxl/
> obj-$(CONFIG_DRM_BOCHS) += bochs/
> diff --git a/drivers/gpu/drm/sunxi/Kconfig b/drivers/gpu/drm/sunxi/Kconfig
> new file mode 100644
> index 0000000..56bde2e
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/Kconfig
> @@ -0,0 +1,21 @@
> +#
> +# Allwinner Video configuration
> +#
> +
> +config DRM_SUNXI
> + tristate "DRM Support for Allwinner Video"
> + depends on DRM && OF
> + depends on ARCH_SUNXI || COMPILE_TEST
> + select DRM_KMS_HELPER
> + select DRM_KMS_CMA_HELPER
> + select DRM_GEM_CMA_HELPER
> + help
> + Choose this option if you have a Allwinner chipset.
> +
> +config DRM_SUNXI_DE2
> + tristate "Support for Allwinner Video with DE2 interface"
> + depends on DRM_SUNXI
> + help
> + Choose this option if your Allwinner chipset has the DE2 interface
> + as the A64, A83T and H3. If M is selected the module will be called
> + sunxi-de2-drm.
> diff --git a/drivers/gpu/drm/sunxi/Makefile b/drivers/gpu/drm/sunxi/Makefile
> new file mode 100644
> index 0000000..62220cb
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for Allwinner's sun8i DRM device driver
> +#
> +
> +sunxi-de2-drm-objs := de2_drv.o de2_de.o de2_crtc.o de2_plane.o
> +
> +obj-$(CONFIG_DRM_SUNXI_DE2) += sunxi-de2-drm.o
> 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.
> +
> +#define XY(x, y) (((x) << 16) | (y))
> +
> +#define tcon_read(base, member) \
> + readl_relaxed(base + offsetof(struct tcon, member))
> +#define tcon_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct tcon, member))
> +
> +/* vertical blank functions */
> +static void de2_atomic_flush(struct drm_crtc *crtc,
> + struct drm_crtc_state *old_state)
> +{
> + struct drm_pending_vblank_event *event = crtc->state->event;
> +
> + if (event) {
> + crtc->state->event = NULL;
> + spin_lock_irq(&crtc->dev->event_lock);
> + if (drm_crtc_vblank_get(crtc) == 0)
> + drm_crtc_arm_vblank_event(crtc, event);
> + else
> + drm_crtc_send_vblank_event(crtc, event);
> + spin_unlock_irq(&crtc->dev->event_lock);
> + }
> +}
> +
> +static irqreturn_t de2_lcd_irq(int irq, void *dev_id)
> +{
> + struct lcd *lcd = (struct lcd *) dev_id;
> + u32 isr;
> +
> + isr = tcon_read(lcd->mmio, gint0);
> +
> + drm_crtc_handle_vblank(&lcd->crtc);
> +
> + tcon_write(lcd->mmio, gint0, isr & ~TCON_GINT0_TCON1_Vb_Int_Flag);
> +
> + return IRQ_HANDLED;
> +}
> +
> +int de2_enable_vblank(struct drm_device *drm, unsigned crtc)
> +{
> + struct priv *priv = drm->dev_private;
> + struct lcd *lcd = priv->lcds[crtc];
> +
> + tcon_write(lcd->mmio, gint0,
> + tcon_read(lcd->mmio, gint0) |
> + TCON_GINT0_TCON1_Vb_Int_En);
That's a weird indentation
> + return 0;
> +}
> +
> +void de2_disable_vblank(struct drm_device *drm, unsigned crtc)
> +{
> + struct priv *priv = drm->dev_private;
> + struct lcd *lcd = priv->lcds[crtc];
> +
> + tcon_write(lcd->mmio, gint0,
> + tcon_read(lcd->mmio, gint0) &
> + ~TCON_GINT0_TCON1_Vb_Int_En);
> +}
> +
> +/* panel functions */
Panel functions? In the CRTC driver?
> +static void de2_set_frame_timings(struct lcd *lcd)
> +{
> + struct drm_crtc *crtc = &lcd->crtc;
> + const struct drm_display_mode *mode = &crtc->mode;
> + int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;
> + int start_delay;
> + u32 data;
> +
> + data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
> + tcon_write(lcd->mmio, basic0, data);
> + tcon_write(lcd->mmio, basic1, data);
> + tcon_write(lcd->mmio, basic2, data);
> + tcon_write(lcd->mmio, basic3,
> + XY(mode->htotal - 1,
> + mode->htotal - mode->hsync_start - 1));
> + tcon_write(lcd->mmio, basic4,
> + XY(mode->vtotal * (3 - interlace),
> + mode->vtotal - mode->vsync_start - 1));
> + tcon_write(lcd->mmio, basic5,
> + XY(mode->hsync_end - mode->hsync_start - 1,
> + mode->vsync_end - mode->vsync_start - 1));
> +
> + tcon_write(lcd->mmio, ps_sync, XY(1, 1));
> +
> + data = TCON1_IO_POL_IO2_inv;
> + if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> + data |= TCON1_IO_POL_IO0_inv;
> + if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> + data |= TCON1_IO_POL_IO1_inv;
> + tcon_write(lcd->mmio, io_pol, data);
> +
> + tcon_write(lcd->mmio, ceu_ctl,
> + tcon_read(lcd->mmio, ceu_ctl) & ~TCON_CEU_CTL_ceu_en);
> +
> + data = tcon_read(lcd->mmio, tcon1_ctl);
> + if (interlace == 2)
> + data |= TCON1_CTL_Interlace_En;
> + else
> + data &= ~TCON1_CTL_Interlace_En;
> + tcon_write(lcd->mmio, tcon1_ctl, data);
> +
> + tcon_write(lcd->mmio, fill_ctl, 0);
> + tcon_write(lcd->mmio, fill_start0, mode->vtotal + 1);
> + tcon_write(lcd->mmio, fill_end0, mode->vtotal);
> + tcon_write(lcd->mmio, fill_data0, 0);
> +
> + start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
> + if (start_delay > 31)
> + start_delay = 31;
> + data = tcon_read(lcd->mmio, tcon1_ctl);
> + data &= ~TCON1_CTL_Start_Delay_MASK;
> + data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
> + tcon_write(lcd->mmio, tcon1_ctl, data);
> +
> + tcon_write(lcd->mmio, io_tri, 0x0fffffff);
> +}
Some comments here would be nice, there's a lot of non trivial things.
> +
> +static void de2_crtc_enable(struct drm_crtc *crtc)
> +{
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + struct drm_display_mode *mode = &crtc->mode;
> +
> + DRM_DEBUG_DRIVER("\n");
Log something useful, or don't.
> +
> + clk_set_rate(lcd->clk, mode->clock * 1000);
> +
> + de2_set_frame_timings(lcd);
> +
> + tcon_write(lcd->mmio, tcon1_ctl,
> + tcon_read(lcd->mmio, tcon1_ctl) | TCON1_CTL_TCON_En);
> +
> + de2_de_panel_init(lcd->priv, lcd->num, mode);
panel_init in the CRTC enable? Shouldn't that be in the panel driver?
or at least the encoder?
> +
> + drm_mode_debug_printmodeline(mode);
This is already printed by the core.
> +}
> +
> +static void de2_crtc_disable(struct drm_crtc *crtc)
> +{
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + unsigned long flags;
> +
> + DRM_DEBUG_DRIVER("\n");
> +
> + tcon_write(lcd->mmio, tcon1_ctl,
> + tcon_read(lcd->mmio, tcon1_ctl) & ~TCON1_CTL_TCON_En);
> +
> + if (crtc->state->event && !crtc->state->active) {
> + spin_lock_irqsave(&crtc->dev->event_lock, flags);
> + drm_crtc_send_vblank_event(crtc, crtc->state->event);
> + spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
> + crtc->state->event = NULL;
> + }
> +}
> +
> +static const struct drm_crtc_funcs de2_crtc_funcs = {
> + .destroy = drm_crtc_cleanup,
> + .set_config = drm_atomic_helper_set_config,
> + .page_flip = drm_atomic_helper_page_flip,
> + .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 de2_crtc_helper_funcs = {
> + .atomic_flush = de2_atomic_flush,
> + .enable = de2_crtc_enable,
> + .disable = de2_crtc_disable,
> +};
> +
> +static void de2_tcon_init(struct lcd *lcd)
> +{
> + tcon_write(lcd->mmio, tcon0_ctl,
> + tcon_read(lcd->mmio, tcon0_ctl) & ~TCON0_CTL_TCON_En);
> + tcon_write(lcd->mmio, tcon1_ctl,
> + tcon_read(lcd->mmio, tcon1_ctl) & ~TCON1_CTL_TCON_En);
> + tcon_write(lcd->mmio, gctl,
> + tcon_read(lcd->mmio, gctl) & ~TCON_GCTL_TCON_En);
> +
> + /* disable/ack interrupts */
> + tcon_write(lcd->mmio, gint0, 0);
> +}
> +
> +static void de2_tcon_enable(struct lcd *lcd)
> +{
> + tcon_write(lcd->mmio, gctl,
> + tcon_read(lcd->mmio, gctl) | TCON_GCTL_TCON_En);
> +}
> +
> +static int de2_crtc_init(struct drm_device *drm, struct lcd *lcd)
> +{
> + struct drm_crtc *crtc = &lcd->crtc;
> + int ret;
> +
> + ret = de2_plane_init(drm, lcd);
> + if (ret < 0)
> + return ret;
> +
> + drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
> +
> + ret = drm_crtc_init_with_planes(drm, crtc,
> + &lcd->planes[DE2_PRIMARY_PLANE],
> + &lcd->planes[DE2_CURSOR_PLANE],
> + &de2_crtc_funcs, NULL);
> + if (ret < 0)
> + return ret;
> +
> + de2_tcon_enable(lcd);
> +
> + de2_de_enable(lcd->priv, lcd->num);
> +
> + return 0;
> +}
> +
> +/*
> + * device init
> + */
> +static int de2_lcd_bind(struct device *dev, struct device *master,
> + void *data)
> +{
> + struct drm_device *drm = data;
> + struct priv *priv = drm->dev_private;
> + struct lcd *lcd = dev_get_drvdata(dev);
> + int ret;
> +
> + lcd->priv = priv;
> +
> + /* (only 2 LCDs) */
> + lcd->crtc_idx = priv->lcds[0] ? 1 : 0;
> + priv->lcds[lcd->crtc_idx] = lcd;
> +
> + ret = de2_crtc_init(drm, lcd);
> + if (ret < 0) {
> + dev_err(dev, "failed to init the crtc\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void de2_lcd_unbind(struct device *dev, struct device *master,
> + void *data)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct lcd *lcd = platform_get_drvdata(pdev);
> +
> + if (lcd->mmio) {
> + if (lcd->priv)
> + de2_de_disable(lcd->priv, lcd->num);
> + tcon_write(lcd->mmio, gctl,
> + tcon_read(lcd->mmio, gctl) & ~TCON_GCTL_TCON_En);
> + }
> +}
> +
> +static const struct component_ops de2_lcd_ops = {
> + .bind = de2_lcd_bind,
> + .unbind = de2_lcd_unbind,
> +};
> +
> +static int de2_lcd_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node, *tmp, *parent, *port;
> + struct lcd *lcd;
> + struct resource *res;
> + int id, irq, ret;
> +
> + id = of_alias_get_id(np, "lcd");
> + if (id < 0) {
> + dev_err(dev, "no alias for lcd\n");
> + id = 0;
> + }
> + lcd = devm_kzalloc(dev, sizeof *lcd, GFP_KERNEL);
> + if (!lcd) {
> + dev_err(dev, "failed to allocate private data\n");
> + return -ENOMEM;
> + }
> + dev_set_drvdata(dev, lcd);
> + lcd->dev = dev;
> + lcd->num = id;
What do you need this number for?
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "failed to get memory resource\n");
> + return -EINVAL;
> + }
> +
> + lcd->mmio = devm_ioremap_resource(dev, res);
> + if (IS_ERR(lcd->mmio)) {
> + dev_err(dev, "failed to map registers\n");
> + return PTR_ERR(lcd->mmio);
> + }
> +
> + snprintf(lcd->name, sizeof(lcd->name), "sunxi-lcd%d", id);
> +
> + /* possible CRTCs */
> + parent = np;
> + tmp = of_get_child_by_name(np, "ports");
> + if (tmp)
> + parent = tmp;
> + port = of_get_child_by_name(parent, "port");
> + of_node_put(tmp);
> + if (!port) {
> + dev_err(dev, "no port node\n");
> + return -ENXIO;
> + }
> + lcd->crtc.port = port;
> +
> + lcd->gate = devm_clk_get(dev, "gate"); /* optional */
Having some kind of error checking would still be nice.
> +
> + lcd->clk = devm_clk_get(dev, "clock");
> + if (IS_ERR(lcd->clk)) {
> + dev_err(dev, "video clock err %d\n", (int) PTR_ERR(lcd->clk));
> + ret = PTR_ERR(lcd->clk);
> + goto err;
> + }
> +
> + lcd->rstc = devm_reset_control_get_optional(dev, NULL);
Ditto.
> +
> + irq = irq_of_parse_and_map(np, 0);
> + if (irq <= 0 || irq == NO_IRQ) {
> + dev_err(dev, "unable to get irq lcd %d\n", id);
> + ret = -EINVAL;
> + goto err;
> + }
You can use platform_get_irq for that.
> +
> + if (!IS_ERR(lcd->rstc)) {
> + ret = reset_control_deassert(lcd->rstc);
> + if (ret) {
> + dev_err(dev, "reset deassert err %d\n", ret);
> + goto err;
> + }
> + }
> +
> + if (!IS_ERR(lcd->gate)) {
> + ret = clk_prepare_enable(lcd->gate);
> + if (ret)
> + goto err2;
> + }
> +
> + ret = clk_prepare_enable(lcd->clk);
> + if (ret)
> + goto err2;
Is there any reason not to do that in the enable / disable? Leaving
clocks running while the device has no guarantee that it's going to be
used seems like a waste of resources.
> +
> + de2_tcon_init(lcd);
> +
> + ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
> + lcd->name, lcd);
> + if (ret < 0) {
> + dev_err(dev, "unable to request irq %d\n", irq);
> + goto err2;
> + }
> +
> + return component_add(dev, &de2_lcd_ops);
> +
> +err2:
> + if (!IS_ERR_OR_NULL(lcd->rstc))
> + reset_control_assert(lcd->rstc);
> + clk_disable_unprepare(lcd->gate);
> + clk_disable_unprepare(lcd->clk);
> +err:
> + of_node_put(lcd->crtc.port);
> + return ret;
> +}
> +
> +static int de2_lcd_remove(struct platform_device *pdev)
> +{
> + struct lcd *lcd = platform_get_drvdata(pdev);
> +
> + component_del(&pdev->dev, &de2_lcd_ops);
> +
> + if (!IS_ERR_OR_NULL(lcd->rstc))
> + reset_control_assert(lcd->rstc);
> + clk_disable_unprepare(lcd->gate);
> + clk_disable_unprepare(lcd->clk);
> + of_node_put(lcd->crtc.port);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id de2_lcd_ids[] = {
> + { .compatible = "allwinner,sun8i-a83t-lcd", },
> + { }
> +};
> +
> +struct platform_driver de2_lcd_platform_driver = {
> + .probe = de2_lcd_probe,
> + .remove = de2_lcd_remove,
> + .driver = {
> + .name = "sunxi-de2-lcd",
> + .of_match_table = of_match_ptr(de2_lcd_ids),
> + },
> +};
> diff --git a/drivers/gpu/drm/sunxi/de2_crtc.h b/drivers/gpu/drm/sunxi/de2_crtc.h
> new file mode 100644
> index 0000000..efbe45d
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_crtc.h
> @@ -0,0 +1,63 @@
> +#ifndef __DE2_CRTC_H__
> +#define __DE2_CRTC_H__
> +/*
> + * Copyright (C) 2016 Jean-Fran?ois Moine
> + *
> + * 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/clk.h>
> +#include <linux/reset.h>
> +#include <drm/drm_plane_helper.h>
> +
> +struct priv;
> +
> +enum de2_plane2 {
> + DE2_PRIMARY_PLANE,
> + DE2_CURSOR_PLANE,
> + DE2_VI_PLANE,
> + DE2_N_PLANES,
> +};
> +struct lcd {
> + void __iomem *mmio;
> +
> + struct device *dev;
> + struct drm_crtc crtc;
> + struct priv *priv; /* DRM/DE private data */
> +
> + short num; /* LCD number in hardware */
> + short crtc_idx; /* CRTC index in drm */
> +
> + struct clk *clk;
> + struct clk *gate;
> + struct reset_control *rstc;
> +
> + char name[16];
> +
> + struct drm_pending_vblank_event *event;
> +
> + struct drm_plane planes[DE2_N_PLANES];
> +};
> +
> +#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
> +
> +/* in de2_de.c */
> +void de2_de_enable(struct priv *priv, int lcd_num);
> +void de2_de_disable(struct priv *priv, int lcd_num);
> +void de2_de_hw_init(struct priv *priv, int lcd_num);
> +void de2_de_panel_init(struct priv *priv, int lcd_num,
> + struct drm_display_mode *mode);
> +void de2_de_plane_disable(struct priv *priv,
> + int lcd_num, int plane_ix);
> +void de2_de_plane_update(struct priv *priv,
> + int lcd_num, int plane_ix,
> + struct drm_plane_state *state,
> + struct drm_plane_state *old_state);
Does it need to be exported?
> +
> +/* in de2_plane.c */
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
> +
> +#endif /* __DE2_CRTC_H__ */
> diff --git a/drivers/gpu/drm/sunxi/de2_de.c b/drivers/gpu/drm/sunxi/de2_de.c
> new file mode 100644
> index 0000000..0d8cb62
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_de.c
> @@ -0,0 +1,591 @@
> +/*
> + * ALLWINNER DRM driver - Display Engine 2
> + *
> + * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
> + * Copyright (c) 2016 Allwinnertech Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + */
> +
> +#include <asm/io.h>
> +#include <drm/drm_gem_cma_helper.h>
> +
> +#include "de2_drm.h"
> +#include "de2_crtc.h"
> +
> +static DEFINE_SPINLOCK(de_lock);
> +
> +#define DE_CLK_RATE_A83T 504000000 /* pll-de */
> +#define DE_CLK_RATE_H3 432000000 /* de */
This can be set in the DT.
> +
> +/* I/O map */
> +
> +#define DE_MOD_REG 0x0000 /* 1 bit per LCD */
> +#define DE_GATE_REG 0x0004
> +#define DE_RESET_REG 0x0008
> +#define DE_DIV_REG 0x000c /* 4 bits per LCD */
> +#define DE_SEL_REG 0x0010
> +
> +#define DE_MUX0_BASE 0x00100000
> +#define DE_MUX1_BASE 0x00200000
> +
> +/* MUX registers (addr / MUX base) */
> +#define DE_MUX_GLB_REGS 0x00000 /* global control */
> +#define DE_MUX_BLD_REGS 0x01000 /* alpha blending */
> +#define DE_MUX_CHAN_REGS 0x02000 /* VI/UI overlay channels */
> +#define DE_MUX_CHAN_SZ 0x1000 /* size of a channel */
> +#define DE_MUX_VSU_REGS 0x20000 /* VSU */
> +#define DE_MUX_GSU1_REGS 0x30000 /* GSUs */
> +#define DE_MUX_GSU2_REGS 0x40000
> +#define DE_MUX_GSU3_REGS 0x50000
> +#define DE_MUX_FCE_REGS 0xa0000 /* FCE */
> +#define DE_MUX_BWS_REGS 0xa2000 /* BWS */
> +#define DE_MUX_LTI_REGS 0xa4000 /* LTI */
> +#define DE_MUX_PEAK_REGS 0xa6000 /* PEAK */
> +#define DE_MUX_ASE_REGS 0xa8000 /* ASE */
> +#define DE_MUX_FCC_REGS 0xaa000 /* FCC */
> +#define DE_MUX_DCSC_REGS 0xb0000 /* DCSC/SMBL */
> +
> +/* global control */
> +struct de_glb {
> + u32 ctl;
> +#define DE_MUX_GLB_CTL_rt_en BIT(0)
> +#define DE_MUX_GLB_CTL_finish_irq_en BIT(4)
> +#define DE_MUX_GLB_CTL_rtwb_port BIT(12)
> + u32 status;
> + u32 dbuff;
> + u32 size;
> +};
> +
> +/* alpha blending */
> +struct de_bld {
> + u32 fcolor_ctl; /* 00 */
> + struct {
> + u32 fcolor;
> + u32 insize;
> + u32 offset;
> + u32 dum;
> + } attr[4];
> + u32 dum0[15]; /* (end of clear offset) */
> + u32 route; /* 80 */
> + u32 premultiply;
> + u32 bkcolor;
> + u32 output_size;
> + u32 bld_mode[4];
> + u32 dum1[4];
> + u32 ck_ctl; /* b0 */
> + u32 ck_cfg;
> + u32 dum2[2];
> + u32 ck_max[4]; /* c0 */
> + u32 dum3[4];
> + u32 ck_min[4]; /* e0 */
> + u32 dum4[3];
> + u32 out_ctl; /* fc */
> +};
> +
> +/* VI channel */
> +struct de_vi {
> + struct {
> + u32 attr;
> +#define VI_CFG_ATTR_en BIT(0)
> +#define VI_CFG_ATTR_fcolor_en BIT(4)
> +#define VI_CFG_ATTR_fmt_SHIFT 8
> +#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
> +#define VI_CFG_ATTR_ui_sel BIT(15)
> +#define VI_CFG_ATTR_top_down BIT(23)
> + u32 size;
> + u32 coord;
> +#define VI_N_PLANES 3
> + u32 pitch[VI_N_PLANES];
> + u32 top_laddr[VI_N_PLANES];
> + u32 bot_laddr[VI_N_PLANES];
> + } cfg[4];
> + u32 fcolor[4]; /* c0 */
> + u32 top_haddr[VI_N_PLANES]; /* d0 */
> + u32 bot_haddr[VI_N_PLANES]; /* dc */
> + u32 ovl_size[2]; /* e8 */
> + u32 hori[2]; /* f0 */
> + u32 vert[2]; /* f8 */
> +};
> +
> +/* UI channel */
> +struct de_ui {
> + struct {
> + u32 attr;
> +#define UI_CFG_ATTR_en BIT(0)
> +#define UI_CFG_ATTR_alpmod_SHIFT 1
> +#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)
> +#define UI_CFG_ATTR_fcolor_en BIT(4)
> +#define UI_CFG_ATTR_fmt_SHIFT 8
> +#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
> +#define UI_CFG_ATTR_top_down BIT(23)
> +#define UI_CFG_ATTR_alpha_SHIFT 24
> +#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)
> + u32 size;
> + u32 coord;
> + u32 pitch;
> + u32 top_laddr;
> + u32 bot_laddr;
> + u32 fcolor;
> + u32 dum;
> + } cfg[4]; /* 00 */
> + u32 top_haddr; /* 80 */
> + u32 bot_haddr;
> + u32 ovl_size; /* 88 */
> +};
Please use defines instead of the structures.
> +
> +/* coordinates and sizes */
> +#define XY(x, y) (((y) << 16) | (x))
> +#define WH(w, h) (((h - 1) << 16) | (w - 1))
> +
> +/* UI video formats */
> +#define DE2_FORMAT_ARGB_8888 0
> +#define DE2_FORMAT_BGRA_8888 3
> +#define DE2_FORMAT_XRGB_8888 4
> +#define DE2_FORMAT_RGB_888 8
> +#define DE2_FORMAT_BGR_888 9
> +
> +/* VI video formats */
> +#define DE2_FORMAT_YUV422_I_YVYU 1 /* Y-V-Y-U */
> +#define DE2_FORMAT_YUV422_I_UYVY 2 /* U-Y-V-Y */
> +#define DE2_FORMAT_YUV422_I_YUYV 3 /* Y-U-Y-V */
> +#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */
> +#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
> +
> +#define glb_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_glb, member))
> +#define glb_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_glb, member))
> +#define bld_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_bld, member))
> +#define bld_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_bld, member))
> +#define ui_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_ui, member))
> +#define ui_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_ui, member))
> +#define vi_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_vi, member))
> +#define vi_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_vi, member))
> +
> +static const struct {
> + char chan;
> + char layer;
> + char pipe;
> +} plane2layer[DE2_N_PLANES] = {
> + [DE2_PRIMARY_PLANE] = {0, 0, 0},
> + [DE2_CURSOR_PLANE] = {1, 0, 1},
> + [DE2_VI_PLANE] = {0, 1, 0},
> +};
Comments?
> +static inline void de_write(struct priv *priv, int reg, u32 data)
> +{
> + writel_relaxed(data, priv->mmio + reg);
> +}
> +
> +static inline u32 de_read(struct priv *priv, int reg)
> +{
> + return readl_relaxed(priv->mmio + reg);
> +}
> +
> +static void de_lcd_select(struct priv *priv,
> + int lcd_num,
> + void __iomem *mux_o)
> +{
> + u32 data;
> +
> + /* select the LCD */
> + data = de_read(priv, DE_SEL_REG);
> + data &= ~1;
> + de_write(priv, DE_SEL_REG, data);
> +
> + /* double register switch */
> + glb_write(mux_o + DE_MUX_GLB_REGS, dbuff, 1);
> +}
> +
> +void de2_de_plane_update(struct priv *priv,
> + int lcd_num, int plane_ix,
> + struct drm_plane_state *state,
> + struct drm_plane_state *old_state)
> +{
> + struct drm_framebuffer *fb = state->fb;
> + struct drm_gem_cma_object *gem;
> + void __iomem *mux_o = priv->mmio;
> + void __iomem *chan_o;
> + u32 size = WH(state->crtc_w, state->crtc_h);
> + u32 coord;
> + u32 screen_size;
> + u32 data, fcolor;
> + u32 ui_sel, alpha_glob;
> + int chan, layer, x, y;
> + unsigned fmt;
> + unsigned long flags;
> +
> + chan = plane2layer[plane_ix].chan;
> + layer = plane2layer[plane_ix].layer;
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> + chan_o = mux_o;
> + chan_o += DE_MUX_CHAN_REGS + DE_MUX_CHAN_SZ * chan;
> +
> + x = state->crtc_x >= 0 ? state->crtc_x : 0;
> + y = state->crtc_y >= 0 ? state->crtc_y : 0;
> + coord = XY(x, y);
> +
> + /* handle the cursor move */
> + if (plane_ix == DE2_CURSOR_PLANE
> + && fb == old_state->fb) {
> + spin_lock_irqsave(&de_lock, flags);
> + de_lcd_select(priv, lcd_num, mux_o);
> + if (chan == 0)
> + vi_write(chan_o, cfg[layer].coord, coord);
> + else
> + ui_write(chan_o, cfg[layer].coord, coord);
> + spin_unlock_irqrestore(&de_lock, flags);
> + return;
> + }
> +
> + gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> + ui_sel = alpha_glob = 0;
> + switch (fb->pixel_format) {
> + case DRM_FORMAT_ARGB8888:
> + fmt = DE2_FORMAT_ARGB_8888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_BGRA8888:
> + fmt = DE2_FORMAT_BGRA_8888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_XRGB8888:
> + fmt = DE2_FORMAT_XRGB_8888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |
> + (0xff << UI_CFG_ATTR_alpha_SHIFT);
> + break;
> + case DRM_FORMAT_RGB888:
> + fmt = DE2_FORMAT_RGB_888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_BGR888:
> + fmt = DE2_FORMAT_BGR_888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_YUYV:
> + fmt = DE2_FORMAT_YUV422_I_YUYV;
> + break;
> + case DRM_FORMAT_YVYU:
> + fmt = DE2_FORMAT_YUV422_I_YVYU;
> + break;
> + case DRM_FORMAT_YUV422:
> + fmt = DE2_FORMAT_YUV422_P;
> + break;
> + case DRM_FORMAT_YUV420:
> + fmt = DE2_FORMAT_YUV420_P;
> + break;
> + case DRM_FORMAT_UYVY:
> + fmt = DE2_FORMAT_YUV422_I_UYVY;
> + break;
> + default:
> + pr_err("format %.4s not yet treated\n",
> + (char *) &fb->pixel_format);
> + return;
> + }
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + screen_size = plane_ix == DE2_PRIMARY_PLANE ?
> + size :
> + glb_read(mux_o + DE_MUX_GLB_REGS, size);
> +
> + /* prepare the activation of alpha blending (1 bit per plane) */
> + fcolor = bld_read(mux_o + DE_MUX_BLD_REGS, fcolor_ctl)
> + | (0x100 << plane2layer[plane_ix].pipe);
> +
> + de_lcd_select(priv, lcd_num, mux_o);
> +
> + if (chan == 0) { /* VI channel */
> + int i;
> +
> + data = VI_CFG_ATTR_en | (fmt << VI_CFG_ATTR_fmt_SHIFT) |
> + ui_sel;
> + vi_write(chan_o, cfg[layer].attr, data);
> + vi_write(chan_o, cfg[layer].size, size);
> + vi_write(chan_o, cfg[layer].coord, coord);
> + for (i = 0; i < VI_N_PLANES; i++) {
> + vi_write(chan_o, cfg[layer].pitch[i],
> + fb->pitches[i] ? fb->pitches[i] :
> + fb->pitches[0]);
> + vi_write(chan_o, cfg[layer].top_laddr[i],
> + gem->paddr + fb->offsets[i]);
> + vi_write(chan_o, fcolor[layer], 0xff000000);
> + }
> + if (layer == 0)
> + vi_write(chan_o, ovl_size[0], screen_size);
> +
> + } else { /* UI channel */
> + data = UI_CFG_ATTR_en | (fmt << UI_CFG_ATTR_fmt_SHIFT) |
> + alpha_glob;
> + ui_write(chan_o, cfg[layer].attr, data);
> + ui_write(chan_o, cfg[layer].size, size);
> + ui_write(chan_o, cfg[layer].coord, coord);
> + ui_write(chan_o, cfg[layer].pitch, fb->pitches[0]);
> + ui_write(chan_o, cfg[layer].top_laddr,
> + gem->paddr + fb->offsets[0]);
> + if (layer == 0)
> + ui_write(chan_o, ovl_size, screen_size);
> + }
> + bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl, fcolor);
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
Splitting that into functions would make it a bit more trivial and
readable.
> +void de2_de_plane_disable(struct priv *priv,
> + int lcd_num, int plane_ix)
> +{
> + void __iomem *mux_o = priv->mmio;
> + void __iomem *chan_o;
> + u32 fcolor;
> + int chan, layer, chan_disable = 0;
> + unsigned long flags;
> +
> + chan = plane2layer[plane_ix].chan;
> + layer = plane2layer[plane_ix].layer;
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> + chan_o = mux_o;
> + chan_o += DE_MUX_CHAN_REGS + DE_MUX_CHAN_SZ * chan;
> +
> + /* (only 2 layers) */
> + if (chan == 0) {
> + if (vi_read(chan_o, cfg[1 - layer].attr) == 0)
> + chan_disable = 1;
> + } else {
> + if (ui_read(chan_o, cfg[1 - layer].attr) == 0)
> + chan_disable = 1;
> + }
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + fcolor = bld_read(mux_o + DE_MUX_BLD_REGS, fcolor_ctl);
> +
> + de_lcd_select(priv, lcd_num, mux_o);
> +
> + if (chan == 0)
> + vi_write(chan_o, cfg[layer].attr, 0);
> + else
> + ui_write(chan_o, cfg[layer].attr, 0);
> +
> + if (chan_disable)
> + bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl,
> + fcolor & ~(0x100 << plane2layer[plane_ix].pipe));
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
Can't you just disable it?
> +void de2_de_panel_init(struct priv *priv, int lcd_num,
> + struct drm_display_mode *mode)
> +{
> + void __iomem *mux_o = priv->mmio;
> + u32 size = WH(mode->hdisplay, mode->vdisplay);
> + unsigned i;
> + unsigned long flags;
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> +
> + DRM_DEBUG_DRIVER("%dx%d\n", mode->hdisplay, mode->vdisplay);
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + de_lcd_select(priv, lcd_num, mux_o);
> +
> + glb_write(mux_o + DE_MUX_GLB_REGS, size, size);
> +
> + /* set alpha blending */
> + for (i = 0; i < 4; i++) {
> + bld_write(mux_o + DE_MUX_BLD_REGS, attr[i].fcolor, 0xff000000);
> + bld_write(mux_o + DE_MUX_BLD_REGS, attr[i].insize, size);
> + }
> + bld_write(mux_o + DE_MUX_BLD_REGS, output_size, size);
> + bld_write(mux_o + DE_MUX_BLD_REGS, out_ctl,
> + mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0);
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
> +
> +void de2_de_enable(struct priv *priv, int lcd_num)
> +{
> + void __iomem *mux_o = priv->mmio;
> + unsigned chan, i;
> + u32 size = WH(1920, 1080);
> + u32 data;
> + unsigned long flags;
> +
> + DRM_DEBUG_DRIVER("lcd %d\n", lcd_num);
> +
> + de_write(priv, DE_RESET_REG,
> + de_read(priv, DE_RESET_REG) |
> + (lcd_num == 0 ? 1 : 4));
> + data = 1 << lcd_num; /* 1 bit / lcd */
> + de_write(priv, DE_GATE_REG,
> + de_read(priv, DE_GATE_REG) | data);
> + de_write(priv, DE_MOD_REG,
> + de_read(priv, DE_MOD_REG) | data);
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + /* select the LCD */
> + data = de_read(priv, DE_SEL_REG);
> + if (lcd_num == 0)
> + data &= ~1;
> + else
> + data |= 1;
> + de_write(priv, DE_SEL_REG, data);
> +
> + /* start init */
> + glb_write(mux_o + DE_MUX_GLB_REGS, ctl,
> + DE_MUX_GLB_CTL_rt_en | DE_MUX_GLB_CTL_rtwb_port);
> + glb_write(mux_o + DE_MUX_GLB_REGS, status, 0);
> + glb_write(mux_o + DE_MUX_GLB_REGS, dbuff, 1); /* dble reg switch */
> + glb_write(mux_o + DE_MUX_GLB_REGS, size, size);
> +
> + /* clear the VI/UI channels */
> + for (chan = 0; chan < 4; chan++) {
> + void __iomem *chan_o = mux_o + DE_MUX_CHAN_REGS +
> + DE_MUX_CHAN_SZ * chan;
> +
> + memset_io(chan_o, 0, chan == 0 ?
> + sizeof(struct de_vi) : sizeof(struct de_ui));
> +
> + /* only 1 VI and 1 UI in lcd1 */
> + if (chan == 2 && lcd_num == 1)
> + break;
> + }
> +
> + /* clear and set alpha blending */
> + memset_io(mux_o + DE_MUX_BLD_REGS, 0, offsetof(struct de_bld, dum0));
> + bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl, 0x00000101);
> + /* fcolor for primary */
> +
> + /* prepare route for planes */
> + data = 0;
> + for (i = 0; i < DE2_N_PLANES; i++)
> + data |= plane2layer[i].chan << (plane2layer[i].pipe * 4);
> + bld_write(mux_o + DE_MUX_BLD_REGS, route, data);
> +
> + bld_write(mux_o + DE_MUX_BLD_REGS, premultiply, 0);
> + bld_write(mux_o + DE_MUX_BLD_REGS, bkcolor, 0xff000000);
> + bld_write(mux_o + DE_MUX_BLD_REGS, bld_mode[0], 0x03010301);
> + /* SRCOVER */
> + bld_write(mux_o + DE_MUX_BLD_REGS, bld_mode[1], 0x03010301);
> + bld_write(mux_o + DE_MUX_BLD_REGS, out_ctl, 0);
> +
> + /* disable the enhancements */
> + writel_relaxed(0, mux_o + DE_MUX_VSU_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_GSU1_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_GSU2_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_GSU3_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_FCE_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_BWS_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_LTI_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_PEAK_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_ASE_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_FCC_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_DCSC_REGS);
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
> +
> +void de2_de_disable(struct priv *priv, int lcd_num)
> +{
> + u32 data;
> +
> + data = ~(1 << lcd_num);
> + de_write(priv, DE_MOD_REG,
> + de_read(priv, DE_MOD_REG) & data);
> + de_write(priv, DE_GATE_REG,
> + de_read(priv, DE_GATE_REG) & data);
> + de_write(priv, DE_RESET_REG,
> + de_read(priv, DE_RESET_REG) & data);
> +}
> +
> +int de2_de_init(struct priv *priv, struct device *dev)
> +{
> + struct resource *res;
> + int ret;
> +
> + DRM_DEBUG_DRIVER("\n");
> +
> + res = platform_get_resource(to_platform_device(dev),
> + IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "failed to get memory resource\n");
> + return -EINVAL;
> + }
> +
> + priv->mmio = devm_ioremap_resource(dev, res);
> + if (IS_ERR(priv->mmio)) {
> + dev_err(dev, "failed to map registers\n");
> + return PTR_ERR(priv->mmio);
> + }
> +
> + priv->gate = devm_clk_get(dev, "gate"); /* optional */
Error checking
> +
> + priv->clk = devm_clk_get(dev, "clock");
> + if (IS_ERR(priv->clk)) {
> + dev_err(dev, "video clock err %d\n", (int) PTR_ERR(priv->clk));
> + return PTR_ERR(priv->clk);
> + }
> +
> + priv->rstc = devm_reset_control_get_optional(dev, NULL);
> +
> + if (!IS_ERR(priv->rstc)) {
> + ret = reset_control_deassert(priv->rstc);
> + if (ret) {
> + dev_err(dev, "reset deassert err %d\n", ret);
> + return ret;
> + }
> + }
> +
> + if (!IS_ERR(priv->gate)) {
> + ret = clk_prepare_enable(priv->gate);
> + if (ret)
> + goto err_gate;
> + }
> +
> + ret = clk_prepare_enable(priv->clk);
> + if (ret)
> + goto err_enable;
> + if (priv->soc_type == SOC_A83T)
> + clk_set_rate(priv->clk, DE_CLK_RATE_A83T);
> + else
> + clk_set_rate(priv->clk, DE_CLK_RATE_H3);
> +
> + /* set the A83T clock divider = 500 / 250 */
> + if (priv->soc_type == SOC_A83T)
> + de_write(priv, DE_DIV_REG,
> + 0x00000011); /* div = 2 for both LCDs */
> +
> + return 0;
> +
> +err_enable:
> + clk_disable_unprepare(priv->gate);
> +err_gate:
> + if (!IS_ERR(priv->rstc))
> + reset_control_assert(priv->rstc);
> + return ret;
> +}
> +
> +void de2_de_cleanup(struct priv *priv)
> +{
> + clk_disable_unprepare(priv->clk);
> + clk_disable_unprepare(priv->gate);
> + if (!IS_ERR(priv->rstc))
> + reset_control_assert(priv->rstc);
> +}
> diff --git a/drivers/gpu/drm/sunxi/de2_drm.h b/drivers/gpu/drm/sunxi/de2_drm.h
> new file mode 100644
> index 0000000..7bb966c
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_drm.h
> @@ -0,0 +1,47 @@
> +#ifndef __DE2_DRM_H__
> +#define __DE2_DRM_H__
> +/*
> + * Copyright (C) 2016 Jean-Fran?ois Moine
> + *
> + * 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/clk.h>
> +#include <linux/reset.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_cma_helper.h>
> +
> +struct lcd;
> +
> +#define N_LCDS 2
> +
> +/* SoC types */
> +#define SOC_A83T 0
> +#define SOC_H3 1
> +
> +struct priv {
> + void __iomem *mmio;
> + struct clk *clk;
> + struct clk *gate;
> + struct reset_control *rstc;
> +
> + int soc_type;
> +
> + struct drm_fbdev_cma *fbdev;
> +
> + struct lcd *lcds[N_LCDS];
> +};
> +
> +/* in de2_crtc.c */
> +int de2_enable_vblank(struct drm_device *drm, unsigned crtc);
> +void de2_disable_vblank(struct drm_device *drm, unsigned crtc);
> +extern struct platform_driver de2_lcd_platform_driver;
> +
> +/* in de2_de.c */
> +int de2_de_init(struct priv *priv, struct device *dev);
> +void de2_de_cleanup(struct priv *priv);
> +
> +#endif /* __DE2_DRM_H__ */
> diff --git a/drivers/gpu/drm/sunxi/de2_drv.c b/drivers/gpu/drm/sunxi/de2_drv.c
> new file mode 100644
> index 0000000..5daa15c
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_drv.c
> @@ -0,0 +1,378 @@
> +/*
> + * Allwinner DRM driver - DE2 DRM driver
> + *
> + * 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/module.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/component.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +
> +#include "de2_drm.h"
> +
> +#define DRIVER_NAME "sunxi-de2"
> +#define DRIVER_DESC "Allwinner DRM DE2"
> +#define DRIVER_DATE "20161001"
> +#define DRIVER_MAJOR 1
> +#define DRIVER_MINOR 0
> +
> +static struct of_device_id de2_drm_of_match[] = {
> + { .compatible = "allwinner,sun8i-a83t-display-engine",
> + .data = (void *) SOC_A83T },
> + { .compatible = "allwinner,sun8i-h3-display-engine",
> + .data = (void *) SOC_H3 },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, de2_drm_of_match);
> +
> +static void de2_fb_output_poll_changed(struct drm_device *drm)
> +{
> + struct priv *priv = drm->dev_private;
> +
> + if (priv->fbdev)
> + drm_fbdev_cma_hotplug_event(priv->fbdev);
> +}
> +
> +static const struct drm_mode_config_funcs de2_mode_config_funcs = {
> + .fb_create = drm_fb_cma_create,
> + .output_poll_changed = de2_fb_output_poll_changed,
> + .atomic_check = drm_atomic_helper_check,
> + .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +/*
> + * DRM operations:
> + */
> +static void de2_lastclose(struct drm_device *drm)
> +{
> + struct priv *priv = drm->dev_private;
> +
> + if (priv->fbdev)
> + drm_fbdev_cma_restore_mode(priv->fbdev);
> +}
> +
> +static const struct file_operations de2_fops = {
> + .owner = THIS_MODULE,
> + .open = drm_open,
> + .release = drm_release,
> + .unlocked_ioctl = drm_ioctl,
> + .poll = drm_poll,
> + .read = drm_read,
> + .llseek = no_llseek,
> + .mmap = drm_gem_cma_mmap,
> +};
> +
> +static struct drm_driver de2_drm_driver = {
> + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
> + DRIVER_ATOMIC,
> + .lastclose = de2_lastclose,
> + .get_vblank_counter = drm_vblank_no_hw_counter,
> + .enable_vblank = de2_enable_vblank,
> + .disable_vblank = de2_disable_vblank,
> + .gem_free_object = drm_gem_cma_free_object,
> + .gem_vm_ops = &drm_gem_cma_vm_ops,
> + .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> + .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> + .gem_prime_import = drm_gem_prime_import,
> + .gem_prime_export = drm_gem_prime_export,
> + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
> + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> + .gem_prime_vmap = drm_gem_cma_prime_vmap,
> + .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
> + .gem_prime_mmap = drm_gem_cma_prime_mmap,
> + .dumb_create = drm_gem_cma_dumb_create,
> + .dumb_map_offset = drm_gem_cma_dumb_map_offset,
> + .dumb_destroy = drm_gem_dumb_destroy,
> + .fops = &de2_fops,
> + .name = DRIVER_NAME,
> + .desc = DRIVER_DESC,
> + .date = DRIVER_DATE,
> + .major = DRIVER_MAJOR,
> + .minor = DRIVER_MINOR,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +/*
> + * Power management
> + */
> +static int de2_pm_suspend(struct device *dev)
> +{
> + struct drm_device *drm = dev_get_drvdata(dev);
> +
> + drm_kms_helper_poll_disable(drm);
> + return 0;
> +}
> +
> +static int de2_pm_resume(struct device *dev)
> +{
> + struct drm_device *drm = dev_get_drvdata(dev);
> +
> + drm_kms_helper_poll_enable(drm);
> + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops de2_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(de2_pm_suspend, de2_pm_resume)
> +};
Why do you need that? How did you test it? There's no runtime_pm calls
in your kernel.
> +/*
> + * Platform driver
> + */
> +
> +static int de2_drm_bind(struct device *dev)
> +{
> + struct drm_device *drm;
> + struct priv *priv;
> + int ret;
> +
> + drm = drm_dev_alloc(&de2_drm_driver, dev);
> + if (!drm)
> + return -ENOMEM;
> +
> + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(dev, "failed to allocate private area\n");
> + ret = -ENOMEM;
> + goto out1;
> + }
> +
> + dev_set_drvdata(dev, drm);
> + drm->dev_private = priv;
> +
> + drm_mode_config_init(drm);
> + drm->mode_config.min_width = 32; /* needed for cursor */
> + drm->mode_config.min_height = 32;
> + drm->mode_config.max_width = 1920;
> + drm->mode_config.max_height = 1080;
> + drm->mode_config.funcs = &de2_mode_config_funcs;
> +
> + drm->irq_enabled = true;
> +
> + /* initialize the display engine */
> + priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
> + ret = de2_de_init(priv, dev);
> + if (ret)
> + goto out2;
> +
> + /* start the subdevices */
> + ret = component_bind_all(dev, drm);
> + if (ret < 0)
> + goto out2;
> +
> + ret = drm_dev_register(drm, 0);
> + if (ret < 0)
> + goto out3;
> +
> + DRM_DEBUG_DRIVER("%d crtcs %d connectors\n",
> + drm->mode_config.num_crtc,
> + drm->mode_config.num_connector);
> +
> + ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
> + if (ret < 0)
> + dev_warn(dev, "failed to initialize vblank\n");
> +
> + drm_mode_config_reset(drm);
> +
> + priv->fbdev = drm_fbdev_cma_init(drm,
> + 32, /* bpp */
> + drm->mode_config.num_crtc,
> + drm->mode_config.num_connector);
> + if (IS_ERR(priv->fbdev)) {
> + ret = PTR_ERR(priv->fbdev);
> + priv->fbdev = NULL;
> + goto out4;
> + }
> +
> + drm_kms_helper_poll_init(drm);
> +
> + return 0;
> +
> +out4:
> + drm_dev_unregister(drm);
> +out3:
> + component_unbind_all(dev, drm);
> +out2:
> + kfree(priv);
> +out1:
> + drm_dev_unref(drm);
> + return ret;
> +}
> +
> +static void de2_drm_unbind(struct device *dev)
> +{
> + struct drm_device *drm = dev_get_drvdata(dev);
> + struct priv *priv = drm->dev_private;
> +
> + if (priv)
> + drm_fbdev_cma_fini(priv->fbdev);
> + drm_kms_helper_poll_fini(drm);
> +
> + drm_dev_unregister(drm);
> + drm_vblank_cleanup(drm);
> +
> + drm_mode_config_cleanup(drm);
> +
> + component_unbind_all(dev, drm);
> +
> + if (priv) {
> + de2_de_cleanup(priv);
> + kfree(priv);
> + }
> +
> + drm_dev_unref(drm);
> +}
> +
> +static const struct component_master_ops de2_drm_comp_ops = {
> + .bind = de2_drm_bind,
> + .unbind = de2_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> + return dev->of_node == data;
> +}
> +
> +static int de2_drm_add_components(struct device *dev,
> + int (*compare_of)(struct device *, void *),
> + const struct component_master_ops *m_ops)
> +{
> + struct device_node *ep, *port, *remote;
> + struct component_match *match = NULL;
> + int i;
> +
> + if (!dev->of_node)
> + return -EINVAL;
> +
> + /* bind the CRTCs */
> + for (i = 0; ; i++) {
> + port = of_parse_phandle(dev->of_node, "ports", i);
> + if (!port)
> + break;
> +
> + if (!of_device_is_available(port->parent)) {
> + of_node_put(port);
> + continue;
> + }
> +
> + component_match_add(dev, &match, compare_of, port->parent);
> + of_node_put(port);
> + }
> +
> + if (i == 0) {
> + dev_err(dev, "missing 'ports' property\n");
> + return -ENODEV;
> + }
> + if (!match) {
> + dev_err(dev, "no available port\n");
> + return -ENODEV;
> + }
> +
> + /* bind the encoders/connectors */
> + for (i = 0; ; i++) {
> + port = of_parse_phandle(dev->of_node, "ports", i);
> + if (!port)
> + break;
> +
> + if (!of_device_is_available(port->parent)) {
> + of_node_put(port);
> + continue;
> + }
> +
> + for_each_child_of_node(port, ep) {
> + remote = of_graph_get_remote_port_parent(ep);
> + if (!remote || !of_device_is_available(remote)) {
> + of_node_put(remote);
> + continue;
> + }
> + if (!of_device_is_available(remote->parent)) {
> + dev_warn(dev,
> + "parent device of %s is not available\n",
> + remote->full_name);
> + of_node_put(remote);
> + continue;
> + }
> +
> + component_match_add(dev, &match, compare_of, remote);
> + of_node_put(remote);
> + }
> + of_node_put(port);
> + }
> +
> + return component_master_add_with_match(dev, m_ops, match);
> +}
> +
> +static int de2_drm_probe(struct platform_device *pdev)
> +{
> + int ret;
> +
> + ret = de2_drm_add_components(&pdev->dev,
> + compare_of,
> + &de2_drm_comp_ops);
> + if (ret == -EINVAL)
> + ret = -ENXIO;
> + return ret;
> +}
> +
> +static int de2_drm_remove(struct platform_device *pdev)
> +{
> + component_master_del(&pdev->dev, &de2_drm_comp_ops);
> +
> + return 0;
> +}
> +
> +static struct platform_driver de2_drm_platform_driver = {
> + .probe = de2_drm_probe,
> + .remove = de2_drm_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .pm = &de2_pm_ops,
> + .of_match_table = de2_drm_of_match,
> + },
> +};
> +
> +static int __init de2_drm_init(void)
> +{
> + int ret;
> +
> +/* uncomment to activate the drm traces at startup time */
> +/* drm_debug = DRM_UT_CORE | DRM_UT_DRIVER | DRM_UT_KMS |
> + DRM_UT_PRIME | DRM_UT_ATOMIC; */
That's useless.
> + DRM_DEBUG_DRIVER("\n");
> +
> + ret = platform_driver_register(&de2_lcd_platform_driver);
> + if (ret < 0)
> + return ret;
> +
> + ret = platform_driver_register(&de2_drm_platform_driver);
> + if (ret < 0)
> + platform_driver_unregister(&de2_lcd_platform_driver);
> +
> + return ret;
> +}
And that really shouldn't be done that way.
> +static void __exit de2_drm_fini(void)
> +{
> + platform_driver_unregister(&de2_lcd_platform_driver);
> + platform_driver_unregister(&de2_drm_platform_driver);
> +}
> +
> +module_init(de2_drm_init);
> +module_exit(de2_drm_fini);
> +
> +MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
> +MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sunxi/de2_plane.c b/drivers/gpu/drm/sunxi/de2_plane.c
> new file mode 100644
> index 0000000..b338684
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_plane.c
> @@ -0,0 +1,119 @@
> +/*
> + * Allwinner DRM driver - DE2 planes
> + *
> + * 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 <drm/drm_atomic_helper.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "de2_drm.h"
> +#include "de2_crtc.h"
> +
> +/* plane formats */
> +static const uint32_t ui_formats[] = {
> + DRM_FORMAT_ARGB8888,
> + DRM_FORMAT_BGRA8888,
> + DRM_FORMAT_XRGB8888,
> + DRM_FORMAT_RGB888,
> + DRM_FORMAT_BGR888,
> +};
> +
> +static const uint32_t vi_formats[] = {
> + DRM_FORMAT_XRGB8888,
> + DRM_FORMAT_YUYV,
> + DRM_FORMAT_YVYU,
> + DRM_FORMAT_YUV422,
> + DRM_FORMAT_YUV420,
> + DRM_FORMAT_UYVY,
> + DRM_FORMAT_BGRA8888,
> + DRM_FORMAT_RGB888,
> + DRM_FORMAT_BGR888,
> +};
> +
> +static void de2_plane_disable(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct drm_crtc *crtc = old_state->crtc;
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + int plane_num = plane - lcd->planes;
> +
> + de2_de_plane_disable(lcd->priv, lcd->num, plane_num);
> +}
> +
> +static void de2_plane_update(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct drm_plane_state *state = plane->state;
> + struct drm_crtc *crtc = state->crtc;
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + struct drm_framebuffer *fb = state->fb;
> + int plane_num = plane - lcd->planes;
> +
> + if (!crtc || !fb) {
> + DRM_DEBUG_DRIVER("no crtc/fb\n");
> + return;
> + }
> +
> + de2_de_plane_update(lcd->priv, lcd->num, plane_num,
> + state, old_state);
> +}
> +
> +static const struct drm_plane_helper_funcs plane_helper_funcs = {
> + .atomic_disable = de2_plane_disable,
> + .atomic_update = de2_plane_update,
> +};
> +
> +static const struct drm_plane_funcs plane_funcs = {
> + .update_plane = drm_atomic_helper_update_plane,
> + .disable_plane = drm_atomic_helper_disable_plane,
> + .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 int de2_one_plane_init(struct drm_device *drm,
> + struct drm_plane *plane,
> + int type, int possible_crtcs,
> + const uint32_t *formats,
> + int nformats)
> +{
> + int ret;
> +
> + ret = drm_universal_plane_init(drm, plane, possible_crtcs,
> + &plane_funcs,
> + formats, nformats, type, NULL);
> + if (ret >= 0)
> + drm_plane_helper_add(plane, &plane_helper_funcs);
> +
> + return ret;
> +}
> +
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
> +{
> + int ret, possible_crtcs = 1 << lcd->crtc_idx;
> +
> + ret = de2_one_plane_init(drm, &lcd->planes[DE2_PRIMARY_PLANE],
> + DRM_PLANE_TYPE_PRIMARY, possible_crtcs,
> + ui_formats, ARRAY_SIZE(ui_formats));
> + if (ret >= 0)
> + ret = de2_one_plane_init(drm, &lcd->planes[DE2_CURSOR_PLANE],
> + DRM_PLANE_TYPE_CURSOR, possible_crtcs,
> + ui_formats, ARRAY_SIZE(ui_formats));
Nothing looks really special about that cursor plane. Any reasion not
to make it an overlay?
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/479b8a6a/attachment-0001.sig>
^ permalink raw reply
* [PATCH] ARM: dt: sun8i-h3: Add sunxi-sid to dts for sun8i-h3
From: Maxime Ripard @ 2016-10-24 14:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023082631.GA28485@Red>
On Sun, Oct 23, 2016 at 10:26:31AM +0200, LABBE Corentin wrote:
> On Thu, Oct 20, 2016 at 10:36:54PM +0200, Maxime Ripard wrote:
> > On Wed, Oct 19, 2016 at 09:40:16AM +0200, LABBE Corentin wrote:
> > > On Wed, Oct 05, 2016 at 12:21:30PM +0200, Jean-Francois Moine wrote:
> > > > On Wed, 5 Oct 2016 11:48:24 +0200
> > > > Corentin Labbe <clabbe.montjoie@gmail.com> wrote:
> > > >
> > > > > This patch add support for the sunxi-sid driver to the device tree for sun8i-h3.
> > > > >
> > > > > Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
> > > > > ---
> > > > > arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++
> > > > > 1 file changed, 5 insertions(+)
> > > > >
> > > > > diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > > index 9f58bb4..abfd29c 100644
> > > > > --- a/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > > +++ b/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > > @@ -211,6 +211,11 @@
> > > > > #size-cells = <0>;
> > > > > };
> > > > >
> > > > > + sid: eeprom at 01c14200 {
> > > > > + compatible = "allwinner,sun7i-a20-sid";
> > > > > + reg = <0x01c14200 0x200>;
> > > >
> > > > The datasheet says 1Kb starting at 0x01c14000.
> > > > Is there any reason to reduce the area and to shift the offset?
> > > >
> > >
> > > According to http://linux-sunxi.org/SID_Register_Guide "For
> > > Allwinner A83T and H3 the SID address space starts at 0x01c14000,
> > > and the e-fuses are at offset 0x200".
> > >
> > > So I use this offset, since the sunxi_sid driver need the base
> > > address of e-fuses.
> > >
> > > The easiest solution is to use 0x01c14200 since the other part of
> > > sid is not used and not known (A83T/H3 user manual doesnt give any
> > > information on all sid space, worse for A64 which reference SID only
> > > in memory map).
> > >
> > > So probably for H3/A64/A83T, there will never any usage of the rest
> > > of the SID address space.
> >
> > And since we can't know that, and we have to maintain the DT ABI,
> > using the whole address map and an offset, with a new compatible, is
> > definetely the safest thing to do.
> >
>
> I have two way of doing it, which one do you prefer ?
>
> - Adding two optionnal properties: efuses-offset and efuses-size defaulting to 0 and resourcesize.
> - Adding a subnode called efuses with its own reg=<>
>
> The first one is easy and didnt need any work on previous DT entries.
I'd prefer to have a compatible, and the size and/or offset of the
efuses associated to it.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/d93d1bab/attachment-0001.sig>
^ permalink raw reply
* [GIT PULL] i.MX fixes for 4.9
From: Shawn Guo @ 2016-10-24 14:06 UTC (permalink / raw)
To: linux-arm-kernel
The following changes since commit 1001354ca34179f3db924eb66672442a173147dc:
Linux 4.9-rc1 (2016-10-15 12:17:50 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git tags/imx-fixes-4.9
for you to fetch changes up to 4edd601c5a9c5094daa714e65063e623826f3bcc:
ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031 (2016-10-24 21:26:01 +0800)
----------------------------------------------------------------
The i.MX fixes for 4.9:
- A couple of patches from Fabio to fix the GPC power domain regression
which is caused by PM Domain core change 0159ec670763dd
("PM / Domains: Verify the PM domain is present when adding a
provider"), and a related kernel crash seen with multi_v7_defconfig
build.
- Correct the PHY ID mask for AR8031 to match phy driver code.
- Apply new added timer erratum A008585 for LS1043A and LS2080A SoC.
- Correct vf610 global timer IRQ flag to avoid warning from gic driver
after commit 992345a58e0c ("irqchip/gic: WARN if setting the
interrupt type for a PPI fails").
----------------------------------------------------------------
Fabio Estevam (3):
ARM: imx: gpc: Initialize all power domains
ARM: imx: gpc: Fix the imx_gpc_genpd_init() error path
ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031
Scott Wood (1):
arm64: dts: Add timer erratum property for LS2080A and LS1043A
Stefan Agner (1):
ARM: dts: vf610: fix IRQ flag of global timer
arch/arm/boot/dts/vf500.dtsi | 2 +-
arch/arm/mach-imx/gpc.c | 15 ++++++++++++---
arch/arm/mach-imx/mach-imx6q.c | 2 +-
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 1 +
arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi | 1 +
5 files changed, 16 insertions(+), 5 deletions(-)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox