diff mbox series

[RFC,11/11] drm: exynos: mixer: Add interconnect support

Message ID 20190723122016.30279-12-a.swigon@partner.samsung.com
State New
Headers show
Series None | expand

Commit Message

Artur Świgoń July 23, 2019, 12:20 p.m. UTC
From: Marek Szyprowski <m.szyprowski@samsung.com>


This patch adds interconnect support to exynos-mixer. Please note that the
mixer works the same as before when CONFIG_INTERCONNECT is 'n'.

Co-developed-by: Artur Świgoń <a.swigon@partner.samsung.com>
Signed-off-by: Artur Świgoń <a.swigon@partner.samsung.com>

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

---
 drivers/gpu/drm/exynos/exynos_mixer.c | 68 +++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 5 deletions(-)

-- 
2.17.1

Comments

Krzysztof Kozlowski July 24, 2019, 6:52 p.m. UTC | #1
On Tue, Jul 23, 2019 at 02:20:16PM +0200, Artur Świgoń wrote:
> From: Marek Szyprowski <m.szyprowski@samsung.com>

> 

> This patch adds interconnect support to exynos-mixer. Please note that the

> mixer works the same as before when CONFIG_INTERCONNECT is 'n'.

> 

> Co-developed-by: Artur Świgoń <a.swigon@partner.samsung.com>

> Signed-off-by: Artur Świgoń <a.swigon@partner.samsung.com>

> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

> ---

>  drivers/gpu/drm/exynos/exynos_mixer.c | 68 +++++++++++++++++++++++++--

>  1 file changed, 63 insertions(+), 5 deletions(-)

> 

> diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c

> index 7b24338fad3c..fb763854b300 100644

> --- a/drivers/gpu/drm/exynos/exynos_mixer.c

> +++ b/drivers/gpu/drm/exynos/exynos_mixer.c

> @@ -13,6 +13,7 @@

>  #include <linux/component.h>

>  #include <linux/delay.h>

>  #include <linux/i2c.h>

> +#include <linux/interconnect.h>

>  #include <linux/interrupt.h>

>  #include <linux/irq.h>

>  #include <linux/kernel.h>

> @@ -97,6 +98,7 @@ struct mixer_context {

>  	struct exynos_drm_crtc	*crtc;

>  	struct exynos_drm_plane	planes[MIXER_WIN_NR];

>  	unsigned long		flags;

> +	struct icc_path		*soc_path;

>  

>  	int			irq;

>  	void __iomem		*mixer_regs;

> @@ -931,6 +933,37 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)

>  	mixer_reg_writemask(mixer_ctx, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);

>  }

>  

> +static void mixer_set_memory_bandwidth(struct exynos_drm_crtc *crtc)

