Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b1f3dc8 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'exynos-drm-next' of...

Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

   This is final pull request for Exynos next and includes device tree
   support for fimc device, one revert, some code cleanups and fixup.
   The revert replaces wrong one[1] with correct one[2].
   This was my mistake and sorry for this.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
  drm/exynos: Don't blend mixer layer 0
  drm/exynos: Remove unnecessary braces in exynos_hdmi.c
  drm/exynos: Select VIDEOMODE_HELPERS for FIMD
  drm/exynos: do not use generic flags to dumb
  drm/exynos: added ipp device registration to drm driver
  exynos/drm: hdmi: cleanup for hdmi common device registration
  drm/exynos: fix wrong return check for platform_device_register_simple
  drm/exynos: add device tree support for fimc ipp driver
  drm/exynos: rework fimc clocks handling
  drm/exynos: remove redundant devm_kfree()
  drm/exynos: enable FIMD clocks
  Revert "drm/exynos: prepare FIMD clocks"
parents d8bf6b0d 0377f4ed
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -25,8 +25,8 @@ config DRM_EXYNOS_DMABUF
config DRM_EXYNOS_FIMD
	bool "Exynos DRM FIMD"
	depends on OF && DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
	select OF_VIDEOMODE
	select FB_MODE_HELPERS
	select VIDEOMODE_HELPERS
	help
	  Choose this option if you want to use Exynos FIMD for DRM.

@@ -56,7 +56,7 @@ config DRM_EXYNOS_IPP

config DRM_EXYNOS_FIMC
	bool "Exynos DRM FIMC"
	depends on DRM_EXYNOS_IPP
	depends on DRM_EXYNOS_IPP && MFD_SYSCON && OF
	help
	  Choose this option if you want to use Exynos FIMC for DRM.

+8 −1
Original line number Diff line number Diff line
@@ -380,6 +380,10 @@ static int __init exynos_drm_init(void)
	ret = platform_driver_register(&ipp_driver);
	if (ret < 0)
		goto out_ipp;

	ret = exynos_platform_device_ipp_register();
	if (ret < 0)
		goto out_ipp_dev;
#endif

	ret = platform_driver_register(&exynos_drm_platform_driver);