> +{

> +	struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;

> +	struct mixer_context *ctx = crtc->ctx;

> +	unsigned long bw, bandwidth = 0;

> +	int i, j, sub;

> +


Early exit if !ctx->soc_path, no need to figure out the bandwidth.
Optionally check it before calling mixer_set_memory_bandwidth() - should
not hurt readability.

> +	for (i = 0; i < MIXER_WIN_NR; i++) {

> +		struct drm_plane *plane = &ctx->planes[i].base;

> +		const struct drm_format_info *format;

> +

> +		if (plane->state && plane->state->crtc && plane->state->fb) {

> +			format = plane->state->fb->format;

> +			bw = mode->hdisplay * mode->vdisplay *

> +							drm_mode_vrefresh(mode);

> +			if (mode->flags & DRM_MODE_FLAG_INTERLACE)

> +				bw /= 2;

> +			for (j = 0; j < format->num_planes; j++) {

> +				sub = j ? (format->vsub * format->hsub) : 1;

> +				bandwidth += format->cpp[j] * bw / sub;

> +			}

> +		}

> +	}

> +

> +	/* add 20% safety margin */

> +	bandwidth = 5UL * bandwidth / 4;

> +

> +	pr_info("exynos-mixer: safe bandwidth %ld Bps\n", bandwidth);


dev_dbg()

Best regards,
Krzysztof
diff mbox series

Patch

diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 7b24338fad3c..fb763854b300 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -13,6 +13,7 @@ 
 #include <linux/component.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/interconnect.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/kernel.h>
@@ -97,6 +98,7 @@  struct mixer_context {
 	struct exynos_drm_crtc	*crtc;
 	struct exynos_drm_plane	planes[MIXER_WIN_NR];
 	unsigned long		flags;
+	struct icc_path		*soc_path;
 
 	int			irq;
 	void __iomem		*mixer_regs;
@@ -931,6 +933,37 @@  static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
 	mixer_reg_writemask(mixer_ctx, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
 }
 
+static void mixer_set_memory_bandwidth(struct exynos_drm_crtc *crtc)
+{
+	struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
+	struct mixer_context *ctx = crtc->ctx;
+	unsigned long bw, bandwidth = 0;
+	int i, j, sub;
+
+	for (i = 0; i < MIXER_WIN_NR; i++) {
+		struct drm_plane *plane = &ctx->planes[i].base;
+		const struct drm_format_info *format;
+
+		if (plane->state && plane->state->crtc && plane->state->fb) {
+			format = plane->state->fb->format;
+			bw = mode->hdisplay * mode->vdisplay *
+							drm_mode_vrefresh(mode);
+			if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+				bw /= 2;
+			for (j = 0; j < format->num_planes; j++) {
+				sub = j ? (format->vsub * format->hsub) : 1;
+				bandwidth += format->cpp[j] * bw / sub;
+			}
+		}
+	}
+
+	/* add 20% safety margin */
+	bandwidth = 5UL * bandwidth / 4;
+
+	pr_info("exynos-mixer: safe bandwidth %ld Bps\n", bandwidth);
+	icc_set_bw(ctx->soc_path, 0, Bps_to_icc(bandwidth));
+}
+
 static void mixer_atomic_begin(struct exynos_drm_crtc *crtc)
 {
 	struct mixer_context *ctx = crtc->ctx;
@@ -982,6 +1015,7 @@  static void mixer_atomic_flush(struct exynos_drm_crtc *crtc)
 	if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
 		return;
 
+	mixer_set_memory_bandwidth(crtc);
 	mixer_enable_sync(mixer_ctx);
 	exynos_crtc_handle_event(crtc);
 }
@@ -1029,6 +1063,7 @@  static void mixer_disable(struct exynos_drm_crtc *crtc)
 	for (i = 0; i < MIXER_WIN_NR; i++)
 		mixer_disable_plane(crtc, &ctx->planes[i]);
 
+	mixer_set_memory_bandwidth(crtc);
 	exynos_drm_pipe_clk_enable(crtc, false);
 
 	pm_runtime_put(ctx->dev);
@@ -1220,12 +1255,22 @@  static int mixer_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	const struct mixer_drv_data *drv;
 	struct mixer_context *ctx;
+	struct icc_path *path;
 	int ret;
 
+	/*
+	 * Returns NULL if CONFIG_INTERCONNECT is disabled.
+	 * May return ERR_PTR(-EPROBE_DEFER).
+	 */
+	path = of_icc_get(dev, NULL);
+	if (IS_ERR(path))
+		return PTR_ERR(path);
+
 	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 	if (!ctx) {
 		DRM_DEV_ERROR(dev, "failed to alloc mixer context.\n");
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto err;
 	}
 
 	drv = of_device_get_match_data(dev);
@@ -1233,6 +1278,7 @@  static int mixer_probe(struct platform_device *pdev)
 	ctx->pdev = pdev;
 	ctx->dev = dev;
 	ctx->mxr_ver = drv->version;
+	ctx->soc_path = path;
 
 	if (drv->is_vp_enabled)
 		__set_bit(MXR_BIT_VP_ENABLED, &ctx->flags);
@@ -1242,17 +1288,29 @@  static int mixer_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, ctx);
 
 	ret = component_add(&pdev->dev, &mixer_component_ops);
-	if (!ret)
-		pm_runtime_enable(dev);
+	if (ret < 0)
+		goto err;
+
+	pm_runtime_enable(dev);
+
+	return 0;
+
+err:
+	icc_put(path);
 
 	return ret;
 }
 
 static int mixer_remove(struct platform_device *pdev)
 {
-	pm_runtime_disable(&pdev->dev);
+	struct device *dev = &pdev->dev;
+	struct mixer_context *ctx = dev_get_drvdata(dev);
 
-	component_del(&pdev->dev, &mixer_component_ops);
+	pm_runtime_disable(dev);
+
+	component_del(dev, &mixer_component_ops);
+
+	icc_put(ctx->soc_path);
 
 	return 0;
 }