@@ -388,7 +392,7 @@ static int __init exynos_drm_init(void)

	exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
				NULL, 0);
	if (IS_ERR_OR_NULL(exynos_drm_pdev)) {
	if (IS_ERR(exynos_drm_pdev)) {
		ret = PTR_ERR(exynos_drm_pdev);
		goto out;
	}
@@ -400,6 +404,8 @@ out:

out_drm:
#ifdef CONFIG_DRM_EXYNOS_IPP
	exynos_platform_device_ipp_unregister();
out_ipp_dev:
	platform_driver_unregister(&ipp_driver);
out_ipp:
#endif
@@ -456,6 +462,7 @@ static void __exit exynos_drm_exit(void)
	platform_driver_unregister(&exynos_drm_platform_driver);

#ifdef CONFIG_DRM_EXYNOS_IPP
	exynos_platform_device_ipp_unregister();
	platform_driver_unregister(&ipp_driver);
#endif

+11 −1
Original line number Diff line number Diff line
@@ -322,13 +322,23 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
 * this function registers exynos drm hdmi platform device. It ensures only one
 * instance of the device is created.
 */
extern int exynos_platform_device_hdmi_register(void);
int exynos_platform_device_hdmi_register(void);

/*
 * this function unregisters exynos drm hdmi platform device if it exists.
 */
void exynos_platform_device_hdmi_unregister(void);

/*
 * this function registers exynos drm ipp platform device.
 */
int exynos_platform_device_ipp_register(void);

/*
 * this function unregisters exynos drm ipp platform device if it exists.
 */
void exynos_platform_device_ipp_unregister(void);

extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;
+153 −120
Original line number Diff line number Diff line
@@ -12,11 +12,12 @@
 *
 */
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <plat/map-base.h>

#include <drm/drmP.h>
#include <drm/exynos_drm.h>
@@ -76,6 +77,27 @@ enum fimc_wb {
	FIMC_WB_B,
};

enum {
	FIMC_CLK_LCLK,
	FIMC_CLK_GATE,
	FIMC_CLK_WB_A,
	FIMC_CLK_WB_B,
	FIMC_CLK_MUX,
	FIMC_CLK_PARENT,
	FIMC_CLKS_MAX
};

static const char * const fimc_clock_names[] = {
	[FIMC_CLK_LCLK]   = "sclk_fimc",
	[FIMC_CLK_GATE]   = "fimc",
	[FIMC_CLK_WB_A]   = "pxl_async0",
	[FIMC_CLK_WB_B]   = "pxl_async1",
	[FIMC_CLK_MUX]    = "mux",
	[FIMC_CLK_PARENT] = "parent",
};

#define FIMC_DEFAULT_LCLK_FREQUENCY 133000000UL

/*
 * A structure of scaler.
 *
@@ -118,15 +140,6 @@ struct fimc_capability {
	u32	rl_h_rot;
};

/*
 * A structure of fimc driver data.
 *
 * @parent_clk: name of parent clock.
 */
struct fimc_driverdata {
	char	*parent_clk;
};

/*
 * A structure of fimc context.
 *
@@ -134,13 +147,10 @@ struct fimc_driverdata {
 * @regs_res: register resources.
 * @regs: memory mapped io registers.
 * @lock: locking of operations.
 * @sclk_fimc_clk: fimc source clock.
 * @fimc_clk: fimc clock.
 * @wb_clk: writeback a clock.
 * @wb_b_clk: writeback b clock.
 * @clocks: fimc clocks.
 * @clk_frequency: LCLK clock frequency.
 * @sysreg: handle to SYSREG block regmap.
 * @sc: scaler infomations.
 * @odr: ordering of YUV.
 * @ver: fimc version.
 * @pol: porarity of writeback.
 * @id: fimc id.
 * @irq: irq number.
@@ -151,12 +161,10 @@ struct fimc_context {
	struct resource	*regs_res;
	void __iomem	*regs;
	struct mutex	lock;
	struct clk	*sclk_fimc_clk;
	struct clk	*fimc_clk;
	struct clk	*wb_clk;
	struct clk	*wb_b_clk;
	struct clk	*clocks[FIMC_CLKS_MAX];
	u32		clk_frequency;
	struct regmap	*sysreg;
	struct fimc_scaler	sc;
	struct fimc_driverdata	*ddata;
	struct exynos_drm_ipp_pol	pol;
	int	id;
	int	irq;
@@ -200,17 +208,13 @@ static void fimc_sw_reset(struct fimc_context *ctx)
	fimc_write(0x0, EXYNOS_CIFCNTSEQ);
}

static void fimc_set_camblk_fimd0_wb(struct fimc_context *ctx)
static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx)
{
	u32 camblk_cfg;

	DRM_DEBUG_KMS("%s\n", __func__);

	camblk_cfg = readl(SYSREG_CAMERA_BLK);
	camblk_cfg &= ~(SYSREG_FIMD0WB_DEST_MASK);
	camblk_cfg |= ctx->id << (SYSREG_FIMD0WB_DEST_SHIFT);

	writel(camblk_cfg, SYSREG_CAMERA_BLK);
	return regmap_update_bits(ctx->sysreg, SYSREG_CAMERA_BLK,
				  SYSREG_FIMD0WB_DEST_MASK,
				  ctx->id << SYSREG_FIMD0WB_DEST_SHIFT);
}

static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
@@ -1301,14 +1305,12 @@ static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable)
	DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable);

	if (enable) {
		clk_enable(ctx->sclk_fimc_clk);
		clk_enable(ctx->fimc_clk);
		clk_enable(ctx->wb_clk);
		clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]);
		clk_prepare_enable(ctx->clocks[FIMC_CLK_WB_A]);
		ctx->suspended = false;
	} else {
		clk_disable(ctx->sclk_fimc_clk);
		clk_disable(ctx->fimc_clk);
		clk_disable(ctx->wb_clk);
		clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]);
		clk_disable_unprepare(ctx->clocks[FIMC_CLK_WB_A]);
		ctx->suspended = true;
	}

@@ -1613,7 +1615,11 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
		fimc_handle_lastend(ctx, true);

		/* setup FIMD */
		fimc_set_camblk_fimd0_wb(ctx);
		ret = fimc_set_camblk_fimd0_wb(ctx);
		if (ret < 0) {
			dev_err(dev, "camblk setup failed.\n");
			return ret;
		}

		set_wb.enable = 1;
		set_wb.refresh = property->refresh_rate;
@@ -1713,75 +1719,117 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
	fimc_write(cfg, EXYNOS_CIGCTRL);
}

static int fimc_probe(struct platform_device *pdev)
static void fimc_put_clocks(struct fimc_context *ctx)
{
	struct device *dev = &pdev->dev;
	struct fimc_context *ctx;
	struct clk	*parent_clk;
	struct resource *res;
	struct exynos_drm_ippdrv *ippdrv;
	struct exynos_drm_fimc_pdata *pdata;
	struct fimc_driverdata *ddata;
	int ret;
	int i;

	pdata = pdev->dev.platform_data;
	if (!pdata) {
		dev_err(dev, "no platform data specified.\n");
		return -EINVAL;
	for (i = 0; i < FIMC_CLKS_MAX; i++) {
		if (IS_ERR(ctx->clocks[i]))
			continue;
		clk_put(ctx->clocks[i]);
		ctx->clocks[i] = ERR_PTR(-EINVAL);
	}
}

	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return -ENOMEM;
static int fimc_setup_clocks(struct fimc_context *ctx)
{
	struct device *fimc_dev = ctx->ippdrv.dev;
	struct device *dev;
	int ret, i;

	ddata = (struct fimc_driverdata *)
		platform_get_device_id(pdev)->driver_data;
	for (i = 0; i < FIMC_CLKS_MAX; i++)
		ctx->clocks[i] = ERR_PTR(-EINVAL);

	/* clock control */
	ctx->sclk_fimc_clk = devm_clk_get(dev, "sclk_fimc");
	if (IS_ERR(ctx->sclk_fimc_clk)) {
		dev_err(dev, "failed to get src fimc clock.\n");
		return PTR_ERR(ctx->sclk_fimc_clk);
	}
	clk_enable(ctx->sclk_fimc_clk);
	for (i = 0; i < FIMC_CLKS_MAX; i++) {
		if (i == FIMC_CLK_WB_A || i == FIMC_CLK_WB_B)
			dev = fimc_dev->parent;
		else
			dev = fimc_dev;

	ctx->fimc_clk = devm_clk_get(dev, "fimc");
	if (IS_ERR(ctx->fimc_clk)) {
		dev_err(dev, "failed to get fimc clock.\n");
		clk_disable(ctx->sclk_fimc_clk);
		return PTR_ERR(ctx->fimc_clk);
		ctx->clocks[i] = clk_get(dev, fimc_clock_names[i]);
		if (IS_ERR(ctx->clocks[i])) {
			if (i >= FIMC_CLK_MUX)
				break;
			ret = PTR_ERR(ctx->clocks[i]);
			dev_err(fimc_dev, "failed to get clock: %s\n",
						fimc_clock_names[i]);
			goto e_clk_free;
		}

	ctx->wb_clk = devm_clk_get(dev, "pxl_async0");
	if (IS_ERR(ctx->wb_clk)) {
		dev_err(dev, "failed to get writeback a clock.\n");
		clk_disable(ctx->sclk_fimc_clk);
		return PTR_ERR(ctx->wb_clk);
	}

	ctx->wb_b_clk = devm_clk_get(dev, "pxl_async1");
	if (IS_ERR(ctx->wb_b_clk)) {
		dev_err(dev, "failed to get writeback b clock.\n");
		clk_disable(ctx->sclk_fimc_clk);
		return PTR_ERR(ctx->wb_b_clk);
	/* Optional FIMC LCLK parent clock setting */
	if (!IS_ERR(ctx->clocks[FIMC_CLK_PARENT])) {
		ret = clk_set_parent(ctx->clocks[FIMC_CLK_MUX],
				     ctx->clocks[FIMC_CLK_PARENT]);
		if (ret < 0) {
			dev_err(fimc_dev, "failed to set parent.\n");
			goto e_clk_free;
		}
	}

	parent_clk = devm_clk_get(dev, ddata->parent_clk);
	ret = clk_set_rate(ctx->clocks[FIMC_CLK_LCLK], ctx->clk_frequency);
	if (ret < 0)
		goto e_clk_free;

	if (IS_ERR(parent_clk)) {
		dev_err(dev, "failed to get parent clock.\n");
		clk_disable(ctx->sclk_fimc_clk);
		return PTR_ERR(parent_clk);
	ret = clk_prepare_enable(ctx->clocks[FIMC_CLK_LCLK]);
	if (!ret)
		return ret;
e_clk_free:
	fimc_put_clocks(ctx);
	return ret;
}

	if (clk_set_parent(ctx->sclk_fimc_clk, parent_clk)) {
		dev_err(dev, "failed to set parent.\n");
		clk_disable(ctx->sclk_fimc_clk);
static int fimc_parse_dt(struct fimc_context *ctx)
{
	struct device_node *node = ctx->ippdrv.dev->of_node;

	/* Handle only devices that support the LCD Writeback data path */
	if (!of_property_read_bool(node, "samsung,lcd-wb"))
		return -ENODEV;

	if (of_property_read_u32(node, "clock-frequency",
					&ctx->clk_frequency))
		ctx->clk_frequency = FIMC_DEFAULT_LCLK_FREQUENCY;

	ctx->id = of_alias_get_id(node, "fimc");

	if (ctx->id < 0) {
		dev_err(ctx->ippdrv.dev, "failed to get node alias id.\n");
		return -EINVAL;
	}

	devm_clk_put(dev, parent_clk);
	clk_set_rate(ctx->sclk_fimc_clk, pdata->clk_rate);
	return 0;
}

static int fimc_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct fimc_context *ctx;
	struct resource *res;
	struct exynos_drm_ippdrv *ippdrv;
	int ret;

	if (!dev->of_node) {
		dev_err(dev, "device tree node not found.\n");
		return -ENODEV;
	}

	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return -ENOMEM;

	ctx->ippdrv.dev = dev;

	ret = fimc_parse_dt(ctx);
	if (ret < 0)
		return ret;

	ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
						"samsung,sysreg");
	if (IS_ERR(ctx->sysreg)) {
		dev_err(dev, "syscon regmap lookup failed.\n");
		return PTR_ERR(ctx->sysreg);
	}

	/* resource memory */
	ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1804,13 +1852,11 @@ static int fimc_probe(struct platform_device *pdev)
		return ret;
	}

	/* context initailization */
	ctx->id = pdev->id;
	ctx->pol = pdata->pol;
	ctx->ddata = ddata;
	ret = fimc_setup_clocks(ctx);
	if (ret < 0)
		goto err_free_irq;

	ippdrv = &ctx->ippdrv;
	ippdrv->dev = dev;
	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops;
	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops;
	ippdrv->check_property = fimc_ippdrv_check_property;
@@ -1820,7 +1866,7 @@ static int fimc_probe(struct platform_device *pdev)
	ret = fimc_init_prop_list(ippdrv);
	if (ret < 0) {
		dev_err(dev, "failed to init property list.\n");
		goto err_get_irq;
		goto err_put_clk;
	}

	DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id,
@@ -1835,17 +1881,18 @@ static int fimc_probe(struct platform_device *pdev)
	ret = exynos_drm_ippdrv_register(ippdrv);
	if (ret < 0) {
		dev_err(dev, "failed to register drm fimc device.\n");
		goto err_ippdrv_register;
		goto err_pm_dis;
	}

	dev_info(&pdev->dev, "drm fimc registered successfully.\n");

	return 0;

err_ippdrv_register:
	devm_kfree(dev, ippdrv->prop_list);
err_pm_dis:
	pm_runtime_disable(dev);
err_get_irq:
err_put_clk:
	fimc_put_clocks(ctx);
err_free_irq:
	free_irq(ctx->irq, ctx);

	return ret;
@@ -1857,10 +1904,10 @@ static int fimc_remove(struct platform_device *pdev)
	struct fimc_context *ctx = get_fimc_context(dev);
	struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;

	devm_kfree(dev, ippdrv->prop_list);
	exynos_drm_ippdrv_unregister(ippdrv);
	mutex_destroy(&ctx->lock);

	fimc_put_clocks(ctx);
	pm_runtime_set_suspended(dev);
	pm_runtime_disable(dev);

@@ -1915,36 +1962,22 @@ static int fimc_runtime_resume(struct device *dev)
}
#endif

static struct fimc_driverdata exynos4210_fimc_data = {
	.parent_clk = "mout_mpll",
};

static struct fimc_driverdata exynos4410_fimc_data = {
	.parent_clk = "mout_mpll_user",
};

static struct platform_device_id fimc_driver_ids[] = {
	{
		.name		= "exynos4210-fimc",
		.driver_data	= (unsigned long)&exynos4210_fimc_data,
	}, {
		.name		= "exynos4412-fimc",
		.driver_data	= (unsigned long)&exynos4410_fimc_data,
	},
	{},
};
MODULE_DEVICE_TABLE(platform, fimc_driver_ids);

static const struct dev_pm_ops fimc_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)
	SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)
};

static const struct of_device_id fimc_of_match[] = {
	{ .compatible = "samsung,exynos4210-fimc" },
	{ .compatible = "samsung,exynos4212-fimc" },
	{ },
};

struct platform_driver fimc_driver = {
	.probe		= fimc_probe,
	.remove		= fimc_remove,
	.id_table	= fimc_driver_ids,
	.driver		= {
		.of_match_table = fimc_of_match,
		.name	= "exynos-drm-fimc",
		.owner	= THIS_MODULE,
		.pm	= &fimc_pm_ops,
+5 −18
Original line number Diff line number Diff line
@@ -801,18 +801,18 @@ static int fimd_clock(struct fimd_context *ctx, bool enable)
	if (enable) {
		int ret;

		ret = clk_enable(ctx->bus_clk);
		ret = clk_prepare_enable(ctx->bus_clk);
		if (ret < 0)
			return ret;

		ret = clk_enable(ctx->lcd_clk);
		ret = clk_prepare_enable(ctx->lcd_clk);
		if  (ret < 0) {
			clk_disable(ctx->bus_clk);
			clk_disable_unprepare(ctx->bus_clk);
			return ret;
		}
	} else {
		clk_disable(ctx->lcd_clk);
		clk_disable(ctx->bus_clk);
		clk_disable_unprepare(ctx->lcd_clk);
		clk_disable_unprepare(ctx->bus_clk);
	}

	return 0;
@@ -949,16 +949,6 @@ static int fimd_probe(struct platform_device *pdev)
		return ret;
	}

	ret = clk_prepare(ctx->bus_clk);
	if (ret < 0)
		return ret;

	ret = clk_prepare(ctx->lcd_clk);
	if  (ret < 0) {
		clk_unprepare(ctx->bus_clk);
		return ret;
	}

	ctx->vidcon0 = pdata->vidcon0;
	ctx->vidcon1 = pdata->vidcon1;
	ctx->default_win = pdata->default_win;
@@ -1006,9 +996,6 @@ static int fimd_remove(struct platform_device *pdev)
	if (ctx->suspended)
		goto out;

	clk_unprepare(ctx->lcd_clk);
	clk_unprepare(ctx->bus_clk);

	pm_runtime_set_suspended(dev);
	pm_runtime_put_sync(dev);

Loading