2019-09-06 22:04:08

by Jacopo Mondi

[permalink] [raw]
Subject: [PATCH v4 0/9 drm: rcar-du: Add Color Management Module (CMM)

[ Ugh, sorry for the double sending, but I forgot --cc-cover to git-send
email and the series has not been delivered to the mailing lists.
Sorry about that. ]

Hello, new iteration of CMM support, with quite a few changes compared to
v3:

References:
A reference to the v1 cover letter, with some background on the CMM is
available here:
https://lkml.org/lkml/2019/6/6/583
v2:
https://lore.kernel.org/linux-renesas-soc/[email protected]/
v3:
https://lore.kernel.org/linux-renesas-soc/[email protected]/

Change log:

*Bindings/DT:
- Rebased on renesas-devel-2019-09-03-v5.3-rc7
- Bindings converted to yaml: thanks Laurent for help
- s/'cmms'/'renesas,cmms'/ in DU bindings as suggested by Rob
- s/cmm-<soctype>/<soctype>-cmm/ as suggested by Geert
- squashed CMM addition for Gen3 SoCs in a single path at the end of
the series

*CMM/DU:
- Only accept fully populated LUT tables, remove the 'size' from the CMM
configuration structure as suggested by Laurent
- Simplify CMM configuration logic: only rely on color_mgmt_changed flag and
unconditionally provide a populated LUT table to the cmm_setup() function
- Protect against probing order inversion (DU is operation while CMM still has
not been probed) by adding rcar_cmm_init() operation as it is done for VSP as
suggested by Laurent
- Add CMM function stubs to fix compilation erros when CONFIG_DRM_RCAR_CMM is
not selected
- Minors in the CMM driver as suggested by Laurent
- Remove per-soc strings
- Make comments style consistent (not using /** anywhere in the .c file,
unify comment style)
- s/rcar_cmm_load()/rcar_cmm_write()/
- Squash cmm configuration and suspend/resume support in rcar_du_kms.c

Testing:
I have tested by injecting a color inversion LUT table at test program
initialization:
https://jmondi.org/cgit/kmsxx/commit/?h=gamma_lut&id=3c6af4db165e5b3dc8996f0a288746c35dbb1cb9
And by changing the CMM content to switch between a color inversion table
and a linear table every 50 frames:
https://jmondi.org/cgit/kmsxx/commit/?h=gamma_lut&id=fe178a43861da7c8e79618e2a13fa0f19dbcd03d

Pretty happy with the result, which seems to be consistent across system
suspend/resume.

Testing with real world use cases might be beneficial. Rajesh are you still
interested in giving this series a spin?

Thanks
j

Jacopo Mondi (9):
dt-bindings: display: renesas,cmm: Add R-Car CMM documentation
dt-bindings: display, renesas,du: Document cmms property
drm: rcar-du: Add support for CMM
drm: rcar-du: Claim CMM support for Gen3 SoCs
drm: rcar-du: kms: Initialize CMM instances
drm: rcar-du: crtc: Enable and disable CMMs
drm: rcar-du: crtc: Register GAMMA_LUT properties
drm: rcar-du: kms: Update CMM in atomic commit tail
arm64: dts: renesas: Add CMM units to Gen3 SoCs

.../bindings/display/renesas,cmm.yaml | 64 +++++
.../bindings/display/renesas,du.txt | 5 +
arch/arm64/boot/dts/renesas/r8a7795.dtsi | 40 ++-
arch/arm64/boot/dts/renesas/r8a7796.dtsi | 28 ++
arch/arm64/boot/dts/renesas/r8a77965.dtsi | 28 ++
arch/arm64/boot/dts/renesas/r8a77990.dtsi | 22 +-
arch/arm64/boot/dts/renesas/r8a77995.dtsi | 22 +-
drivers/gpu/drm/rcar-du/Kconfig | 7 +
drivers/gpu/drm/rcar-du/Makefile | 1 +
drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 ++++++++++++++++++
drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++
drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 17 ++
drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 +
drivers/gpu/drm/rcar-du/rcar_du_drv.c | 32 ++-
drivers/gpu/drm/rcar-du/rcar_du_drv.h | 3 +
drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 +
drivers/gpu/drm/rcar-du/rcar_du_group.h | 2 +
drivers/gpu/drm/rcar-du/rcar_du_kms.c | 106 ++++++++
drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +
19 files changed, 697 insertions(+), 7 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/renesas,cmm.yaml
create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h

--
2.23.0


2019-09-06 22:06:09

by Jacopo Mondi

[permalink] [raw]
Subject: [PATCH v4 6/9] drm: rcar-du: crtc: Enable and disable CMMs

Enable/disable the CMM associated with a CRTC at CRTC start and stop
time and enable the CMM unit through the Display Extensional Functions
register at group setup time.

Reviewed-by: Ulrich Hecht <[email protected]>
Reviewed-by: Laurent Pinchart <[email protected]>
Signed-off-by: Jacopo Mondi <[email protected]>
---
drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++
drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++++++
drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +++++
3 files changed, 20 insertions(+)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 23f1d6cc1719..3dac605c3a67 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -21,6 +21,7 @@
#include <drm/drm_plane_helper.h>
#include <drm/drm_vblank.h>

+#include "rcar_cmm.h"
#include "rcar_du_crtc.h"
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
@@ -619,6 +620,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
rcar_du_vsp_disable(rcrtc);

+ if (rcrtc->cmm)
+ rcar_cmm_disable(rcrtc->cmm);
+
/*
* Select switch sync mode. This stops display operation and configures
* the HSYNC and VSYNC signals as inputs.
@@ -686,6 +690,9 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
}

rcar_du_crtc_start(rcrtc);
+
+ if (rcrtc->cmm)
+ rcar_cmm_enable(rcrtc->cmm);
}

static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
index 9eee47969e77..25d0fc125d7a 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
@@ -147,6 +147,14 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)

rcar_du_group_setup_pins(rgrp);

+ if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
+ u32 defr7 = DEFR7_CODE
+ | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
+ | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
+
+ rcar_du_group_write(rgrp, DEFR7, defr7);
+ }
+
if (rcdu->info->gen >= 2) {
rcar_du_group_setup_defr8(rgrp);
rcar_du_group_setup_didsr(rgrp);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index bc87f080b170..fb9964949368 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -197,6 +197,11 @@
#define DEFR6_MLOS1 (1 << 2)
#define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)

+#define DEFR7 0x000ec
+#define DEFR7_CODE (0x7779 << 16)
+#define DEFR7_CMME1 BIT(6)
+#define DEFR7_CMME0 BIT(4)
+
/* -----------------------------------------------------------------------------
* R8A7790-only Control Registers
*/
--
2.23.0

2019-09-06 22:06:15

by Jacopo Mondi

[permalink] [raw]
Subject: [PATCH v4 7/9] drm: rcar-du: crtc: Register GAMMA_LUT properties

Enable the GAMMA_LUT KMS property using the framework helpers to
register the property and set the associated gamma table maximum size.

Reviewed-by: Ulrich Hecht <[email protected]>
Reviewed-by: Laurent Pinchart <[email protected]>
Signed-off-by: Jacopo Mondi <[email protected]>
---
drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 3dac605c3a67..dc59eeec990c 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -1082,6 +1082,7 @@ static const struct drm_crtc_funcs crtc_funcs_gen3 = {
.set_crc_source = rcar_du_crtc_set_crc_source,
.verify_crc_source = rcar_du_crtc_verify_crc_source,
.get_crc_sources = rcar_du_crtc_get_crc_sources,
+ .gamma_set = drm_atomic_helper_legacy_gamma_set,
};

/* -----------------------------------------------------------------------------
@@ -1205,6 +1206,9 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
if (rcdu->cmms[swindex]) {
rcrtc->cmm = rcdu->cmms[swindex];
rgrp->cmms_mask |= BIT(hwindex % 2);
+
+ drm_mode_crtc_set_gamma_size(crtc, CM2_LUT_SIZE);
+ drm_crtc_enable_color_mgmt(crtc, 0, false, CM2_LUT_SIZE);
}

drm_crtc_helper_add(crtc, &crtc_helper_funcs);
--
2.23.0

2019-09-06 22:14:54

by Jacopo Mondi

[permalink] [raw]
Subject: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Add a driver for the R-Car Display Unit Color Correction Module.

In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
to perform image enhancement and color correction.

Add support for CMM through a driver that supports configuration of
the 1-dimensional LUT table. More advanced CMM feature will be
implemented on top of this basic one.

Signed-off-by: Jacopo Mondi <[email protected]>
---
drivers/gpu/drm/rcar-du/Kconfig | 7 +
drivers/gpu/drm/rcar-du/Makefile | 1 +
drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
4 files changed, 320 insertions(+)
create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h

diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 1529849e217e..539d232790d1 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -13,6 +13,13 @@ config DRM_RCAR_DU
Choose this option if you have an R-Car chipset.
If M is selected the module will be called rcar-du-drm.

+config DRM_RCAR_CMM
+ bool "R-Car DU Color Management Module (CMM) Support"
+ depends on DRM && OF
+ depends on DRM_RCAR_DU
+ help
+ Enable support for R-Car Color Management Module (CMM).
+
config DRM_RCAR_DW_HDMI
tristate "R-Car DU Gen3 HDMI Encoder Support"
depends on DRM && OF
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 6c2ed9c46467..4d1187ccc3e5 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o

+obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c b/drivers/gpu/drm/rcar-du/rcar_cmm.c
new file mode 100644
index 000000000000..3cacdc4474c7
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * rcar_cmm.c -- R-Car Display Unit Color Management Module
+ *
+ * Copyright (C) 2019 Jacopo Mondi <[email protected]>
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_color_mgmt.h>
+
+#include "rcar_cmm.h"
+
+#define CM2_LUT_CTRL 0x0000
+#define CM2_LUT_CTRL_LUT_EN BIT(0)
+#define CM2_LUT_TBL_BASE 0x0600
+#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
+
+struct rcar_cmm {
+ void __iomem *base;
+ bool enabled;
+
+ /*
+ * @lut: 1D-LUT status
+ * @lut.enabled: 1D-LUT enabled flag
+ * @lut.table: Table of 1D-LUT entries scaled to hardware
+ * precision (8-bits per color component)
+ */
+ struct {
+ bool enabled;
+ u32 table[CM2_LUT_SIZE];
+ } lut;
+};
+
+static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
+{
+ return ioread32(rcmm->base + reg);
+}
+
+static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
+{
+ iowrite32(data, rcmm->base + reg);
+}
+
+/*
+ * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM LUT table
+ * entries and store them.
+ * @rcmm: Pointer to the CMM device
+ * @drm_lut: Pointer to the DRM LUT table
+ */
+static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
+ const struct drm_color_lut *drm_lut)
+{
+ unsigned int i;
+
+ for (i = 0; i < CM2_LUT_SIZE; ++i) {
+ const struct drm_color_lut *lut = &drm_lut[i];
+
+ rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
+ | drm_color_lut_extract(lut->green, 8) << 8
+ | drm_color_lut_extract(lut->blue, 8);
+ }
+}
+
+/*
+ * rcar_cmm_lut_write() - Write to hardware the LUT table entries from the
+ * local table.
+ * @rcmm: Pointer to the CMM device
+ */
+static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
+{
+ unsigned int i;
+
+ for (i = 0; i < CM2_LUT_SIZE; ++i)
+ rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
+}
+
+/*
+ * rcar_cmm_setup() - Configure the CMM unit.
+ * @pdev: The platform device associated with the CMM instance
+ * @config: The CRTC-provided configuration.
+ *
+ * Configure the CMM unit with the CRTC-provided configuration.
+ * Currently enabling, disabling and programming of the 1-D LUT unit is
+ * supported.
+ */
+int rcar_cmm_setup(struct platform_device *pdev,
+ const struct rcar_cmm_config *config)
+{
+ struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
+
+ /*
+ * As rcar_cmm_setup() is called by atomic commit tail helper, it might
+ * be called when the CMM is disabled. As we can't program the hardware
+ * in that case, store the configuration internally and apply it when
+ * the CMM will be enabled by the CRTC through rcar_cmm_enable().
+ */
+ if (!rcmm->enabled) {
+ if (!config->lut.enable)
+ return 0;
+
+ rcar_cmm_lut_extract(rcmm, config->lut.table);
+ rcmm->lut.enabled = true;
+
+ return 0;
+ }
+
+ /* Stop LUT operations if requested. */
+ if (!config->lut.enable) {
+ if (rcmm->lut.enabled) {
+ rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
+ rcmm->lut.enabled = false;
+ }
+
+ return 0;
+ }
+
+ /*
+ * Enable LUT and program the new gamma table values.
+ *
+ * FIXME: In order to have stable operations it is required to first
+ * enable the 1D-LUT and then program its table entries. This seems to
+ * contradict what the chip manual reports, and will have to be
+ * reconsidered when implementing support for double buffering.
+ */
+ if (!rcmm->lut.enabled) {
+ rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
+ rcmm->lut.enabled = true;
+ }
+
+ rcar_cmm_lut_extract(rcmm, config->lut.table);
+ rcar_cmm_lut_write(rcmm);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_setup);
+
+/*
+ * rcar_cmm_enable() - Enable the CMM unit.
+ * @pdev: The platform device associated with the CMM instance
+ *
+ * Enable the CMM unit by enabling the parent clock and enabling the CMM
+ * components, such as 1-D LUT, if requested.
+ */
+int rcar_cmm_enable(struct platform_device *pdev)
+{
+ struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Apply the LUT table values saved at rcar_cmm_setup() time. */
+ if (rcmm->lut.enabled) {
+ rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
+ rcar_cmm_lut_write(rcmm);
+ }
+
+ rcmm->enabled = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_enable);
+
+/*
+ * rcar_cmm_disable() - Disable the CMM unit.
+ * @pdev: The platform device associated with the CMM instance
+ *
+ * Disable the CMM unit by stopping the parent clock.
+ */
+void rcar_cmm_disable(struct platform_device *pdev)
+{
+ struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
+
+ rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
+
+ pm_runtime_put(&pdev->dev);
+
+ rcmm->lut.enabled = false;
+ rcmm->enabled = false;
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_disable);
+
+/*
+ * rcar_cmm_init() - Make sure the CMM has probed.
+ * @pdev: The platform device associated with the CMM instance
+ *
+ * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
+ */
+int rcar_cmm_init(struct platform_device *pdev)
+{
+ struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
+
+ if (!rcmm)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_init);
+
+static int rcar_cmm_probe(struct platform_device *pdev)
+{
+ struct rcar_cmm *rcmm;
+
+ rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
+ if (!rcmm)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, rcmm);
+
+ rcmm->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rcmm->base))
+ return PTR_ERR(rcmm->base);
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+}
+
+static int rcar_cmm_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id rcar_cmm_of_table[] = {
+ { .compatible = "renesas,rcar-gen3-cmm", },
+ { .compatible = "renesas,rcar-gen2-cmm", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
+
+static struct platform_driver rcar_cmm_platform_driver = {
+ .probe = rcar_cmm_probe,
+ .remove = rcar_cmm_remove,
+ .driver = {
+ .name = "rcar-cmm",
+ .of_match_table = rcar_cmm_of_table,
+ },
+};
+
+module_platform_driver(rcar_cmm_platform_driver);
+
+MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
+MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h b/drivers/gpu/drm/rcar-du/rcar_cmm.h
new file mode 100644
index 000000000000..15a2c874b6a6
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * rcar_cmm.h -- R-Car Display Unit Color Management Module
+ *
+ * Copyright (C) 2019 Jacopo Mondi <[email protected]>
+ */
+
+#ifndef __RCAR_CMM_H__
+#define __RCAR_CMM_H__
+
+#define CM2_LUT_SIZE 256
+
+struct drm_color_lut;
+struct platform_device;
+
+/**
+ * struct rcar_cmm_config - CMM configuration
+ *
+ * @lut: 1D-LUT configuration
+ * @lut.enable: 1D-LUT enable flag
+ * @lut.table: 1D-LUT table entries. Might be set to NULL when the CMM has to
+ * be re-enabled but not re=programmed.
+ */
+struct rcar_cmm_config {
+ struct {
+ bool enable;
+ struct drm_color_lut *table;
+ } lut;
+};
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
+int rcar_cmm_init(struct platform_device *pdev);
+
+int rcar_cmm_enable(struct platform_device *pdev);
+void rcar_cmm_disable(struct platform_device *pdev);
+
+int rcar_cmm_setup(struct platform_device *pdev,
+ const struct rcar_cmm_config *config);
+#else
+static inline int rcar_cmm_init(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static inline int rcar_cmm_enable(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static inline void rcar_cmm_disable(struct platform_device *pdev)
+{
+}
+
+static int rcar_cmm_setup(struct platform_device *pdev,
+ const struct rcar_cmm_config *config)
+{
+ return 0;
+}
+#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
+
+#endif /* __RCAR_CMM_H__ */
--
2.23.0

2019-09-06 22:19:15

by Jacopo Mondi

[permalink] [raw]
Subject: [PATCH v4 9/9] arm64: dts: renesas: Add CMM units to Gen3 SoCs

Add CMM units to Renesas R-Car Gen3 SoC that support it, and reference them
from the Display Unit they are connected to.

Sort the 'vsps' and 'renesas,cmm' entries in the DU unit consistently
in all the involved DTS.

Signed-off-by: Jacopo Mondi <[email protected]>
---
arch/arm64/boot/dts/renesas/r8a7795.dtsi | 40 ++++++++++++++++++++++-
arch/arm64/boot/dts/renesas/r8a7796.dtsi | 28 ++++++++++++++++
arch/arm64/boot/dts/renesas/r8a77965.dtsi | 28 ++++++++++++++++
arch/arm64/boot/dts/renesas/r8a77990.dtsi | 22 ++++++++++++-
arch/arm64/boot/dts/renesas/r8a77995.dtsi | 22 ++++++++++++-
5 files changed, 137 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
index 6675462f7585..67c242a447bc 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
@@ -2939,6 +2939,42 @@
iommus = <&ipmmu_vi1 10>;
};

+ cmm0: cmm@fea40000 {
+ compatible = "renesas,r8a7795-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea40000 0 0x1000>;
+ power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 711>;
+ resets = <&cpg 711>;
+ };
+
+ cmm1: cmm@fea50000 {
+ compatible = "renesas,r8a7795-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea50000 0 0x1000>;
+ power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 710>;
+ resets = <&cpg 710>;
+ };
+
+ cmm2: cmm@fea60000 {
+ compatible = "renesas,r8a7795-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea60000 0 0x1000>;
+ power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 709>;
+ resets = <&cpg 709>;
+ };
+
+ cmm3: cmm@fea70000 {
+ compatible = "renesas,r8a7795-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea70000 0 0x1000>;
+ power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 708>;
+ resets = <&cpg 708>;
+ };
+
csi20: csi2@fea80000 {
compatible = "renesas,r8a7795-csi2";
reg = <0 0xfea80000 0 0x10000>;
@@ -3142,9 +3178,11 @@
<&cpg CPG_MOD 722>,
<&cpg CPG_MOD 721>;
clock-names = "du.0", "du.1", "du.2", "du.3";
- vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>;
status = "disabled";

+ vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>;
+ renesas,cmms = <&cmm0 &cmm1 &cmm2 &cmm3>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
index 822c96601d3c..837c3b2da773 100644
--- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
@@ -2641,6 +2641,33 @@
renesas,fcp = <&fcpvi0>;
};

+ cmm0: cmm@fea40000 {
+ compatible = "renesas,r8a7796-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea40000 0 0x1000>;
+ power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 711>;
+ resets = <&cpg 711>;
+ };
+
+ cmm1: cmm@fea50000 {
+ compatible = "renesas,r8a7796-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea50000 0 0x1000>;
+ power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 710>;
+ resets = <&cpg 710>;
+ };
+
+ cmm2: cmm@fea60000 {
+ compatible = "renesas,r8a7796-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea60000 0 0x1000>;
+ power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 709>;
+ resets = <&cpg 709>;
+ };
+
csi20: csi2@fea80000 {
compatible = "renesas,r8a7796-csi2";
reg = <0 0xfea80000 0 0x10000>;
@@ -2794,6 +2821,7 @@
status = "disabled";

vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>;
+ renesas,cmms = <&cmm0 &cmm1 &cmm2>;

ports {
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/renesas/r8a77965.dtsi b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
index 4ae163220f60..c7635e8b261c 100644
--- a/arch/arm64/boot/dts/renesas/r8a77965.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
@@ -2320,6 +2320,33 @@
resets = <&cpg 611>;
};

+ cmm0: cmm@fea40000 {
+ compatible = "renesas,r8a77965-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea40000 0 0x1000>;
+ power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 711>;
+ resets = <&cpg 711>;
+ };
+
+ cmm1: cmm@fea50000 {
+ compatible = "renesas,r8a77965-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea50000 0 0x1000>;
+ power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 710>;
+ resets = <&cpg 710>;
+ };
+
+ cmm3: cmm@fea70000 {
+ compatible = "renesas,r8a77965-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea70000 0 0x1000>;
+ power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 708>;
+ resets = <&cpg 708>;
+ };
+
csi20: csi2@fea80000 {
compatible = "renesas,r8a77965-csi2";
reg = <0 0xfea80000 0 0x10000>;
@@ -2470,6 +2497,7 @@
status = "disabled";

vsps = <&vspd0 0>, <&vspd1 0>, <&vspd0 1>;
+ renesas,cmms = <&cmm0 &cmm1 &cmm3>;

ports {
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/renesas/r8a77990.dtsi b/arch/arm64/boot/dts/renesas/r8a77990.dtsi
index 455954c3d98e..5e3d758a033f 100644
--- a/arch/arm64/boot/dts/renesas/r8a77990.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77990.dtsi
@@ -1727,6 +1727,24 @@
iommus = <&ipmmu_vi0 9>;
};

+ cmm0: cmm@fea40000 {
+ compatible = "renesas,r8a77990-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea40000 0 0x1000>;
+ power-domains = <&sysc R8A77990_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 711>;
+ resets = <&cpg 711>;
+ };
+
+ cmm1: cmm@fea50000 {
+ compatible = "renesas,r8a77990-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea50000 0 0x1000>;
+ power-domains = <&sysc R8A77990_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 710>;
+ resets = <&cpg 710>;
+ };
+
csi40: csi2@feaa0000 {
compatible = "renesas,r8a77990-csi2";
reg = <0 0xfeaa0000 0 0x10000>;
@@ -1768,9 +1786,11 @@
clock-names = "du.0", "du.1";
resets = <&cpg 724>;
reset-names = "du.0";
- vsps = <&vspd0 0>, <&vspd1 0>;
status = "disabled";

+ vsps = <&vspd0 0>, <&vspd1 0>;
+ renesas,cmms = <&cmm0 &cmm1>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi
index 183fef86cf7c..6838a81f5caa 100644
--- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi
@@ -993,6 +993,24 @@
iommus = <&ipmmu_vi0 9>;
};

+ cmm0: cmm@fea40000 {
+ compatible = "renesas,r8a77995-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea40000 0 0x1000>;
+ power-domains = <&sysc R8A77995_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 711>;
+ resets = <&cpg 711>;
+ };
+
+ cmm1: cmm@fea50000 {
+ compatible = "renesas,r8a77995-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea50000 0 0x1000>;
+ power-domains = <&sysc R8A77995_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 710>;
+ resets = <&cpg 710>;
+ };
+
du: display@feb00000 {
compatible = "renesas,du-r8a77995";
reg = <0 0xfeb00000 0 0x40000>;
@@ -1003,9 +1021,11 @@
clock-names = "du.0", "du.1";
resets = <&cpg 724>;
reset-names = "du.0";
- vsps = <&vspd0 0>, <&vspd1 0>;
status = "disabled";

+ vsps = <&vspd0 0>, <&vspd1 0>;
+ renesas,cmms = <&cmm0 &cmm1>;
+
ports {
#address-cells = <1>;
#size-cells = <0>;
--
2.23.0

2019-09-06 22:27:16

by Jacopo Mondi

[permalink] [raw]
Subject: [PATCH v4 8/9] drm: rcar-du: kms: Update CMM in atomic commit tail

Update CMM settings at in the atomic commit tail helper method.
The CMM is updated with new gamma values provided to the driver
in the GAMMA_LUT blob property.

When resuming from system suspend, the DU driver is responsible for
reprogramming and enabling the CMM unit if it was in use at the time the
system entered the suspend state. Force the color_mgmt_changed flag to
true if the DRM gamma lut color transformation property was set in the
CRTC state duplicated at suspend time, as the CMM gets reprogrammed only
if said flag is active in the rcar_du_atomic_commit_update_cmm() method.

Reviewed-by: Ulrich Hecht <[email protected]>
Reviewed-by: Laurent Pinchart <[email protected]>
Signed-off-by: Jacopo Mondi <[email protected]>
---

Daniel could you have a look if resume bits are worth being moved to the
DRM core? The color_mgmt_changed flag is set to false when the state is
duplicated if I read the code correctly, but when this happens in a
suspend/resume sequence its value should probably be restored to true if
any color management property was set in the crtc state when system entered
suspend.

---

drivers/gpu/drm/rcar-du/rcar_du_drv.c | 20 ++++++++++++++
drivers/gpu/drm/rcar-du/rcar_du_kms.c | 38 +++++++++++++++++++++++++++
2 files changed, 58 insertions(+)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 018480a8f35c..d1003d31cfaf 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/wait.h>

+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
@@ -482,6 +483,25 @@ static int rcar_du_pm_suspend(struct device *dev)
static int rcar_du_pm_resume(struct device *dev)
{
struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+ struct drm_atomic_state *state = rcdu->ddev->mode_config.suspend_state;
+ unsigned int i;
+
+ for (i = 0; i < rcdu->num_crtcs; ++i) {
+ struct drm_crtc *crtc = &rcdu->crtcs[i].crtc;
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (!crtc_state)
+ continue;
+
+ /*
+ * Force re-enablement of CMM after system resume if any
+ * of the DRM color transformation properties was set in
+ * the state saved at system suspend time.
+ */
+ if (crtc_state->gamma_lut)
+ crtc_state->color_mgmt_changed = true;
+ }

return drm_mode_config_helper_resume(rcdu->ddev);
}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 294630e56992..fc30fff0eb8d 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -22,6 +22,7 @@
#include <linux/of_platform.h>
#include <linux/wait.h>

+#include "rcar_cmm.h"
#include "rcar_du_crtc.h"
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
@@ -368,6 +369,40 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
* Atomic Check and Update
*/

+static void rcar_du_atomic_commit_update_cmm(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+ struct rcar_cmm_config cmm_config = {};
+ struct device *dev = rcrtc->dev->dev;
+ struct drm_property_blob *lut_blob;
+
+ if (!rcrtc->cmm || !crtc->state->color_mgmt_changed)
+ return;
+
+ if (!crtc->state->gamma_lut) {
+ cmm_config.lut.enable = false;
+ rcar_cmm_setup(rcrtc->cmm, &cmm_config);
+ return;
+ }
+
+ lut_blob = crtc->state->gamma_lut;
+ if (lut_blob->length != (CM2_LUT_SIZE * sizeof(struct drm_color_lut))) {
+ /*
+ * We only accept fully populated LUT tables;
+ * be loud here, otherwise the CMM gets silently ignored.
+ */
+ dev_err(dev, "invalid gamma lut size of %lu bytes\n",
+ lut_blob->length);
+ return;
+ }
+
+ cmm_config.lut.enable = true;
+ cmm_config.lut.table = (struct drm_color_lut *)lut_blob->data;
+
+ rcar_cmm_setup(rcrtc->cmm, &cmm_config);
+}
+
static int rcar_du_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
@@ -410,6 +445,9 @@ static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state)
rcdu->dpad1_source = rcrtc->index;
}

+ for_each_old_crtc_in_state(old_state, crtc, crtc_state, i)
+ rcar_du_atomic_commit_update_cmm(crtc, crtc_state);
+
/* Apply the atomic update. */
drm_atomic_helper_commit_modeset_disables(dev, old_state);
drm_atomic_helper_commit_planes(dev, old_state,
--
2.23.0

2019-09-11 15:58:45

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Jacopo,

<This time replying to the mail with ML's included Doh!>

On 06/09/2019 14:43, Jacopo Mondi wrote:
> Add a driver for the R-Car Display Unit Color Correction Module.
>
> In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
> to perform image enhancement and color correction.
>
> Add support for CMM through a driver that supports configuration of
> the 1-dimensional LUT table. More advanced CMM feature will be

s/feature/features/

> implemented on top of this basic one.

s/basic/initial/

>
> Signed-off-by: Jacopo Mondi <[email protected]>
> ---
> drivers/gpu/drm/rcar-du/Kconfig | 7 +
> drivers/gpu/drm/rcar-du/Makefile | 1 +
> drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
> drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
> 4 files changed, 320 insertions(+)
> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
>
> diff --git a/drivers/gpu/drm/rcar-du/Kconfig
b/drivers/gpu/drm/rcar-du/Kconfig
> index 1529849e217e..539d232790d1 100644
> --- a/drivers/gpu/drm/rcar-du/Kconfig
> +++ b/drivers/gpu/drm/rcar-du/Kconfig
> @@ -13,6 +13,13 @@ config DRM_RCAR_DU
> Choose this option if you have an R-Car chipset.
> If M is selected the module will be called rcar-du-drm.
>
> +config DRM_RCAR_CMM
> + bool "R-Car DU Color Management Module (CMM) Support"
> + depends on DRM && OF
> + depends on DRM_RCAR_DU
> + help
> + Enable support for R-Car Color Management Module (CMM).
> +
> config DRM_RCAR_DW_HDMI
> tristate "R-Car DU Gen3 HDMI Encoder Support"
> depends on DRM && OF
> diff --git a/drivers/gpu/drm/rcar-du/Makefile
b/drivers/gpu/drm/rcar-du/Makefile
> index 6c2ed9c46467..4d1187ccc3e5 100644
> --- a/drivers/gpu/drm/rcar-du/Makefile
> +++ b/drivers/gpu/drm/rcar-du/Makefile
> @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
> rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
> rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
>
> +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
> obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
> obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
> obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c
b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> new file mode 100644
> index 000000000000..3cacdc4474c7
> --- /dev/null
> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * rcar_cmm.c -- R-Car Display Unit Color Management Module
> + *
> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> + */
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_color_mgmt.h>
> +
> +#include "rcar_cmm.h"
> +
> +#define CM2_LUT_CTRL 0x0000
> +#define CM2_LUT_CTRL_LUT_EN BIT(0)

I'd have a new line here

> +#define CM2_LUT_TBL_BASE 0x0600
> +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
> +
> +struct rcar_cmm {
> + void __iomem *base;
> + bool enabled;
> +
> + /*
> + * @lut: 1D-LUT status
> + * @lut.enabled: 1D-LUT enabled flag
> + * @lut.table: Table of 1D-LUT entries scaled to hardware
> + * precision (8-bits per color component)
> + */
> + struct {
> + bool enabled;
> + u32 table[CM2_LUT_SIZE];
> + } lut;
> +};
> +
> +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
> +{
> + return ioread32(rcmm->base + reg);
> +}
> +
> +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32
data)
> +{
> + iowrite32(data, rcmm->base + reg);
> +}
> +
> +/*
> + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM
LUT table
> + * entries and store them.
> + * @rcmm: Pointer to the CMM device
> + * @drm_lut: Pointer to the DRM LUT table
> + */
> +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
> + const struct drm_color_lut *drm_lut)
> +{
> + unsigned int i;

I think you're missing the following here:

if (!drm_lut)
return;

You mention below that drm_lut could be passed in as NULL, which would
cause a segfault here otherwise.


> +
> + for (i = 0; i < CM2_LUT_SIZE; ++i) {
> + const struct drm_color_lut *lut = &drm_lut[i];
> +
> + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
> + | drm_color_lut_extract(lut->green, 8) << 8
> + | drm_color_lut_extract(lut->blue, 8);
> + }
> +}
> +
> +/*
> + * rcar_cmm_lut_write() - Write to hardware the LUT table entries
from the
> + * local table.
> + * @rcmm: Pointer to the CMM device
> + */
> +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < CM2_LUT_SIZE; ++i)
> + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
> +}
> +
> +/*
> + * rcar_cmm_setup() - Configure the CMM unit.
> + * @pdev: The platform device associated with the CMM instance
> + * @config: The CRTC-provided configuration.

I don't think CRTC-provided should be hyphenated like that.
Perhaps just:
"The colour management configuration".

As I don't think who provides the configuration is relevant to the
actual call.

> + *
> + * Configure the CMM unit with the CRTC-provided configuration.

s/CRTC-provided/given/

> + * Currently enabling, disabling and programming of the 1-D LUT unit is
> + * supported.
> + */
> +int rcar_cmm_setup(struct platform_device *pdev,
> + const struct rcar_cmm_config *config)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> +
> + /*
> + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
> + * be called when the CMM is disabled. As we can't program the hardware
> + * in that case, store the configuration internally and apply it when
> + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
> + */
> + if (!rcmm->enabled) {
> + if (!config->lut.enable)
> + return 0;
> +
> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> + rcmm->lut.enabled = true;
> +
> + return 0;
> + }
> +
> + /* Stop LUT operations if requested. */
> + if (!config->lut.enable) {
> + if (rcmm->lut.enabled) {
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> + rcmm->lut.enabled = false;
> + }
> +
> + return 0;
> + }
> +
> + /*
> + * Enable LUT and program the new gamma table values.
> + *
> + * FIXME: In order to have stable operations it is required to first
> + * enable the 1D-LUT and then program its table entries. This seems to
> + * contradict what the chip manual reports, and will have to be
> + * reconsidered when implementing support for double buffering.
> + */
> + if (!rcmm->lut.enabled) {
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> + rcmm->lut.enabled = true;
> + }
> +
> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> + rcar_cmm_lut_write(rcmm);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
> +
> +/*
> + * rcar_cmm_enable() - Enable the CMM unit.
> + * @pdev: The platform device associated with the CMM instance
> + *
> + * Enable the CMM unit by enabling the parent clock and enabling the CMM
> + * components, such as 1-D LUT, if requested.
> + */
> +int rcar_cmm_enable(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> + int ret;
> +
> + ret = pm_runtime_get_sync(&pdev->dev);
> + if (ret < 0)
> + return ret;
> +
> + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
> + if (rcmm->lut.enabled) {
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> + rcar_cmm_lut_write(rcmm);
> + }
> +
> + rcmm->enabled = true;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
> +
> +/*
> + * rcar_cmm_disable() - Disable the CMM unit.
> + * @pdev: The platform device associated with the CMM instance
> + *
> + * Disable the CMM unit by stopping the parent clock.
> + */
> +void rcar_cmm_disable(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> +
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> +
> + pm_runtime_put(&pdev->dev);
> +
> + rcmm->lut.enabled = false;
> + rcmm->enabled = false;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
> +
> +/*
> + * rcar_cmm_init() - Make sure the CMM has probed.
> + * @pdev: The platform device associated with the CMM instance
> + *
> + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
> + */
> +int rcar_cmm_init(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> +
> + if (!rcmm)
> + return -EPROBE_DEFER;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_init);
> +
> +static int rcar_cmm_probe(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm;
> +
> + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
> + if (!rcmm)
> + return -ENOMEM;
> + platform_set_drvdata(pdev, rcmm);
> +
> + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(rcmm->base))
> + return PTR_ERR(rcmm->base);
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static int rcar_cmm_remove(struct platform_device *pdev)
> +{
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id rcar_cmm_of_table[] = {
> + { .compatible = "renesas,rcar-gen3-cmm", },
> + { .compatible = "renesas,rcar-gen2-cmm", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
> +
> +static struct platform_driver rcar_cmm_platform_driver = {
> + .probe = rcar_cmm_probe,
> + .remove = rcar_cmm_remove,
> + .driver = {
> + .name = "rcar-cmm",
> + .of_match_table = rcar_cmm_of_table,
> + },
> +};
> +
> +module_platform_driver(rcar_cmm_platform_driver);
> +
> +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
> +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h
b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> new file mode 100644
> index 000000000000..15a2c874b6a6
> --- /dev/null
> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * rcar_cmm.h -- R-Car Display Unit Color Management Module
> + *
> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> + */
> +
> +#ifndef __RCAR_CMM_H__
> +#define __RCAR_CMM_H__
> +
> +#define CM2_LUT_SIZE 256
> +
> +struct drm_color_lut;
> +struct platform_device;
> +
> +/**
> + * struct rcar_cmm_config - CMM configuration
> + *
> + * @lut: 1D-LUT configuration
> + * @lut.enable: 1D-LUT enable flag
> + * @lut.table: 1D-LUT table entries. Might be set to NULL when the
CMM has to
> + * be re-enabled but not re=programmed.

So lut.table can be NULL ... See comment at rcar_cmm_lut_extract()

> + */
> +struct rcar_cmm_config {
> + struct {
> + bool enable;
> + struct drm_color_lut *table;
> + } lut;
> +};
> +
> +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
> +int rcar_cmm_init(struct platform_device *pdev);
> +
> +int rcar_cmm_enable(struct platform_device *pdev);
> +void rcar_cmm_disable(struct platform_device *pdev);
> +
> +int rcar_cmm_setup(struct platform_device *pdev,
> + const struct rcar_cmm_config *config);
> +#else
> +static inline int rcar_cmm_init(struct platform_device *pdev)
> +{
> + return 0;
> +}
> +
> +static inline int rcar_cmm_enable(struct platform_device *pdev)
> +{
> + return 0;
> +}
> +
> +static inline void rcar_cmm_disable(struct platform_device *pdev)
> +{
> +}
> +
> +static int rcar_cmm_setup(struct platform_device *pdev,
> + const struct rcar_cmm_config *config)
> +{
> + return 0;
> +}
> +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
> +
> +#endif /* __RCAR_CMM_H__ */
>

2019-09-11 18:19:43

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 9/9] arm64: dts: renesas: Add CMM units to Gen3 SoCs

Hi Jacopo,

On 06/09/2019 14:54, Jacopo Mondi wrote:
> Add CMM units to Renesas R-Car Gen3 SoC that support it, and reference them
> from the Display Unit they are connected to.
>
> Sort the 'vsps' and 'renesas,cmm' entries in the DU unit consistently
> in all the involved DTS.

I think if you chose the ordering in the r8a7795, then you only have to
adjust/correct the ordering in the r8a7796 and r8a77965 ...

Especially as you haven't changed the ordering of r8a77970, and r8a77980
which have the status after the vsps entry.


>
> Signed-off-by: Jacopo Mondi <[email protected]>
> ---
> arch/arm64/boot/dts/renesas/r8a7795.dtsi | 40 ++++++++++++++++++++++-
> arch/arm64/boot/dts/renesas/r8a7796.dtsi | 28 ++++++++++++++++
> arch/arm64/boot/dts/renesas/r8a77965.dtsi | 28 ++++++++++++++++
> arch/arm64/boot/dts/renesas/r8a77990.dtsi | 22 ++++++++++++-
> arch/arm64/boot/dts/renesas/r8a77995.dtsi | 22 ++++++++++++-
> 5 files changed, 137 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> index 6675462f7585..67c242a447bc 100644
> --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> @@ -2939,6 +2939,42 @@
> iommus = <&ipmmu_vi1 10>;
> };
>
> + cmm0: cmm@fea40000 {
> + compatible = "renesas,r8a7795-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea40000 0 0x1000>;
> + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 711>;
> + resets = <&cpg 711>;
> + };
> +
> + cmm1: cmm@fea50000 {
> + compatible = "renesas,r8a7795-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea50000 0 0x1000>;
> + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 710>;
> + resets = <&cpg 710>;
> + };
> +
> + cmm2: cmm@fea60000 {
> + compatible = "renesas,r8a7795-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea60000 0 0x1000>;
> + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 709>;
> + resets = <&cpg 709>;
> + };
> +
> + cmm3: cmm@fea70000 {
> + compatible = "renesas,r8a7795-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea70000 0 0x1000>;
> + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 708>;
> + resets = <&cpg 708>;
> + };
> +
> csi20: csi2@fea80000 {
> compatible = "renesas,r8a7795-csi2";
> reg = <0 0xfea80000 0 0x10000>;
> @@ -3142,9 +3178,11 @@
> <&cpg CPG_MOD 722>,
> <&cpg CPG_MOD 721>;
> clock-names = "du.0", "du.1", "du.2", "du.3";
> - vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>;
> status = "disabled";

I'm not sure the vsps should be below the status = disabled line.

I'd have this as:

clock-names...
vsps...
renesas,cmms...
<blank line>
status...
<blank line>
ports...

>
> + vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>;
> + renesas,cmms = <&cmm0 &cmm1 &cmm2 &cmm3>;

I think these should be separated by comma's to show they are separate
references, or references to separate phandles or such.

The only precedence I could find was in pmu_a53:

interrupt-affinity = <&a53_0>, <&a53_1>, <&a53_2>, <&a53_3>;


> +
> ports {
> #address-cells = <1>;
> #size-cells = <0>;
> diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
> index 822c96601d3c..837c3b2da773 100644
> --- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
> @@ -2641,6 +2641,33 @@
> renesas,fcp = <&fcpvi0>;
> };
>
> + cmm0: cmm@fea40000 {
> + compatible = "renesas,r8a7796-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea40000 0 0x1000>;
> + power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 711>;
> + resets = <&cpg 711>;
> + };
> +
> + cmm1: cmm@fea50000 {
> + compatible = "renesas,r8a7796-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea50000 0 0x1000>;
> + power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 710>;
> + resets = <&cpg 710>;
> + };
> +
> + cmm2: cmm@fea60000 {
> + compatible = "renesas,r8a7796-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea60000 0 0x1000>;
> + power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 709>;
> + resets = <&cpg 709>;
> + };
> +
> csi20: csi2@fea80000 {
> compatible = "renesas,r8a7796-csi2";
> reg = <0 0xfea80000 0 0x10000>;
> @@ -2794,6 +2821,7 @@
> status = "disabled";
>
> vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>;
> + renesas,cmms = <&cmm0 &cmm1 &cmm2>;

Aha, yes, I'd move this vsps rather than the one at r8a7795, which I'd
consider to be more 'correct'.


>
> ports {
> #address-cells = <1>;
> diff --git a/arch/arm64/boot/dts/renesas/r8a77965.dtsi b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
> index 4ae163220f60..c7635e8b261c 100644
> --- a/arch/arm64/boot/dts/renesas/r8a77965.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
> @@ -2320,6 +2320,33 @@
> resets = <&cpg 611>;
> };
>
> + cmm0: cmm@fea40000 {
> + compatible = "renesas,r8a77965-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea40000 0 0x1000>;
> + power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 711>;
> + resets = <&cpg 711>;
> + };
> +
> + cmm1: cmm@fea50000 {
> + compatible = "renesas,r8a77965-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea50000 0 0x1000>;
> + power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 710>;
> + resets = <&cpg 710>;
> + };
> +
> + cmm3: cmm@fea70000 {
> + compatible = "renesas,r8a77965-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea70000 0 0x1000>;
> + power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 708>;
> + resets = <&cpg 708>;
> + };
> +
> csi20: csi2@fea80000 {
> compatible = "renesas,r8a77965-csi2";
> reg = <0 0xfea80000 0 0x10000>;
> @@ -2470,6 +2497,7 @@
> status = "disabled";
>
> vsps = <&vspd0 0>, <&vspd1 0>, <&vspd0 1>;
> + renesas,cmms = <&cmm0 &cmm1 &cmm3>;

Again, I'd consider this the wrong sort order, due to the status'
importance. I wouldn't hide it in the middle.

>
> ports {
> #address-cells = <1>;
> diff --git a/arch/arm64/boot/dts/renesas/r8a77990.dtsi b/arch/arm64/boot/dts/renesas/r8a77990.dtsi
> index 455954c3d98e..5e3d758a033f 100644
> --- a/arch/arm64/boot/dts/renesas/r8a77990.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a77990.dtsi
> @@ -1727,6 +1727,24 @@
> iommus = <&ipmmu_vi0 9>;
> };
>
> + cmm0: cmm@fea40000 {
> + compatible = "renesas,r8a77990-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea40000 0 0x1000>;
> + power-domains = <&sysc R8A77990_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 711>;
> + resets = <&cpg 711>;
> + };
> +
> + cmm1: cmm@fea50000 {
> + compatible = "renesas,r8a77990-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea50000 0 0x1000>;
> + power-domains = <&sysc R8A77990_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 710>;
> + resets = <&cpg 710>;
> + };
> +
> csi40: csi2@feaa0000 {
> compatible = "renesas,r8a77990-csi2";
> reg = <0 0xfeaa0000 0 0x10000>;
> @@ -1768,9 +1786,11 @@
> clock-names = "du.0", "du.1";
> resets = <&cpg 724>;
> reset-names = "du.0";
> - vsps = <&vspd0 0>, <&vspd1 0>;
> status = "disabled";
>
> + vsps = <&vspd0 0>, <&vspd1 0>;
> + renesas,cmms = <&cmm0 &cmm1>;


Same ... :D

> +
> ports {
> #address-cells = <1>;
> #size-cells = <0>;
> diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi
> index 183fef86cf7c..6838a81f5caa 100644
> --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi
> @@ -993,6 +993,24 @@
> iommus = <&ipmmu_vi0 9>;
> };
>
> + cmm0: cmm@fea40000 {
> + compatible = "renesas,r8a77995-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea40000 0 0x1000>;
> + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 711>;
> + resets = <&cpg 711>;
> + };
> +
> + cmm1: cmm@fea50000 {
> + compatible = "renesas,r8a77995-cmm",
> + "renesas,rcar-gen3-cmm";
> + reg = <0 0xfea50000 0 0x1000>;
> + power-domains = <&sysc R8A77995_PD_ALWAYS_ON>;
> + clocks = <&cpg CPG_MOD 710>;
> + resets = <&cpg 710>;
> + };
> +
> du: display@feb00000 {
> compatible = "renesas,du-r8a77995";
> reg = <0 0xfeb00000 0 0x40000>;
> @@ -1003,9 +1021,11 @@
> clock-names = "du.0", "du.1";
> resets = <&cpg 724>;
> reset-names = "du.0";
> - vsps = <&vspd0 0>, <&vspd1 0>;
> status = "disabled";
>
> + vsps = <&vspd0 0>, <&vspd1 0>;
> + renesas,cmms = <&cmm0 &cmm1>;
> +

Same.

> ports {
> #address-cells = <1>;
> #size-cells = <0>;
>

2019-09-11 18:43:00

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 6/9] drm: rcar-du: crtc: Enable and disable CMMs

Hi Jacopo,

On 06/09/2019 14:54, Jacopo Mondi wrote:
> Enable/disable the CMM associated with a CRTC at CRTC start and stop
> time and enable the CMM unit through the Display Extensional Functions
> register at group setup time.
>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Jacopo Mondi <[email protected]>
> ---
> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++
> drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++++++
> drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +++++
> 3 files changed, 20 insertions(+)
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> index 23f1d6cc1719..3dac605c3a67 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> @@ -21,6 +21,7 @@
> #include <drm/drm_plane_helper.h>
> #include <drm/drm_vblank.h>
>
> +#include "rcar_cmm.h"
> #include "rcar_du_crtc.h"
> #include "rcar_du_drv.h"
> #include "rcar_du_encoder.h"
> @@ -619,6 +620,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
> if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
> rcar_du_vsp_disable(rcrtc);
>
> + if (rcrtc->cmm)
> + rcar_cmm_disable(rcrtc->cmm);
> +
> /*
> * Select switch sync mode. This stops display operation and configures
> * the HSYNC and VSYNC signals as inputs.
> @@ -686,6 +690,9 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
> }
>
> rcar_du_crtc_start(rcrtc);
> +
> + if (rcrtc->cmm)
> + rcar_cmm_enable(rcrtc->cmm);
> }
>
> static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> index 9eee47969e77..25d0fc125d7a 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> @@ -147,6 +147,14 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
>
> rcar_du_group_setup_pins(rgrp);
>
> + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
> + u32 defr7 = DEFR7_CODE
> + | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
> + | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
> +
> + rcar_du_group_write(rgrp, DEFR7, defr7);
> + }
> +

What's the effect here on platforms with a CMM, but with
CONFIG_DRM_RCAR_CMM unset?

Will this incorrectly configure the DU ?

Will it stall the display if the DU tries to interact with another
module which is not enabled?


> if (rcdu->info->gen >= 2) {
> rcar_du_group_setup_defr8(rgrp);
> rcar_du_group_setup_didsr(rgrp);
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> index bc87f080b170..fb9964949368 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> @@ -197,6 +197,11 @@
> #define DEFR6_MLOS1 (1 << 2)
> #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)
>
> +#define DEFR7 0x000ec
> +#define DEFR7_CODE (0x7779 << 16)
> +#define DEFR7_CMME1 BIT(6)
> +#define DEFR7_CMME0 BIT(4)
> +
> /* -----------------------------------------------------------------------------
> * R8A7790-only Control Registers
> */
>

2019-09-11 21:43:29

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 7/9] drm: rcar-du: crtc: Register GAMMA_LUT properties

Hi Jacopo,

On 06/09/2019 14:54, Jacopo Mondi wrote:
> Enable the GAMMA_LUT KMS property using the framework helpers to
> register the property and set the associated gamma table maximum size.
>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>

LGTM.

Reviewed-by: Kieran Bingham <[email protected]>

> Signed-off-by: Jacopo Mondi <[email protected]>
> ---
> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> index 3dac605c3a67..dc59eeec990c 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> @@ -1082,6 +1082,7 @@ static const struct drm_crtc_funcs crtc_funcs_gen3 = {
> .set_crc_source = rcar_du_crtc_set_crc_source,
> .verify_crc_source = rcar_du_crtc_verify_crc_source,
> .get_crc_sources = rcar_du_crtc_get_crc_sources,
> + .gamma_set = drm_atomic_helper_legacy_gamma_set,
> };
>
> /* -----------------------------------------------------------------------------
> @@ -1205,6 +1206,9 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
> if (rcdu->cmms[swindex]) {
> rcrtc->cmm = rcdu->cmms[swindex];
> rgrp->cmms_mask |= BIT(hwindex % 2);
> +
> + drm_mode_crtc_set_gamma_size(crtc, CM2_LUT_SIZE);
> + drm_crtc_enable_color_mgmt(crtc, 0, false, CM2_LUT_SIZE);
> }
>
> drm_crtc_helper_add(crtc, &crtc_helper_funcs);
>

2019-09-11 22:21:02

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH v4 9/9] arm64: dts: renesas: Add CMM units to Gen3 SoCs

Hi Kieran, Jacopo,

On Wed, Sep 11, 2019 at 8:16 PM Kieran Bingham
<[email protected]> wrote:
> On 06/09/2019 14:54, Jacopo Mondi wrote:
> > Add CMM units to Renesas R-Car Gen3 SoC that support it, and reference them
> > from the Display Unit they are connected to.
> >
> > Sort the 'vsps' and 'renesas,cmm' entries in the DU unit consistently
> > in all the involved DTS.
>
> I think if you chose the ordering in the r8a7795, then you only have to
> adjust/correct the ordering in the r8a7796 and r8a77965 ...
>
> Especially as you haven't changed the ordering of r8a77970, and r8a77980
> which have the status after the vsps entry.
>
>
> >
> > Signed-off-by: Jacopo Mondi <[email protected]>

> > --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> > +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi

> > @@ -3142,9 +3178,11 @@
> > <&cpg CPG_MOD 722>,
> > <&cpg CPG_MOD 721>;
> > clock-names = "du.0", "du.1", "du.2", "du.3";
> > - vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>;
> > status = "disabled";
>
> I'm not sure the vsps should be below the status = disabled line.
>
> I'd have this as:
>
> clock-names...
> vsps...
> renesas,cmms...
> <blank line>
> status...
> <blank line>
> ports...

Indeed.

And better write "ports { ... }", so it's clear this is a subnode.

>
> >
> > + vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>;

And the above will become "renesas,vsps", needing another reordering?

> > + renesas,cmms = <&cmm0 &cmm1 &cmm2 &cmm3>;
>
> I think these should be separated by comma's to show they are separate
> references, or references to separate phandles or such.

Yep, looks better, and makes the grouping clear.

> The only precedence I could find was in pmu_a53:
>
> interrupt-affinity = <&a53_0>, <&a53_1>, <&a53_2>, <&a53_3>;

That's because most other phandle stuff has #<foo>-cells as non-zero.

We do have

clocks = ... <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>;

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2019-09-12 08:00:10

by jacopo mondi

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Kiernan,
thanks for review

On Wed, Sep 11, 2019 at 04:54:58PM +0100, Kieran Bingham wrote:
> Hi Jacopo,
>
> <This time replying to the mail with ML's included Doh!>
>
> On 06/09/2019 14:43, Jacopo Mondi wrote:
> > Add a driver for the R-Car Display Unit Color Correction Module.
> >
> > In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
> > to perform image enhancement and color correction.
> >
> > Add support for CMM through a driver that supports configuration of
> > the 1-dimensional LUT table. More advanced CMM feature will be
>
> s/feature/features/
>
> > implemented on top of this basic one.
>
> s/basic/initial/
>
> >
> > Signed-off-by: Jacopo Mondi <[email protected]>
> > ---
> > drivers/gpu/drm/rcar-du/Kconfig | 7 +
> > drivers/gpu/drm/rcar-du/Makefile | 1 +
> > drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
> > drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
> > 4 files changed, 320 insertions(+)
> > create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
> > create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
> >
> > diff --git a/drivers/gpu/drm/rcar-du/Kconfig
> b/drivers/gpu/drm/rcar-du/Kconfig
> > index 1529849e217e..539d232790d1 100644
> > --- a/drivers/gpu/drm/rcar-du/Kconfig
> > +++ b/drivers/gpu/drm/rcar-du/Kconfig
> > @@ -13,6 +13,13 @@ config DRM_RCAR_DU
> > Choose this option if you have an R-Car chipset.
> > If M is selected the module will be called rcar-du-drm.
> >
> > +config DRM_RCAR_CMM
> > + bool "R-Car DU Color Management Module (CMM) Support"
> > + depends on DRM && OF
> > + depends on DRM_RCAR_DU
> > + help
> > + Enable support for R-Car Color Management Module (CMM).
> > +
> > config DRM_RCAR_DW_HDMI
> > tristate "R-Car DU Gen3 HDMI Encoder Support"
> > depends on DRM && OF
> > diff --git a/drivers/gpu/drm/rcar-du/Makefile
> b/drivers/gpu/drm/rcar-du/Makefile
> > index 6c2ed9c46467..4d1187ccc3e5 100644
> > --- a/drivers/gpu/drm/rcar-du/Makefile
> > +++ b/drivers/gpu/drm/rcar-du/Makefile
> > @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
> > rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
> > rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
> >
> > +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
> > obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
> > obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
> > obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c
> b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> > new file mode 100644
> > index 000000000000..3cacdc4474c7
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> > @@ -0,0 +1,251 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * rcar_cmm.c -- R-Car Display Unit Color Management Module
> > + *
> > + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +
> > +#include <drm/drm_color_mgmt.h>
> > +
> > +#include "rcar_cmm.h"
> > +
> > +#define CM2_LUT_CTRL 0x0000
> > +#define CM2_LUT_CTRL_LUT_EN BIT(0)
>
> I'd have a new line here
>
> > +#define CM2_LUT_TBL_BASE 0x0600
> > +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
> > +
> > +struct rcar_cmm {
> > + void __iomem *base;
> > + bool enabled;
> > +
> > + /*
> > + * @lut: 1D-LUT status
> > + * @lut.enabled: 1D-LUT enabled flag
> > + * @lut.table: Table of 1D-LUT entries scaled to hardware
> > + * precision (8-bits per color component)
> > + */
> > + struct {
> > + bool enabled;
> > + u32 table[CM2_LUT_SIZE];
> > + } lut;
> > +};
> > +
> > +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
> > +{
> > + return ioread32(rcmm->base + reg);
> > +}
> > +
> > +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32
> data)
> > +{
> > + iowrite32(data, rcmm->base + reg);
> > +}
> > +
> > +/*
> > + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM
> LUT table
> > + * entries and store them.
> > + * @rcmm: Pointer to the CMM device
> > + * @drm_lut: Pointer to the DRM LUT table
> > + */
> > +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
> > + const struct drm_color_lut *drm_lut)
> > +{
> > + unsigned int i;
>
> I think you're missing the following here:
>
> if (!drm_lut)
> return;
>
> You mention below that drm_lut could be passed in as NULL, which would
> cause a segfault here otherwise.
>

Thanks for spotting, but I should have removed that comment, as I have
killed that case in rcar_du_atomic_commit_update_cmm() in patch 8/9,
as from my testing it seems it is not possible to provide from
userspace a non populated LUT table to just re-enable the CMM.

So we're fine here.

>
> > +
> > + for (i = 0; i < CM2_LUT_SIZE; ++i) {
> > + const struct drm_color_lut *lut = &drm_lut[i];
> > +
> > + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
> > + | drm_color_lut_extract(lut->green, 8) << 8
> > + | drm_color_lut_extract(lut->blue, 8);
> > + }
> > +}
> > +
> > +/*
> > + * rcar_cmm_lut_write() - Write to hardware the LUT table entries
> from the
> > + * local table.
> > + * @rcmm: Pointer to the CMM device
> > + */
> > +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
> > +{
> > + unsigned int i;
> > +
> > + for (i = 0; i < CM2_LUT_SIZE; ++i)
> > + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
> > +}
> > +
> > +/*
> > + * rcar_cmm_setup() - Configure the CMM unit.
> > + * @pdev: The platform device associated with the CMM instance
> > + * @config: The CRTC-provided configuration.
>
> I don't think CRTC-provided should be hyphenated like that.
> Perhaps just:
> "The colour management configuration".
>
> As I don't think who provides the configuration is relevant to the
> actual call.
>

I have no idea. I've been bought into this by the comment

"CRTC-provided" is a compound adjective, qualifying the noun
"configuration". It thus needs a hyphen.

I'm happy to remove it for a simpler version.

Thanks
j

> > + * Configure the CMM unit with the CRTC-provided configuration.
>
> s/CRTC-provided/given/
>
> > + * Currently enabling, disabling and programming of the 1-D LUT unit is
> > + * supported.
> > + */
> > +int rcar_cmm_setup(struct platform_device *pdev,
> > + const struct rcar_cmm_config *config)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > +
> > + /*
> > + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
> > + * be called when the CMM is disabled. As we can't program the hardware
> > + * in that case, store the configuration internally and apply it when
> > + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
> > + */
> > + if (!rcmm->enabled) {
> > + if (!config->lut.enable)
> > + return 0;
> > +
> > + rcar_cmm_lut_extract(rcmm, config->lut.table);
> > + rcmm->lut.enabled = true;
> > +
> > + return 0;
> > + }
> > +
> > + /* Stop LUT operations if requested. */
> > + if (!config->lut.enable) {
> > + if (rcmm->lut.enabled) {
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> > + rcmm->lut.enabled = false;
> > + }
> > +
> > + return 0;
> > + }
> > +
> > + /*
> > + * Enable LUT and program the new gamma table values.
> > + *
> > + * FIXME: In order to have stable operations it is required to first
> > + * enable the 1D-LUT and then program its table entries. This seems to
> > + * contradict what the chip manual reports, and will have to be
> > + * reconsidered when implementing support for double buffering.
> > + */
> > + if (!rcmm->lut.enabled) {
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> > + rcmm->lut.enabled = true;
> > + }
> > +
> > + rcar_cmm_lut_extract(rcmm, config->lut.table);
> > + rcar_cmm_lut_write(rcmm);
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
> > +
> > +/*
> > + * rcar_cmm_enable() - Enable the CMM unit.
> > + * @pdev: The platform device associated with the CMM instance
> > + *
> > + * Enable the CMM unit by enabling the parent clock and enabling the CMM
> > + * components, such as 1-D LUT, if requested.
> > + */
> > +int rcar_cmm_enable(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > + int ret;
> > +
> > + ret = pm_runtime_get_sync(&pdev->dev);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
> > + if (rcmm->lut.enabled) {
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> > + rcar_cmm_lut_write(rcmm);
> > + }
> > +
> > + rcmm->enabled = true;
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
> > +
> > +/*
> > + * rcar_cmm_disable() - Disable the CMM unit.
> > + * @pdev: The platform device associated with the CMM instance
> > + *
> > + * Disable the CMM unit by stopping the parent clock.
> > + */
> > +void rcar_cmm_disable(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > +
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> > +
> > + pm_runtime_put(&pdev->dev);
> > +
> > + rcmm->lut.enabled = false;
> > + rcmm->enabled = false;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
> > +
> > +/*
> > + * rcar_cmm_init() - Make sure the CMM has probed.
> > + * @pdev: The platform device associated with the CMM instance
> > + *
> > + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
> > + */
> > +int rcar_cmm_init(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > +
> > + if (!rcmm)
> > + return -EPROBE_DEFER;
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_init);
> > +
> > +static int rcar_cmm_probe(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm;
> > +
> > + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
> > + if (!rcmm)
> > + return -ENOMEM;
> > + platform_set_drvdata(pdev, rcmm);
> > +
> > + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(rcmm->base))
> > + return PTR_ERR(rcmm->base);
> > +
> > + pm_runtime_enable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_cmm_remove(struct platform_device *pdev)
> > +{
> > + pm_runtime_disable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id rcar_cmm_of_table[] = {
> > + { .compatible = "renesas,rcar-gen3-cmm", },
> > + { .compatible = "renesas,rcar-gen2-cmm", },
> > + { },
> > +};
> > +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
> > +
> > +static struct platform_driver rcar_cmm_platform_driver = {
> > + .probe = rcar_cmm_probe,
> > + .remove = rcar_cmm_remove,
> > + .driver = {
> > + .name = "rcar-cmm",
> > + .of_match_table = rcar_cmm_of_table,
> > + },
> > +};
> > +
> > +module_platform_driver(rcar_cmm_platform_driver);
> > +
> > +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
> > +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h
> b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> > new file mode 100644
> > index 000000000000..15a2c874b6a6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> > @@ -0,0 +1,61 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * rcar_cmm.h -- R-Car Display Unit Color Management Module
> > + *
> > + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> > + */
> > +
> > +#ifndef __RCAR_CMM_H__
> > +#define __RCAR_CMM_H__
> > +
> > +#define CM2_LUT_SIZE 256
> > +
> > +struct drm_color_lut;
> > +struct platform_device;
> > +
> > +/**
> > + * struct rcar_cmm_config - CMM configuration
> > + *
> > + * @lut: 1D-LUT configuration
> > + * @lut.enable: 1D-LUT enable flag
> > + * @lut.table: 1D-LUT table entries. Might be set to NULL when the
> CMM has to
> > + * be re-enabled but not re=programmed.
>
> So lut.table can be NULL ... See comment at rcar_cmm_lut_extract()
>
> > + */
> > +struct rcar_cmm_config {
> > + struct {
> > + bool enable;
> > + struct drm_color_lut *table;
> > + } lut;
> > +};
> > +
> > +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
> > +int rcar_cmm_init(struct platform_device *pdev);
> > +
> > +int rcar_cmm_enable(struct platform_device *pdev);
> > +void rcar_cmm_disable(struct platform_device *pdev);
> > +
> > +int rcar_cmm_setup(struct platform_device *pdev,
> > + const struct rcar_cmm_config *config);
> > +#else
> > +static inline int rcar_cmm_init(struct platform_device *pdev)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int rcar_cmm_enable(struct platform_device *pdev)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void rcar_cmm_disable(struct platform_device *pdev)
> > +{
> > +}
> > +
> > +static int rcar_cmm_setup(struct platform_device *pdev,
> > + const struct rcar_cmm_config *config)
> > +{
> > + return 0;
> > +}
> > +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
> > +
> > +#endif /* __RCAR_CMM_H__ */
> >
>


Attachments:
(No filename) (13.15 kB)
signature.asc (849.00 B)
Download all attachments

2019-09-12 08:08:31

by jacopo mondi

[permalink] [raw]
Subject: Re: [PATCH v4 6/9] drm: rcar-du: crtc: Enable and disable CMMs

Hi Kieran,

On Wed, Sep 11, 2019 at 07:40:27PM +0100, Kieran Bingham wrote:
> Hi Jacopo,
>
> On 06/09/2019 14:54, Jacopo Mondi wrote:
> > Enable/disable the CMM associated with a CRTC at CRTC start and stop
> > time and enable the CMM unit through the Display Extensional Functions
> > register at group setup time.
> >
> > Reviewed-by: Ulrich Hecht <[email protected]>
> > Reviewed-by: Laurent Pinchart <[email protected]>
> > Signed-off-by: Jacopo Mondi <[email protected]>
> > ---
> > drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++
> > drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++++++
> > drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +++++
> > 3 files changed, 20 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> > index 23f1d6cc1719..3dac605c3a67 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> > @@ -21,6 +21,7 @@
> > #include <drm/drm_plane_helper.h>
> > #include <drm/drm_vblank.h>
> >
> > +#include "rcar_cmm.h"
> > #include "rcar_du_crtc.h"
> > #include "rcar_du_drv.h"
> > #include "rcar_du_encoder.h"
> > @@ -619,6 +620,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
> > if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
> > rcar_du_vsp_disable(rcrtc);
> >
> > + if (rcrtc->cmm)
> > + rcar_cmm_disable(rcrtc->cmm);
> > +
> > /*
> > * Select switch sync mode. This stops display operation and configures
> > * the HSYNC and VSYNC signals as inputs.
> > @@ -686,6 +690,9 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
> > }
> >
> > rcar_du_crtc_start(rcrtc);
> > +
> > + if (rcrtc->cmm)
> > + rcar_cmm_enable(rcrtc->cmm);
> > }
> >
> > static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> > index 9eee47969e77..25d0fc125d7a 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> > @@ -147,6 +147,14 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
> >
> > rcar_du_group_setup_pins(rgrp);
> >
> > + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
> > + u32 defr7 = DEFR7_CODE
> > + | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
> > + | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
> > +
> > + rcar_du_group_write(rgrp, DEFR7, defr7);
> > + }
> > +
>
> What's the effect here on platforms with a CMM, but with
> CONFIG_DRM_RCAR_CMM unset?
>
> Will this incorrectly configure the DU ?
>
> Will it stall the display if the DU tries to interact with another
> module which is not enabled?

I recall I tested that (that's why I had to add stubs for CMM
functions, as I had linkage errors otherwise) and thing seems to be
fine as the CMM configuration/enblement resolve to an empty function.

Would you prefer to have this guarded by an #if IS_ENABLED() ?

Thanks
j
>
>
> > if (rcdu->info->gen >= 2) {
> > rcar_du_group_setup_defr8(rgrp);
> > rcar_du_group_setup_didsr(rgrp);
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> > index bc87f080b170..fb9964949368 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> > @@ -197,6 +197,11 @@
> > #define DEFR6_MLOS1 (1 << 2)
> > #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)
> >
> > +#define DEFR7 0x000ec
> > +#define DEFR7_CODE (0x7779 << 16)
> > +#define DEFR7_CMME1 BIT(6)
> > +#define DEFR7_CMME0 BIT(4)
> > +
> > /* -----------------------------------------------------------------------------
> > * R8A7790-only Control Registers
> > */
> >
>


Attachments:
(No filename) (3.77 kB)
signature.asc (849.00 B)
Download all attachments

2019-09-12 09:24:53

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 6/9] drm: rcar-du: crtc: Enable and disable CMMs

Hi Jacopo,

On 12/09/2019 09:07, Jacopo Mondi wrote:
> Hi Kieran,
>
> On Wed, Sep 11, 2019 at 07:40:27PM +0100, Kieran Bingham wrote:
>> Hi Jacopo,
>>
>> On 06/09/2019 14:54, Jacopo Mondi wrote:
>>> Enable/disable the CMM associated with a CRTC at CRTC start and stop
>>> time and enable the CMM unit through the Display Extensional Functions
>>> register at group setup time.
>>>
>>> Reviewed-by: Ulrich Hecht <[email protected]>
>>> Reviewed-by: Laurent Pinchart <[email protected]>
>>> Signed-off-by: Jacopo Mondi <[email protected]>
>>> ---
>>> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++
>>> drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++++++
>>> drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +++++
>>> 3 files changed, 20 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
>>> index 23f1d6cc1719..3dac605c3a67 100644
>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
>>> @@ -21,6 +21,7 @@
>>> #include <drm/drm_plane_helper.h>
>>> #include <drm/drm_vblank.h>
>>>
>>> +#include "rcar_cmm.h"
>>> #include "rcar_du_crtc.h"
>>> #include "rcar_du_drv.h"
>>> #include "rcar_du_encoder.h"
>>> @@ -619,6 +620,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
>>> if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
>>> rcar_du_vsp_disable(rcrtc);
>>>
>>> + if (rcrtc->cmm)
>>> + rcar_cmm_disable(rcrtc->cmm);
>>> +
>>> /*
>>> * Select switch sync mode. This stops display operation and configures
>>> * the HSYNC and VSYNC signals as inputs.
>>> @@ -686,6 +690,9 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
>>> }
>>>
>>> rcar_du_crtc_start(rcrtc);
>>> +
>>> + if (rcrtc->cmm)
>>> + rcar_cmm_enable(rcrtc->cmm);
>>> }
>>>
>>> static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
>>> index 9eee47969e77..25d0fc125d7a 100644
>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
>>> @@ -147,6 +147,14 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
>>>
>>> rcar_du_group_setup_pins(rgrp);
>>>
>>> + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
>>> + u32 defr7 = DEFR7_CODE
>>> + | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
>>> + | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
>>> +
>>> + rcar_du_group_write(rgrp, DEFR7, defr7);
>>> + }
>>> +
>>
>> What's the effect here on platforms with a CMM, but with
>> CONFIG_DRM_RCAR_CMM unset?
>>
>> Will this incorrectly configure the DU ?
>>
>> Will it stall the display if the DU tries to interact with another
>> module which is not enabled?
>
> I recall I tested that (that's why I had to add stubs for CMM
> functions, as I had linkage errors otherwise) and thing seems to be
> fine as the CMM configuration/enblement resolve to an empty function.

Yes, I see the stubs to allow for linkage, but it's the hardware I'm
concerned about. If it passes the tests and doesn't break then that's
probably ok ... but I'm really weary that we're enabling a hardware
pipeline with a disabled component in the middle.


> Would you prefer to have this guarded by an #if IS_ENABLED() ?

I don't think we need a compile time conditional, but I'd say it
probably needs to be more about whether the CMM has actually probed or not

Aha, and I see that in rcar_du_cmm_init() we already do a
call to rcar_cmm_init(), which if fails will leave rcdu->cmms[i] as
NULL. So that's catered for, which results in the rgrp->cmms_mask being
correctly representative of whether there is a CMM connected or not.

... so I think that means the ...
"if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM))" is somewhat redundant:


This could be:

if (rgrp->cmms_mask) {
u32 defr7 = DEFR7_CODE
| (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
| (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);

rcar_du_group_write(rgrp, DEFR7, defr7);

Or in fact, if we don't mind writing 0 to DEFR7 when there is no CMM
(which is safe by the looks of things as DEFR7 is available on all
platforms), then we can even remove the outer conditional, and leave
this all up to the ternary operators to write the correct value to the
defr7.


Phew ... net result - your current code *is* safe with the
CONFIG_DRM_RCAR_CMM option disabled. I'll leave it up to you if you want
to simplify the code here and remove the RCAR_DU_FEATURE_CMM.

As this RCAR_DU_FEATURE_CMM flag is only checked here, removing it would
however simplify all of the rcar_du_device_info structures.

So - with or without the _FEATURE_CMM" simplification, this patch looks
functional and safe so:


Reviewed-by: Kieran Bingham <[email protected]>


> Thanks
> j
>>
>>
>>> if (rcdu->info->gen >= 2) {
>>> rcar_du_group_setup_defr8(rgrp);
>>> rcar_du_group_setup_didsr(rgrp);
>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
>>> index bc87f080b170..fb9964949368 100644
>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
>>> @@ -197,6 +197,11 @@
>>> #define DEFR6_MLOS1 (1 << 2)
>>> #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)
>>>
>>> +#define DEFR7 0x000ec
>>> +#define DEFR7_CODE (0x7779 << 16)
>>> +#define DEFR7_CMME1 BIT(6)
>>> +#define DEFR7_CMME0 BIT(4)
>>> +
>>> /* -----------------------------------------------------------------------------
>>> * R8A7790-only Control Registers
>>> */
>>>
>>

2019-09-12 09:54:37

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Jacopo,

On 12/09/2019 08:59, Jacopo Mondi wrote:
> Hi Kiernan,
> thanks for review
>
> On Wed, Sep 11, 2019 at 04:54:58PM +0100, Kieran Bingham wrote:
>> Hi Jacopo,
>>
>> <This time replying to the mail with ML's included Doh!>
>>
>> On 06/09/2019 14:43, Jacopo Mondi wrote:
>>> Add a driver for the R-Car Display Unit Color Correction Module.
>>>
>>> In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
>>> to perform image enhancement and color correction.
>>>
>>> Add support for CMM through a driver that supports configuration of
>>> the 1-dimensional LUT table. More advanced CMM feature will be
>>
>> s/feature/features/
>>
>>> implemented on top of this basic one.
>>
>> s/basic/initial/
>>
>>>
>>> Signed-off-by: Jacopo Mondi <[email protected]>
>>> ---
>>> drivers/gpu/drm/rcar-du/Kconfig | 7 +
>>> drivers/gpu/drm/rcar-du/Makefile | 1 +
>>> drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
>>> drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
>>> 4 files changed, 320 insertions(+)
>>> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
>>> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
>>>
>>> diff --git a/drivers/gpu/drm/rcar-du/Kconfig
>> b/drivers/gpu/drm/rcar-du/Kconfig
>>> index 1529849e217e..539d232790d1 100644
>>> --- a/drivers/gpu/drm/rcar-du/Kconfig
>>> +++ b/drivers/gpu/drm/rcar-du/Kconfig
>>> @@ -13,6 +13,13 @@ config DRM_RCAR_DU
>>> Choose this option if you have an R-Car chipset.
>>> If M is selected the module will be called rcar-du-drm.
>>>
>>> +config DRM_RCAR_CMM
>>> + bool "R-Car DU Color Management Module (CMM) Support"
>>> + depends on DRM && OF
>>> + depends on DRM_RCAR_DU
>>> + help
>>> + Enable support for R-Car Color Management Module (CMM).
>>> +
>>> config DRM_RCAR_DW_HDMI
>>> tristate "R-Car DU Gen3 HDMI Encoder Support"
>>> depends on DRM && OF
>>> diff --git a/drivers/gpu/drm/rcar-du/Makefile
>> b/drivers/gpu/drm/rcar-du/Makefile
>>> index 6c2ed9c46467..4d1187ccc3e5 100644
>>> --- a/drivers/gpu/drm/rcar-du/Makefile
>>> +++ b/drivers/gpu/drm/rcar-du/Makefile
>>> @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
>>> rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
>>> rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
>>>
>>> +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
>>> obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
>>> obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
>>> obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c
>> b/drivers/gpu/drm/rcar-du/rcar_cmm.c
>>> new file mode 100644
>>> index 000000000000..3cacdc4474c7
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
>>> @@ -0,0 +1,251 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +/*
>>> + * rcar_cmm.c -- R-Car Display Unit Color Management Module
>>> + *
>>> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
>>> + */
>>> +
>>> +#include <linux/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm_runtime.h>
>>> +
>>> +#include <drm/drm_color_mgmt.h>
>>> +
>>> +#include "rcar_cmm.h"
>>> +
>>> +#define CM2_LUT_CTRL 0x0000
>>> +#define CM2_LUT_CTRL_LUT_EN BIT(0)
>>
>> I'd have a new line here
>>
>>> +#define CM2_LUT_TBL_BASE 0x0600
>>> +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
>>> +
>>> +struct rcar_cmm {
>>> + void __iomem *base;
>>> + bool enabled;
>>> +
>>> + /*
>>> + * @lut: 1D-LUT status
>>> + * @lut.enabled: 1D-LUT enabled flag
>>> + * @lut.table: Table of 1D-LUT entries scaled to hardware
>>> + * precision (8-bits per color component)
>>> + */
>>> + struct {
>>> + bool enabled;
>>> + u32 table[CM2_LUT_SIZE];
>>> + } lut;
>>> +};
>>> +
>>> +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
>>> +{
>>> + return ioread32(rcmm->base + reg);
>>> +}
>>> +
>>> +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32
>> data)
>>> +{
>>> + iowrite32(data, rcmm->base + reg);
>>> +}
>>> +
>>> +/*
>>> + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM
>> LUT table
>>> + * entries and store them.
>>> + * @rcmm: Pointer to the CMM device
>>> + * @drm_lut: Pointer to the DRM LUT table
>>> + */
>>> +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
>>> + const struct drm_color_lut *drm_lut)
>>> +{
>>> + unsigned int i;
>>
>> I think you're missing the following here:
>>
>> if (!drm_lut)
>> return;
>>
>> You mention below that drm_lut could be passed in as NULL, which would
>> cause a segfault here otherwise.
>>
>
> Thanks for spotting, but I should have removed that comment, as I have
> killed that case in rcar_du_atomic_commit_update_cmm() in patch 8/9,
> as from my testing it seems it is not possible to provide from
> userspace a non populated LUT table to just re-enable the CMM.
>
> So we're fine here.

Great, in that case then with the comment removed, and the other
trivials in this handled however you see fit,

Reviewed-by: Kieran Bingham <[email protected]>



>
>>
>>> +
>>> + for (i = 0; i < CM2_LUT_SIZE; ++i) {
>>> + const struct drm_color_lut *lut = &drm_lut[i];
>>> +
>>> + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
>>> + | drm_color_lut_extract(lut->green, 8) << 8
>>> + | drm_color_lut_extract(lut->blue, 8);
>>> + }
>>> +}
>>> +
>>> +/*
>>> + * rcar_cmm_lut_write() - Write to hardware the LUT table entries
>> from the
>>> + * local table.
>>> + * @rcmm: Pointer to the CMM device
>>> + */
>>> +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
>>> +{
>>> + unsigned int i;
>>> +
>>> + for (i = 0; i < CM2_LUT_SIZE; ++i)
>>> + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
>>> +}
>>> +
>>> +/*
>>> + * rcar_cmm_setup() - Configure the CMM unit.
>>> + * @pdev: The platform device associated with the CMM instance
>>> + * @config: The CRTC-provided configuration.
>>
>> I don't think CRTC-provided should be hyphenated like that.
>> Perhaps just:
>> "The colour management configuration".
>>
>> As I don't think who provides the configuration is relevant to the
>> actual call.
>>
>
> I have no idea. I've been bought into this by the comment
>
> "CRTC-provided" is a compound adjective, qualifying the noun
> "configuration". It thus needs a hyphen.
>
> I'm happy to remove it for a simpler version.

CRTC-provided just looks weird to me :-D however you wish to leave it is
up to you though :D


> +++ b/arch/arm64/boot/dts/renesas/r8a77965.dtsi
@@ -2320,6 +2320,33 @@
resets = <&cpg 611>;
};

+ cmm0: cmm@fea40000 {
+ compatible = "renesas,r8a77965-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea40000 0 0x1000>;
+ power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 711>;
+ resets = <&cpg 711>;
+ };
+
+ cmm1: cmm@fea50000 {
+ compatible = "renesas,r8a77965-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea50000 0 0x1000>;
+ power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 710>;
+ resets = <&cpg 710>;
+ };
+
+ cmm3: cmm@fea70000 {
+ compatible = "renesas,r8a77965-cmm",
+ "renesas,rcar-gen3-cmm";
+ reg = <0 0xfea70000 0 0x1000>;
+ power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
+ clocks = <&cpg CPG_MOD 708>;
+ resets = <&cpg 708>;
+ };
+
csi20: csi2@fea80000 {
compatible = "renesas,r8a77965-csi2";
reg = <0 0xfea80000 0 0x10000>;
@@ -2470,6 +2497,7 @@
status = "disabled";

vsps = <&vspd0 0>, <&vspd1 0>, <&vspd0 1>;
+ renesas,cmms = <&cmm0 &cmm1 &cmm3>;
> Thanks
> j
>
>>> + * Configure the CMM unit with the CRTC-provided configuration.
>>
>> s/CRTC-provided/given/
>>
>>> + * Currently enabling, disabling and programming of the 1-D LUT unit is
>>> + * supported.
>>> + */
>>> +int rcar_cmm_setup(struct platform_device *pdev,
>>> + const struct rcar_cmm_config *config)
>>> +{
>>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>>> +
>>> + /*
>>> + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
>>> + * be called when the CMM is disabled. As we can't program the hardware
>>> + * in that case, store the configuration internally and apply it when
>>> + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
>>> + */
>>> + if (!rcmm->enabled) {
>>> + if (!config->lut.enable)
>>> + return 0;
>>> +
>>> + rcar_cmm_lut_extract(rcmm, config->lut.table);
>>> + rcmm->lut.enabled = true;
>>> +
>>> + return 0;
>>> + }
>>> +
>>> + /* Stop LUT operations if requested. */
>>> + if (!config->lut.enable) {
>>> + if (rcmm->lut.enabled) {
>>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
>>> + rcmm->lut.enabled = false;
>>> + }
>>> +
>>> + return 0;
>>> + }
>>> +
>>> + /*
>>> + * Enable LUT and program the new gamma table values.
>>> + *
>>> + * FIXME: In order to have stable operations it is required to first
>>> + * enable the 1D-LUT and then program its table entries. This seems to
>>> + * contradict what the chip manual reports, and will have to be
>>> + * reconsidered when implementing support for double buffering.
>>> + */
>>> + if (!rcmm->lut.enabled) {
>>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
>>> + rcmm->lut.enabled = true;
>>> + }
>>> +
>>> + rcar_cmm_lut_extract(rcmm, config->lut.table);
>>> + rcar_cmm_lut_write(rcmm);
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
>>> +
>>> +/*
>>> + * rcar_cmm_enable() - Enable the CMM unit.
>>> + * @pdev: The platform device associated with the CMM instance
>>> + *
>>> + * Enable the CMM unit by enabling the parent clock and enabling the CMM
>>> + * components, such as 1-D LUT, if requested.
>>> + */
>>> +int rcar_cmm_enable(struct platform_device *pdev)
>>> +{
>>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>>> + int ret;
>>> +
>>> + ret = pm_runtime_get_sync(&pdev->dev);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
>>> + if (rcmm->lut.enabled) {
>>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
>>> + rcar_cmm_lut_write(rcmm);
>>> + }
>>> +
>>> + rcmm->enabled = true;
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
>>> +
>>> +/*
>>> + * rcar_cmm_disable() - Disable the CMM unit.
>>> + * @pdev: The platform device associated with the CMM instance
>>> + *
>>> + * Disable the CMM unit by stopping the parent clock.
>>> + */
>>> +void rcar_cmm_disable(struct platform_device *pdev)
>>> +{
>>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>>> +
>>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
>>> +
>>> + pm_runtime_put(&pdev->dev);
>>> +
>>> + rcmm->lut.enabled = false;
>>> + rcmm->enabled = false;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
>>> +
>>> +/*
>>> + * rcar_cmm_init() - Make sure the CMM has probed.
>>> + * @pdev: The platform device associated with the CMM instance
>>> + *
>>> + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
>>> + */
>>> +int rcar_cmm_init(struct platform_device *pdev)
>>> +{
>>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>>> +
>>> + if (!rcmm)
>>> + return -EPROBE_DEFER;
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rcar_cmm_init);
>>> +
>>> +static int rcar_cmm_probe(struct platform_device *pdev)
>>> +{
>>> + struct rcar_cmm *rcmm;
>>> +
>>> + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
>>> + if (!rcmm)
>>> + return -ENOMEM;
>>> + platform_set_drvdata(pdev, rcmm);
>>> +
>>> + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
>>> + if (IS_ERR(rcmm->base))
>>> + return PTR_ERR(rcmm->base);
>>> +
>>> + pm_runtime_enable(&pdev->dev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int rcar_cmm_remove(struct platform_device *pdev)
>>> +{
>>> + pm_runtime_disable(&pdev->dev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct of_device_id rcar_cmm_of_table[] = {
>>> + { .compatible = "renesas,rcar-gen3-cmm", },
>>> + { .compatible = "renesas,rcar-gen2-cmm", },
>>> + { },
>>> +};
>>> +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
>>> +
>>> +static struct platform_driver rcar_cmm_platform_driver = {
>>> + .probe = rcar_cmm_probe,
>>> + .remove = rcar_cmm_remove,
>>> + .driver = {
>>> + .name = "rcar-cmm",
>>> + .of_match_table = rcar_cmm_of_table,
>>> + },
>>> +};
>>> +
>>> +module_platform_driver(rcar_cmm_platform_driver);
>>> +
>>> +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
>>> +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h
>> b/drivers/gpu/drm/rcar-du/rcar_cmm.h
>>> new file mode 100644
>>> index 000000000000..15a2c874b6a6
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
>>> @@ -0,0 +1,61 @@
>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>> +/*
>>> + * rcar_cmm.h -- R-Car Display Unit Color Management Module
>>> + *
>>> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
>>> + */
>>> +
>>> +#ifndef __RCAR_CMM_H__
>>> +#define __RCAR_CMM_H__
>>> +
>>> +#define CM2_LUT_SIZE 256
>>> +
>>> +struct drm_color_lut;
>>> +struct platform_device;
>>> +
>>> +/**
>>> + * struct rcar_cmm_config - CMM configuration
>>> + *
>>> + * @lut: 1D-LUT configuration
>>> + * @lut.enable: 1D-LUT enable flag
>>> + * @lut.table: 1D-LUT table entries. Might be set to NULL when the
>> CMM has to
>>> + * be re-enabled but not re=programmed.
>>
>> So lut.table can be NULL ... See comment at rcar_cmm_lut_extract()

Ok - so this comment will be removed.



>>
>>> + */
>>> +struct rcar_cmm_config {
>>> + struct {
>>> + bool enable;
>>> + struct drm_color_lut *table;
>>> + } lut;
>>> +};
>>> +
>>> +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
>>> +int rcar_cmm_init(struct platform_device *pdev);
>>> +
>>> +int rcar_cmm_enable(struct platform_device *pdev);
>>> +void rcar_cmm_disable(struct platform_device *pdev);
>>> +
>>> +int rcar_cmm_setup(struct platform_device *pdev,
>>> + const struct rcar_cmm_config *config);
>>> +#else
>>> +static inline int rcar_cmm_init(struct platform_device *pdev)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +static inline int rcar_cmm_enable(struct platform_device *pdev)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +static inline void rcar_cmm_disable(struct platform_device *pdev)
>>> +{
>>> +}
>>> +
>>> +static int rcar_cmm_setup(struct platform_device *pdev,
>>> + const struct rcar_cmm_config *config)
>>> +{
>>> + return 0;
>>> +}
>>> +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
>>> +
>>> +#endif /* __RCAR_CMM_H__ */
>>>
>>

2019-09-12 09:57:33

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 8/9] drm: rcar-du: kms: Update CMM in atomic commit tail

Hi Jacopo,

On 06/09/2019 14:54, Jacopo Mondi wrote:
> Update CMM settings at in the atomic commit tail helper method.
> The CMM is updated with new gamma values provided to the driver
> in the GAMMA_LUT blob property.
>
> When resuming from system suspend, the DU driver is responsible for
> reprogramming and enabling the CMM unit if it was in use at the time the
> system entered the suspend state. Force the color_mgmt_changed flag to
> true if the DRM gamma lut color transformation property was set in the
> CRTC state duplicated at suspend time, as the CMM gets reprogrammed only
> if said flag is active in the rcar_du_atomic_commit_update_cmm() method.
>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Jacopo Mondi <[email protected]>

Also throwing in an LGTM here.

Reviewed-by: Kieran Bingham <[email protected]>



> ---
>
> Daniel could you have a look if resume bits are worth being moved to the
> DRM core? The color_mgmt_changed flag is set to false when the state is
> duplicated if I read the code correctly, but when this happens in a
> suspend/resume sequence its value should probably be restored to true if
> any color management property was set in the crtc state when system entered
> suspend.
>
> ---
>
> drivers/gpu/drm/rcar-du/rcar_du_drv.c | 20 ++++++++++++++
> drivers/gpu/drm/rcar-du/rcar_du_kms.c | 38 +++++++++++++++++++++++++++
> 2 files changed, 58 insertions(+)
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> index 018480a8f35c..d1003d31cfaf 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> @@ -17,6 +17,7 @@
> #include <linux/slab.h>
> #include <linux/wait.h>
>
> +#include <drm/drm_atomic.h>
> #include <drm/drm_atomic_helper.h>
> #include <drm/drm_fb_cma_helper.h>
> #include <drm/drm_fb_helper.h>
> @@ -482,6 +483,25 @@ static int rcar_du_pm_suspend(struct device *dev)
> static int rcar_du_pm_resume(struct device *dev)
> {
> struct rcar_du_device *rcdu = dev_get_drvdata(dev);
> + struct drm_atomic_state *state = rcdu->ddev->mode_config.suspend_state;
> + unsigned int i;
> +
> + for (i = 0; i < rcdu->num_crtcs; ++i) {
> + struct drm_crtc *crtc = &rcdu->crtcs[i].crtc;
> + struct drm_crtc_state *crtc_state;
> +
> + crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> + if (!crtc_state)
> + continue;
> +
> + /*
> + * Force re-enablement of CMM after system resume if any
> + * of the DRM color transformation properties was set in
> + * the state saved at system suspend time.
> + */
> + if (crtc_state->gamma_lut)
> + crtc_state->color_mgmt_changed = true;
> + }
>
> return drm_mode_config_helper_resume(rcdu->ddev);
> }
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> index 294630e56992..fc30fff0eb8d 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> @@ -22,6 +22,7 @@
> #include <linux/of_platform.h>
> #include <linux/wait.h>
>
> +#include "rcar_cmm.h"
> #include "rcar_du_crtc.h"
> #include "rcar_du_drv.h"
> #include "rcar_du_encoder.h"
> @@ -368,6 +369,40 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
> * Atomic Check and Update
> */
>
> +static void rcar_du_atomic_commit_update_cmm(struct drm_crtc *crtc,
> + struct drm_crtc_state *old_state)
> +{
> + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
> + struct rcar_cmm_config cmm_config = {};
> + struct device *dev = rcrtc->dev->dev;
> + struct drm_property_blob *lut_blob;
> +
> + if (!rcrtc->cmm || !crtc->state->color_mgmt_changed)
> + return;
> +
> + if (!crtc->state->gamma_lut) {
> + cmm_config.lut.enable = false;
> + rcar_cmm_setup(rcrtc->cmm, &cmm_config);
> + return;
> + }
> +
> + lut_blob = crtc->state->gamma_lut;
> + if (lut_blob->length != (CM2_LUT_SIZE * sizeof(struct drm_color_lut))) {
> + /*
> + * We only accept fully populated LUT tables;
> + * be loud here, otherwise the CMM gets silently ignored.
> + */
> + dev_err(dev, "invalid gamma lut size of %lu bytes\n",
> + lut_blob->length);
> + return;
> + }
> +
> + cmm_config.lut.enable = true;
> + cmm_config.lut.table = (struct drm_color_lut *)lut_blob->data;
> +
> + rcar_cmm_setup(rcrtc->cmm, &cmm_config);
> +}
> +
> static int rcar_du_atomic_check(struct drm_device *dev,
> struct drm_atomic_state *state)
> {
> @@ -410,6 +445,9 @@ static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state)
> rcdu->dpad1_source = rcrtc->index;
> }
>
> + for_each_old_crtc_in_state(old_state, crtc, crtc_state, i)
> + rcar_du_atomic_commit_update_cmm(crtc, crtc_state);
> +
> /* Apply the atomic update. */
> drm_atomic_helper_commit_modeset_disables(dev, old_state);
> drm_atomic_helper_commit_planes(dev, old_state,
> --
> 2.23.0
>

2019-09-19 00:02:43

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Jacopo,

Thank you for the patch.

On Fri, Sep 06, 2019 at 03:54:30PM +0200, Jacopo Mondi wrote:
> Add a driver for the R-Car Display Unit Color Correction Module.
>
> In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
> to perform image enhancement and color correction.
>
> Add support for CMM through a driver that supports configuration of
> the 1-dimensional LUT table. More advanced CMM feature will be
> implemented on top of this basic one.
>
> Signed-off-by: Jacopo Mondi <[email protected]>
> ---
> drivers/gpu/drm/rcar-du/Kconfig | 7 +
> drivers/gpu/drm/rcar-du/Makefile | 1 +
> drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
> drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
> 4 files changed, 320 insertions(+)
> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
>
> diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
> index 1529849e217e..539d232790d1 100644
> --- a/drivers/gpu/drm/rcar-du/Kconfig
> +++ b/drivers/gpu/drm/rcar-du/Kconfig
> @@ -13,6 +13,13 @@ config DRM_RCAR_DU
> Choose this option if you have an R-Car chipset.
> If M is selected the module will be called rcar-du-drm.
>
> +config DRM_RCAR_CMM
> + bool "R-Car DU Color Management Module (CMM) Support"
> + depends on DRM && OF
> + depends on DRM_RCAR_DU
> + help
> + Enable support for R-Car Color Management Module (CMM).
> +
> config DRM_RCAR_DW_HDMI
> tristate "R-Car DU Gen3 HDMI Encoder Support"
> depends on DRM && OF
> diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
> index 6c2ed9c46467..4d1187ccc3e5 100644
> --- a/drivers/gpu/drm/rcar-du/Makefile
> +++ b/drivers/gpu/drm/rcar-du/Makefile
> @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
> rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
> rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
>
> +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
> obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
> obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
> obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> new file mode 100644
> index 000000000000..3cacdc4474c7
> --- /dev/null
> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * rcar_cmm.c -- R-Car Display Unit Color Management Module
> + *
> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> + */
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drm_color_mgmt.h>
> +
> +#include "rcar_cmm.h"
> +
> +#define CM2_LUT_CTRL 0x0000
> +#define CM2_LUT_CTRL_LUT_EN BIT(0)
> +#define CM2_LUT_TBL_BASE 0x0600
> +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
> +
> +struct rcar_cmm {
> + void __iomem *base;
> + bool enabled;
> +
> + /*
> + * @lut: 1D-LUT status
> + * @lut.enabled: 1D-LUT enabled flag
> + * @lut.table: Table of 1D-LUT entries scaled to hardware
> + * precision (8-bits per color component)
> + */
> + struct {
> + bool enabled;
> + u32 table[CM2_LUT_SIZE];
> + } lut;
> +};
> +
> +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
> +{
> + return ioread32(rcmm->base + reg);
> +}
> +
> +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
> +{
> + iowrite32(data, rcmm->base + reg);
> +}
> +
> +/*
> + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM LUT table
> + * entries and store them.

"Scale the DRM LUT table entries to hardware precision and store them."

> + * @rcmm: Pointer to the CMM device
> + * @drm_lut: Pointer to the DRM LUT table
> + */
> +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
> + const struct drm_color_lut *drm_lut)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < CM2_LUT_SIZE; ++i) {
> + const struct drm_color_lut *lut = &drm_lut[i];
> +
> + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
> + | drm_color_lut_extract(lut->green, 8) << 8
> + | drm_color_lut_extract(lut->blue, 8);
> + }
> +}
> +
> +/*
> + * rcar_cmm_lut_write() - Write to hardware the LUT table entries from the
> + * local table.

"Write the LUT table entries from the local table to the hardware."

> + * @rcmm: Pointer to the CMM device
> + */
> +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < CM2_LUT_SIZE; ++i)
> + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
> +}
> +
> +/*
> + * rcar_cmm_setup() - Configure the CMM unit.
> + * @pdev: The platform device associated with the CMM instance
> + * @config: The CRTC-provided configuration.
> + *
> + * Configure the CMM unit with the CRTC-provided configuration.
> + * Currently enabling, disabling and programming of the 1-D LUT unit is
> + * supported.
> + */
> +int rcar_cmm_setup(struct platform_device *pdev,
> + const struct rcar_cmm_config *config)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> +
> + /*
> + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
> + * be called when the CMM is disabled. As we can't program the hardware
> + * in that case, store the configuration internally and apply it when
> + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
> + */
> + if (!rcmm->enabled) {
> + if (!config->lut.enable)
> + return 0;
> +
> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> + rcmm->lut.enabled = true;
> +
> + return 0;
> + }
> +
> + /* Stop LUT operations if requested. */
> + if (!config->lut.enable) {
> + if (rcmm->lut.enabled) {
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> + rcmm->lut.enabled = false;
> + }
> +
> + return 0;
> + }
> +
> + /*
> + * Enable LUT and program the new gamma table values.
> + *
> + * FIXME: In order to have stable operations it is required to first
> + * enable the 1D-LUT and then program its table entries. This seems to
> + * contradict what the chip manual reports, and will have to be
> + * reconsidered when implementing support for double buffering.
> + */
> + if (!rcmm->lut.enabled) {
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> + rcmm->lut.enabled = true;
> + }
> +
> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> + rcar_cmm_lut_write(rcmm);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
> +
> +/*
> + * rcar_cmm_enable() - Enable the CMM unit.
> + * @pdev: The platform device associated with the CMM instance
> + *
> + * Enable the CMM unit by enabling the parent clock and enabling the CMM
> + * components, such as 1-D LUT, if requested.
> + */
> +int rcar_cmm_enable(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> + int ret;
> +
> + ret = pm_runtime_get_sync(&pdev->dev);
> + if (ret < 0)
> + return ret;
> +
> + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
> + if (rcmm->lut.enabled) {
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> + rcar_cmm_lut_write(rcmm);
> + }
> +
> + rcmm->enabled = true;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
> +
> +/*
> + * rcar_cmm_disable() - Disable the CMM unit.
> + * @pdev: The platform device associated with the CMM instance
> + *
> + * Disable the CMM unit by stopping the parent clock.
> + */
> +void rcar_cmm_disable(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> +
> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> +
> + pm_runtime_put(&pdev->dev);
> +
> + rcmm->lut.enabled = false;
> + rcmm->enabled = false;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
> +
> +/*
> + * rcar_cmm_init() - Make sure the CMM has probed.

I would document this as "Intialize the CMM" to match the function name.
We may add more initialization in the future.

> + * @pdev: The platform device associated with the CMM instance
> + *
> + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise

0 on success, -EPROBE_DEFER is the CMM isn't availablet yet

> + */
> +int rcar_cmm_init(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> +
> + if (!rcmm)
> + return -EPROBE_DEFER;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rcar_cmm_init);
> +
> +static int rcar_cmm_probe(struct platform_device *pdev)
> +{
> + struct rcar_cmm *rcmm;
> +
> + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
> + if (!rcmm)
> + return -ENOMEM;
> + platform_set_drvdata(pdev, rcmm);
> +
> + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(rcmm->base))
> + return PTR_ERR(rcmm->base);
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static int rcar_cmm_remove(struct platform_device *pdev)
> +{
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id rcar_cmm_of_table[] = {
> + { .compatible = "renesas,rcar-gen3-cmm", },
> + { .compatible = "renesas,rcar-gen2-cmm", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
> +
> +static struct platform_driver rcar_cmm_platform_driver = {
> + .probe = rcar_cmm_probe,
> + .remove = rcar_cmm_remove,
> + .driver = {
> + .name = "rcar-cmm",
> + .of_match_table = rcar_cmm_of_table,
> + },
> +};
> +
> +module_platform_driver(rcar_cmm_platform_driver);
> +
> +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
> +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> new file mode 100644
> index 000000000000..15a2c874b6a6
> --- /dev/null
> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * rcar_cmm.h -- R-Car Display Unit Color Management Module
> + *
> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> + */
> +
> +#ifndef __RCAR_CMM_H__
> +#define __RCAR_CMM_H__
> +
> +#define CM2_LUT_SIZE 256
> +
> +struct drm_color_lut;
> +struct platform_device;
> +
> +/**
> + * struct rcar_cmm_config - CMM configuration
> + *
> + * @lut: 1D-LUT configuration
> + * @lut.enable: 1D-LUT enable flag
> + * @lut.table: 1D-LUT table entries. Might be set to NULL when the CMM has to
> + * be re-enabled but not re=programmed.

s/re=programmed/re-programmed/

As discussed offline this can't really happen as far as I can tell.
However, it will still be useful when we'll add CLU support, as then a
CLU reprogramming without a LUT reprogramming could happen.

I think we should make the documentation a bit clearer:

"1D-LUT table entries. Only valid when lut.enable is true, shall be NULL
otherwise. When non-NULL, the LUT table will be programmed with the new
values. Otherwise the LUT table will retain its previously programmed
values."

This being said, the code in rcar_cmm_setup() will crash if table is
NULL. I would either drop the option of table being NULL (and thus
update the documentation here) if you don't need this yet in the DU
driver, or fix rcar_cmm_setup(). You've posted enough versions of this
series in my opinion, so please pick the easiest option, and we'll
rework the code when adding CLU support anyway.

With those small issues fixes,

Reviewed-by: Laurent Pinchart <[email protected]>

> + */
> +struct rcar_cmm_config {
> + struct {
> + bool enable;
> + struct drm_color_lut *table;
> + } lut;
> +};
> +
> +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
> +int rcar_cmm_init(struct platform_device *pdev);
> +
> +int rcar_cmm_enable(struct platform_device *pdev);
> +void rcar_cmm_disable(struct platform_device *pdev);
> +
> +int rcar_cmm_setup(struct platform_device *pdev,
> + const struct rcar_cmm_config *config);
> +#else
> +static inline int rcar_cmm_init(struct platform_device *pdev)
> +{
> + return 0;
> +}
> +
> +static inline int rcar_cmm_enable(struct platform_device *pdev)
> +{
> + return 0;
> +}
> +
> +static inline void rcar_cmm_disable(struct platform_device *pdev)
> +{
> +}
> +
> +static int rcar_cmm_setup(struct platform_device *pdev,
> + const struct rcar_cmm_config *config)
> +{
> + return 0;
> +}
> +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
> +
> +#endif /* __RCAR_CMM_H__ */

--
Regards,

Laurent Pinchart

2019-09-19 00:13:55

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 6/9] drm: rcar-du: crtc: Enable and disable CMMs

Hello,

On Thu, Sep 12, 2019 at 10:19:30AM +0100, Kieran Bingham wrote:
> On 12/09/2019 09:07, Jacopo Mondi wrote:
> > On Wed, Sep 11, 2019 at 07:40:27PM +0100, Kieran Bingham wrote:
> >> On 06/09/2019 14:54, Jacopo Mondi wrote:
> >>> Enable/disable the CMM associated with a CRTC at CRTC start and stop
> >>> time and enable the CMM unit through the Display Extensional Functions
> >>> register at group setup time.
> >>>
> >>> Reviewed-by: Ulrich Hecht <[email protected]>
> >>> Reviewed-by: Laurent Pinchart <[email protected]>
> >>> Signed-off-by: Jacopo Mondi <[email protected]>
> >>> ---
> >>> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++
> >>> drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++++++
> >>> drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +++++
> >>> 3 files changed, 20 insertions(+)
> >>>
> >>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> >>> index 23f1d6cc1719..3dac605c3a67 100644
> >>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> >>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> >>> @@ -21,6 +21,7 @@
> >>> #include <drm/drm_plane_helper.h>
> >>> #include <drm/drm_vblank.h>
> >>>
> >>> +#include "rcar_cmm.h"
> >>> #include "rcar_du_crtc.h"
> >>> #include "rcar_du_drv.h"
> >>> #include "rcar_du_encoder.h"
> >>> @@ -619,6 +620,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
> >>> if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
> >>> rcar_du_vsp_disable(rcrtc);
> >>>
> >>> + if (rcrtc->cmm)
> >>> + rcar_cmm_disable(rcrtc->cmm);
> >>> +
> >>> /*
> >>> * Select switch sync mode. This stops display operation and configures
> >>> * the HSYNC and VSYNC signals as inputs.
> >>> @@ -686,6 +690,9 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
> >>> }
> >>>
> >>> rcar_du_crtc_start(rcrtc);
> >>> +
> >>> + if (rcrtc->cmm)
> >>> + rcar_cmm_enable(rcrtc->cmm);
> >>> }
> >>>
> >>> static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
> >>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> >>> index 9eee47969e77..25d0fc125d7a 100644
> >>> --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
> >>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> >>> @@ -147,6 +147,14 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
> >>>
> >>> rcar_du_group_setup_pins(rgrp);
> >>>
> >>> + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
> >>> + u32 defr7 = DEFR7_CODE
> >>> + | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
> >>> + | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
> >>> +
> >>> + rcar_du_group_write(rgrp, DEFR7, defr7);
> >>> + }
> >>> +
> >>
> >> What's the effect here on platforms with a CMM, but with
> >> CONFIG_DRM_RCAR_CMM unset?
> >>
> >> Will this incorrectly configure the DU ?
> >>
> >> Will it stall the display if the DU tries to interact with another
> >> module which is not enabled?
> >
> > I recall I tested that (that's why I had to add stubs for CMM
> > functions, as I had linkage errors otherwise) and thing seems to be
> > fine as the CMM configuration/enblement resolve to an empty function.
>
> Yes, I see the stubs to allow for linkage, but it's the hardware I'm
> concerned about. If it passes the tests and doesn't break then that's
> probably ok ... but I'm really weary that we're enabling a hardware
> pipeline with a disabled component in the middle.
>
> > Would you prefer to have this guarded by an #if IS_ENABLED() ?
>
> I don't think we need a compile time conditional, but I'd say it
> probably needs to be more about whether the CMM has actually probed or not
>
> Aha, and I see that in rcar_du_cmm_init() we already do a
> call to rcar_cmm_init(), which if fails will leave rcdu->cmms[i] as
> NULL. So that's catered for, which results in the rgrp->cmms_mask being
> correctly representative of whether there is a CMM connected or not.

Doesn't this result in probe failure ?

> ... so I think that means the ...
> "if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM))" is somewhat redundant:
>
>
> This could be:
>
> if (rgrp->cmms_mask) {
> u32 defr7 = DEFR7_CODE
> | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
> | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
>
> rcar_du_group_write(rgrp, DEFR7, defr7);
>
> Or in fact, if we don't mind writing 0 to DEFR7 when there is no CMM
> (which is safe by the looks of things as DEFR7 is available on all
> platforms), then we can even remove the outer conditional, and leave
> this all up to the ternary operators to write the correct value to the
> defr7.
>
> Phew ... net result - your current code *is* safe with the
> CONFIG_DRM_RCAR_CMM option disabled. I'll leave it up to you if you want
> to simplify the code here and remove the RCAR_DU_FEATURE_CMM.
>
> As this RCAR_DU_FEATURE_CMM flag is only checked here, removing it would
> however simplify all of the rcar_du_device_info structures.
>
> So - with or without the _FEATURE_CMM" simplification, this patch looks
> functional and safe so:
>
>
> Reviewed-by: Kieran Bingham <[email protected]>
>
> >>> if (rcdu->info->gen >= 2) {
> >>> rcar_du_group_setup_defr8(rgrp);
> >>> rcar_du_group_setup_didsr(rgrp);
> >>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> >>> index bc87f080b170..fb9964949368 100644
> >>> --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> >>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> >>> @@ -197,6 +197,11 @@
> >>> #define DEFR6_MLOS1 (1 << 2)
> >>> #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)
> >>>
> >>> +#define DEFR7 0x000ec
> >>> +#define DEFR7_CODE (0x7779 << 16)
> >>> +#define DEFR7_CMME1 BIT(6)
> >>> +#define DEFR7_CMME0 BIT(4)
> >>> +
> >>> /* -----------------------------------------------------------------------------
> >>> * R8A7790-only Control Registers
> >>> */

--
Regards,

Laurent Pinchart

2019-09-19 08:12:45

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 6/9] drm: rcar-du: crtc: Enable and disable CMMs

Hi Laurent, / Jacopo

On 19/09/2019 00:23, Laurent Pinchart wrote:
> Hello,
>
> On Thu, Sep 12, 2019 at 10:19:30AM +0100, Kieran Bingham wrote:
>> On 12/09/2019 09:07, Jacopo Mondi wrote:
>>> On Wed, Sep 11, 2019 at 07:40:27PM +0100, Kieran Bingham wrote:
>>>> On 06/09/2019 14:54, Jacopo Mondi wrote:
>>>>> Enable/disable the CMM associated with a CRTC at CRTC start and stop
>>>>> time and enable the CMM unit through the Display Extensional Functions
>>>>> register at group setup time.
>>>>>
>>>>> Reviewed-by: Ulrich Hecht <[email protected]>
>>>>> Reviewed-by: Laurent Pinchart <[email protected]>
>>>>> Signed-off-by: Jacopo Mondi <[email protected]>
>>>>> ---
>>>>> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++
>>>>> drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++++++
>>>>> drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +++++
>>>>> 3 files changed, 20 insertions(+)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
>>>>> index 23f1d6cc1719..3dac605c3a67 100644
>>>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
>>>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
>>>>> @@ -21,6 +21,7 @@
>>>>> #include <drm/drm_plane_helper.h>
>>>>> #include <drm/drm_vblank.h>
>>>>>
>>>>> +#include "rcar_cmm.h"
>>>>> #include "rcar_du_crtc.h"
>>>>> #include "rcar_du_drv.h"
>>>>> #include "rcar_du_encoder.h"
>>>>> @@ -619,6 +620,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
>>>>> if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
>>>>> rcar_du_vsp_disable(rcrtc);
>>>>>
>>>>> + if (rcrtc->cmm)
>>>>> + rcar_cmm_disable(rcrtc->cmm);
>>>>> +
>>>>> /*
>>>>> * Select switch sync mode. This stops display operation and configures
>>>>> * the HSYNC and VSYNC signals as inputs.
>>>>> @@ -686,6 +690,9 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
>>>>> }
>>>>>
>>>>> rcar_du_crtc_start(rcrtc);
>>>>> +
>>>>> + if (rcrtc->cmm)
>>>>> + rcar_cmm_enable(rcrtc->cmm);
>>>>> }
>>>>>
>>>>> static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
>>>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
>>>>> index 9eee47969e77..25d0fc125d7a 100644
>>>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
>>>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
>>>>> @@ -147,6 +147,14 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
>>>>>
>>>>> rcar_du_group_setup_pins(rgrp);
>>>>>
>>>>> + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
>>>>> + u32 defr7 = DEFR7_CODE
>>>>> + | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
>>>>> + | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
>>>>> +
>>>>> + rcar_du_group_write(rgrp, DEFR7, defr7);
>>>>> + }
>>>>> +
>>>>
>>>> What's the effect here on platforms with a CMM, but with
>>>> CONFIG_DRM_RCAR_CMM unset?
>>>>
>>>> Will this incorrectly configure the DU ?
>>>>
>>>> Will it stall the display if the DU tries to interact with another
>>>> module which is not enabled?
>>>
>>> I recall I tested that (that's why I had to add stubs for CMM
>>> functions, as I had linkage errors otherwise) and thing seems to be
>>> fine as the CMM configuration/enblement resolve to an empty function.
>>
>> Yes, I see the stubs to allow for linkage, but it's the hardware I'm
>> concerned about. If it passes the tests and doesn't break then that's
>> probably ok ... but I'm really weary that we're enabling a hardware
>> pipeline with a disabled component in the middle.
>>
>>> Would you prefer to have this guarded by an #if IS_ENABLED() ?
>>
>> I don't think we need a compile time conditional, but I'd say it
>> probably needs to be more about whether the CMM has actually probed or not
>>
>> Aha, and I see that in rcar_du_cmm_init() we already do a
>> call to rcar_cmm_init(), which if fails will leave rcdu->cmms[i] as
>> NULL. So that's catered for, which results in the rgrp->cmms_mask being
>> correctly representative of whether there is a CMM connected or not.
>
> Doesn't this result in probe failure ?

I think I mis-spoke above, I didn't mean "if rcar_cmm_init() fails" I
meant "if rcar_du_cmm_init() determines there are no connected CMM's or
if they are disabled."


If rcar_cmm_init() returns a failure, then yes we will fail to probe.

But I think it's up to rcar_du_cmm_init() to determine if the CMM exists
or not (or is enabled) and if that's not a failure case then it should
not prevent the probing of the DU.


In fact, I've now seen that if CONFIG_DRM_RCAR_CMM is not enabled,
rcar_cmm_init() returns 0, and I think in fact it should return -ENODEV,
with an exception on that return value in rcar_du_cmm_init() so that the
DU continues with no CMM attached there.


>
>> ... so I think that means the ...
>> "if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM))" is somewhat redundant:
>>
>>
>> This could be:
>>
>> if (rgrp->cmms_mask) {
>> u32 defr7 = DEFR7_CODE
>> | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
>> | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
>>
>> rcar_du_group_write(rgrp, DEFR7, defr7);
>>
>> Or in fact, if we don't mind writing 0 to DEFR7 when there is no CMM
>> (which is safe by the looks of things as DEFR7 is available on all
>> platforms), then we can even remove the outer conditional, and leave
>> this all up to the ternary operators to write the correct value to the
>> defr7.
>>
>> Phew ... net result - your current code *is* safe with the
>> CONFIG_DRM_RCAR_CMM option disabled. I'll leave it up to you if you want
>> to simplify the code here and remove the RCAR_DU_FEATURE_CMM.
>>
>> As this RCAR_DU_FEATURE_CMM flag is only checked here, removing it would
>> however simplify all of the rcar_du_device_info structures.
>>
>> So - with or without the _FEATURE_CMM" simplification, this patch looks
>> functional and safe so:
>>
>>
>> Reviewed-by: Kieran Bingham <[email protected]>
>>
>>>>> if (rcdu->info->gen >= 2) {
>>>>> rcar_du_group_setup_defr8(rgrp);
>>>>> rcar_du_group_setup_didsr(rgrp);
>>>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
>>>>> index bc87f080b170..fb9964949368 100644
>>>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
>>>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
>>>>> @@ -197,6 +197,11 @@
>>>>> #define DEFR6_MLOS1 (1 << 2)
>>>>> #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)
>>>>>
>>>>> +#define DEFR7 0x000ec
>>>>> +#define DEFR7_CODE (0x7779 << 16)
>>>>> +#define DEFR7_CMME1 BIT(6)
>>>>> +#define DEFR7_CMME0 BIT(4)
>>>>> +
>>>>> /* -----------------------------------------------------------------------------
>>>>> * R8A7790-only Control Registers
>>>>> */
>

2019-09-19 09:04:53

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Jacopo, Laurent,


On 18/09/2019 23:55, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Fri, Sep 06, 2019 at 03:54:30PM +0200, Jacopo Mondi wrote:
>> Add a driver for the R-Car Display Unit Color Correction Module.
>>
>> In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
>> to perform image enhancement and color correction.
>>
>> Add support for CMM through a driver that supports configuration of
>> the 1-dimensional LUT table. More advanced CMM feature will be
>> implemented on top of this basic one.
>>
>> Signed-off-by: Jacopo Mondi <[email protected]>
>> ---
>> drivers/gpu/drm/rcar-du/Kconfig | 7 +
>> drivers/gpu/drm/rcar-du/Makefile | 1 +
>> drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
>> drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
>> 4 files changed, 320 insertions(+)
>> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
>> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
>>
>> diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
>> index 1529849e217e..539d232790d1 100644
>> --- a/drivers/gpu/drm/rcar-du/Kconfig
>> +++ b/drivers/gpu/drm/rcar-du/Kconfig
>> @@ -13,6 +13,13 @@ config DRM_RCAR_DU
>> Choose this option if you have an R-Car chipset.
>> If M is selected the module will be called rcar-du-drm.
>>
>> +config DRM_RCAR_CMM
>> + bool "R-Car DU Color Management Module (CMM) Support"
>> + depends on DRM && OF
>> + depends on DRM_RCAR_DU
>> + help
>> + Enable support for R-Car Color Management Module (CMM).
>> +
>> config DRM_RCAR_DW_HDMI
>> tristate "R-Car DU Gen3 HDMI Encoder Support"
>> depends on DRM && OF
>> diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
>> index 6c2ed9c46467..4d1187ccc3e5 100644
>> --- a/drivers/gpu/drm/rcar-du/Makefile
>> +++ b/drivers/gpu/drm/rcar-du/Makefile
>> @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
>> rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
>> rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
>>
>> +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
>> obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
>> obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
>> obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
>> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c b/drivers/gpu/drm/rcar-du/rcar_cmm.c
>> new file mode 100644
>> index 000000000000..3cacdc4474c7
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
>> @@ -0,0 +1,251 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * rcar_cmm.c -- R-Car Display Unit Color Management Module
>> + *
>> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include <drm/drm_color_mgmt.h>
>> +
>> +#include "rcar_cmm.h"
>> +
>> +#define CM2_LUT_CTRL 0x0000
>> +#define CM2_LUT_CTRL_LUT_EN BIT(0)
>> +#define CM2_LUT_TBL_BASE 0x0600
>> +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
>> +
>> +struct rcar_cmm {
>> + void __iomem *base;
>> + bool enabled;
>> +
>> + /*
>> + * @lut: 1D-LUT status
>> + * @lut.enabled: 1D-LUT enabled flag
>> + * @lut.table: Table of 1D-LUT entries scaled to hardware
>> + * precision (8-bits per color component)
>> + */
>> + struct {
>> + bool enabled;
>> + u32 table[CM2_LUT_SIZE];
>> + } lut;
>> +};
>> +
>> +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
>> +{
>> + return ioread32(rcmm->base + reg);
>> +}
>> +
>> +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
>> +{
>> + iowrite32(data, rcmm->base + reg);
>> +}
>> +
>> +/*
>> + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM LUT table
>> + * entries and store them.
>
> "Scale the DRM LUT table entries to hardware precision and store them."
>
>> + * @rcmm: Pointer to the CMM device
>> + * @drm_lut: Pointer to the DRM LUT table
>> + */
>> +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
>> + const struct drm_color_lut *drm_lut)
>> +{
>> + unsigned int i;
>> +
>> + for (i = 0; i < CM2_LUT_SIZE; ++i) {
>> + const struct drm_color_lut *lut = &drm_lut[i];
>> +
>> + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
>> + | drm_color_lut_extract(lut->green, 8) << 8
>> + | drm_color_lut_extract(lut->blue, 8);
>> + }
>> +}
>> +
>> +/*
>> + * rcar_cmm_lut_write() - Write to hardware the LUT table entries from the
>> + * local table.
>
> "Write the LUT table entries from the local table to the hardware."
>
>> + * @rcmm: Pointer to the CMM device
>> + */
>> +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
>> +{
>> + unsigned int i;
>> +
>> + for (i = 0; i < CM2_LUT_SIZE; ++i)
>> + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
>> +}
>> +
>> +/*
>> + * rcar_cmm_setup() - Configure the CMM unit.
>> + * @pdev: The platform device associated with the CMM instance
>> + * @config: The CRTC-provided configuration.
>> + *
>> + * Configure the CMM unit with the CRTC-provided configuration.
>> + * Currently enabling, disabling and programming of the 1-D LUT unit is
>> + * supported.
>> + */
>> +int rcar_cmm_setup(struct platform_device *pdev,
>> + const struct rcar_cmm_config *config)
>> +{
>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>> +
>> + /*
>> + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
>> + * be called when the CMM is disabled. As we can't program the hardware
>> + * in that case, store the configuration internally and apply it when
>> + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
>> + */
>> + if (!rcmm->enabled) {
>> + if (!config->lut.enable)
>> + return 0;
>> +
>> + rcar_cmm_lut_extract(rcmm, config->lut.table);
>> + rcmm->lut.enabled = true;
>> +
>> + return 0;
>> + }
>> +
>> + /* Stop LUT operations if requested. */
>> + if (!config->lut.enable) {
>> + if (rcmm->lut.enabled) {
>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
>> + rcmm->lut.enabled = false;
>> + }
>> +
>> + return 0;
>> + }
>> +
>> + /*
>> + * Enable LUT and program the new gamma table values.
>> + *
>> + * FIXME: In order to have stable operations it is required to first
>> + * enable the 1D-LUT and then program its table entries. This seems to
>> + * contradict what the chip manual reports, and will have to be
>> + * reconsidered when implementing support for double buffering.
>> + */
>> + if (!rcmm->lut.enabled) {
>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
>> + rcmm->lut.enabled = true;
>> + }
>> +
>> + rcar_cmm_lut_extract(rcmm, config->lut.table);
>> + rcar_cmm_lut_write(rcmm);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
>> +
>> +/*
>> + * rcar_cmm_enable() - Enable the CMM unit.
>> + * @pdev: The platform device associated with the CMM instance
>> + *
>> + * Enable the CMM unit by enabling the parent clock and enabling the CMM
>> + * components, such as 1-D LUT, if requested.
>> + */
>> +int rcar_cmm_enable(struct platform_device *pdev)
>> +{
>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>> + int ret;
>> +
>> + ret = pm_runtime_get_sync(&pdev->dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
>> + if (rcmm->lut.enabled) {
>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
>> + rcar_cmm_lut_write(rcmm);
>> + }
>> +
>> + rcmm->enabled = true;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
>> +
>> +/*
>> + * rcar_cmm_disable() - Disable the CMM unit.
>> + * @pdev: The platform device associated with the CMM instance
>> + *
>> + * Disable the CMM unit by stopping the parent clock.
>> + */
>> +void rcar_cmm_disable(struct platform_device *pdev)
>> +{
>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>> +
>> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
>> +
>> + pm_runtime_put(&pdev->dev);
>> +
>> + rcmm->lut.enabled = false;
>> + rcmm->enabled = false;
>> +}
>> +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
>> +
>> +/*
>> + * rcar_cmm_init() - Make sure the CMM has probed.
>
> I would document this as "Intialize the CMM" to match the function name.
> We may add more initialization in the future.
>
>> + * @pdev: The platform device associated with the CMM instance
>> + *
>> + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
>
> 0 on success, -EPROBE_DEFER is the CMM isn't availablet yet

And possibly -ENODEV if the suggestion below in the header is worthy, to
return no device if the CMM is disabled at the CONFIG_ level.



>
>> + */
>> +int rcar_cmm_init(struct platform_device *pdev)
>> +{
>> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
>> +
>> + if (!rcmm)
>> + return -EPROBE_DEFER;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(rcar_cmm_init);
>> +
>> +static int rcar_cmm_probe(struct platform_device *pdev)
>> +{
>> + struct rcar_cmm *rcmm;
>> +
>> + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
>> + if (!rcmm)
>> + return -ENOMEM;
>> + platform_set_drvdata(pdev, rcmm);
>> +
>> + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
>> + if (IS_ERR(rcmm->base))
>> + return PTR_ERR(rcmm->base);
>> +
>> + pm_runtime_enable(&pdev->dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int rcar_cmm_remove(struct platform_device *pdev)
>> +{
>> + pm_runtime_disable(&pdev->dev);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id rcar_cmm_of_table[] = {
>> + { .compatible = "renesas,rcar-gen3-cmm", },
>> + { .compatible = "renesas,rcar-gen2-cmm", },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
>> +
>> +static struct platform_driver rcar_cmm_platform_driver = {
>> + .probe = rcar_cmm_probe,
>> + .remove = rcar_cmm_remove,
>> + .driver = {
>> + .name = "rcar-cmm",
>> + .of_match_table = rcar_cmm_of_table,
>> + },
>> +};
>> +
>> +module_platform_driver(rcar_cmm_platform_driver);
>> +
>> +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
>> +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h b/drivers/gpu/drm/rcar-du/rcar_cmm.h
>> new file mode 100644
>> index 000000000000..15a2c874b6a6
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
>> @@ -0,0 +1,61 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * rcar_cmm.h -- R-Car Display Unit Color Management Module
>> + *
>> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
>> + */
>> +
>> +#ifndef __RCAR_CMM_H__
>> +#define __RCAR_CMM_H__
>> +
>> +#define CM2_LUT_SIZE 256
>> +
>> +struct drm_color_lut;
>> +struct platform_device;
>> +
>> +/**
>> + * struct rcar_cmm_config - CMM configuration
>> + *
>> + * @lut: 1D-LUT configuration
>> + * @lut.enable: 1D-LUT enable flag
>> + * @lut.table: 1D-LUT table entries. Might be set to NULL when the CMM has to
>> + * be re-enabled but not re=programmed.
>
> s/re=programmed/re-programmed/
>
> As discussed offline this can't really happen as far as I can tell.
> However, it will still be useful when we'll add CLU support, as then a
> CLU reprogramming without a LUT reprogramming could happen.
>
> I think we should make the documentation a bit clearer:
>
> "1D-LUT table entries. Only valid when lut.enable is true, shall be NULL
> otherwise. When non-NULL, the LUT table will be programmed with the new
> values. Otherwise the LUT table will retain its previously programmed
> values."
>
> This being said, the code in rcar_cmm_setup() will crash if table is
> NULL. I would either drop the option of table being NULL (and thus
> update the documentation here) if you don't need this yet in the DU
> driver, or fix rcar_cmm_setup(). You've posted enough versions of this
> series in my opinion, so please pick the easiest option, and we'll
> rework the code when adding CLU support anyway.
>
> With those small issues fixes,
>
> Reviewed-by: Laurent Pinchart <[email protected]>
>
>> + */
>> +struct rcar_cmm_config {
>> + struct {
>> + bool enable;
>> + struct drm_color_lut *table;
>> + } lut;
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
>> +int rcar_cmm_init(struct platform_device *pdev);
>> +
>> +int rcar_cmm_enable(struct platform_device *pdev);
>> +void rcar_cmm_disable(struct platform_device *pdev);
>> +
>> +int rcar_cmm_setup(struct platform_device *pdev,
>> + const struct rcar_cmm_config *config);
>> +#else
>> +static inline int rcar_cmm_init(struct platform_device *pdev)
>> +{
>> + return 0;

I feel like here, this should return -ENODEV if CMM is disabled, and
allow rcar_du_cmm_init() to succeed, but leave the rcdu->cmms[i]
unpopulated. ...

But this could be updated later too if it's awkward ...

--
Kieran


>> +}
>> +
>> +static inline int rcar_cmm_enable(struct platform_device *pdev)
>> +{
>> + return 0;
>> +}
>> +
>> +static inline void rcar_cmm_disable(struct platform_device *pdev)
>> +{
>> +}
>> +
>> +static int rcar_cmm_setup(struct platform_device *pdev,
>> + const struct rcar_cmm_config *config)
>> +{
>> + return 0;
>> +}
>> +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
>> +
>> +#endif /* __RCAR_CMM_H__ */
>

2019-09-23 12:30:53

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 8/9] drm: rcar-du: kms: Update CMM in atomic commit tail

Hi Daniel,

On Fri, Sep 06, 2019 at 03:54:35PM +0200, Jacopo Mondi wrote:
> Update CMM settings at in the atomic commit tail helper method.
> The CMM is updated with new gamma values provided to the driver
> in the GAMMA_LUT blob property.
>
> When resuming from system suspend, the DU driver is responsible for
> reprogramming and enabling the CMM unit if it was in use at the time the
> system entered the suspend state. Force the color_mgmt_changed flag to
> true if the DRM gamma lut color transformation property was set in the
> CRTC state duplicated at suspend time, as the CMM gets reprogrammed only
> if said flag is active in the rcar_du_atomic_commit_update_cmm() method.
>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Jacopo Mondi <[email protected]>
> ---
>
> Daniel could you have a look if resume bits are worth being moved to the
> DRM core? The color_mgmt_changed flag is set to false when the state is
> duplicated if I read the code correctly, but when this happens in a
> suspend/resume sequence its value should probably be restored to true if
> any color management property was set in the crtc state when system entered
> suspend.

Gentle ping on this.

> ---
>
> drivers/gpu/drm/rcar-du/rcar_du_drv.c | 20 ++++++++++++++
> drivers/gpu/drm/rcar-du/rcar_du_kms.c | 38 +++++++++++++++++++++++++++
> 2 files changed, 58 insertions(+)
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> index 018480a8f35c..d1003d31cfaf 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> @@ -17,6 +17,7 @@
> #include <linux/slab.h>
> #include <linux/wait.h>
>
> +#include <drm/drm_atomic.h>
> #include <drm/drm_atomic_helper.h>
> #include <drm/drm_fb_cma_helper.h>
> #include <drm/drm_fb_helper.h>
> @@ -482,6 +483,25 @@ static int rcar_du_pm_suspend(struct device *dev)
> static int rcar_du_pm_resume(struct device *dev)
> {
> struct rcar_du_device *rcdu = dev_get_drvdata(dev);
> + struct drm_atomic_state *state = rcdu->ddev->mode_config.suspend_state;
> + unsigned int i;
> +
> + for (i = 0; i < rcdu->num_crtcs; ++i) {
> + struct drm_crtc *crtc = &rcdu->crtcs[i].crtc;
> + struct drm_crtc_state *crtc_state;
> +
> + crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> + if (!crtc_state)
> + continue;
> +
> + /*
> + * Force re-enablement of CMM after system resume if any
> + * of the DRM color transformation properties was set in
> + * the state saved at system suspend time.
> + */
> + if (crtc_state->gamma_lut)
> + crtc_state->color_mgmt_changed = true;
> + }
>
> return drm_mode_config_helper_resume(rcdu->ddev);
> }
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> index 294630e56992..fc30fff0eb8d 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> @@ -22,6 +22,7 @@
> #include <linux/of_platform.h>
> #include <linux/wait.h>
>
> +#include "rcar_cmm.h"
> #include "rcar_du_crtc.h"
> #include "rcar_du_drv.h"
> #include "rcar_du_encoder.h"
> @@ -368,6 +369,40 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
> * Atomic Check and Update
> */
>
> +static void rcar_du_atomic_commit_update_cmm(struct drm_crtc *crtc,
> + struct drm_crtc_state *old_state)
> +{
> + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
> + struct rcar_cmm_config cmm_config = {};
> + struct device *dev = rcrtc->dev->dev;
> + struct drm_property_blob *lut_blob;
> +
> + if (!rcrtc->cmm || !crtc->state->color_mgmt_changed)
> + return;
> +
> + if (!crtc->state->gamma_lut) {
> + cmm_config.lut.enable = false;
> + rcar_cmm_setup(rcrtc->cmm, &cmm_config);
> + return;
> + }
> +
> + lut_blob = crtc->state->gamma_lut;
> + if (lut_blob->length != (CM2_LUT_SIZE * sizeof(struct drm_color_lut))) {
> + /*
> + * We only accept fully populated LUT tables;
> + * be loud here, otherwise the CMM gets silently ignored.
> + */
> + dev_err(dev, "invalid gamma lut size of %lu bytes\n",
> + lut_blob->length);
> + return;
> + }
> +
> + cmm_config.lut.enable = true;
> + cmm_config.lut.table = (struct drm_color_lut *)lut_blob->data;
> +
> + rcar_cmm_setup(rcrtc->cmm, &cmm_config);
> +}
> +
> static int rcar_du_atomic_check(struct drm_device *dev,
> struct drm_atomic_state *state)
> {
> @@ -410,6 +445,9 @@ static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state)
> rcdu->dpad1_source = rcrtc->index;
> }
>
> + for_each_old_crtc_in_state(old_state, crtc, crtc_state, i)
> + rcar_du_atomic_commit_update_cmm(crtc, crtc_state);
> +
> /* Apply the atomic update. */
> drm_atomic_helper_commit_modeset_disables(dev, old_state);
> drm_atomic_helper_commit_planes(dev, old_state,

--
Regards,

Laurent Pinchart

2019-09-23 13:21:21

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 6/9] drm: rcar-du: crtc: Enable and disable CMMs

Hi Kieran,

On Thu, Sep 19, 2019 at 09:08:18AM +0100, Kieran Bingham wrote:
> On 19/09/2019 00:23, Laurent Pinchart wrote:
> > On Thu, Sep 12, 2019 at 10:19:30AM +0100, Kieran Bingham wrote:
> >> On 12/09/2019 09:07, Jacopo Mondi wrote:
> >>> On Wed, Sep 11, 2019 at 07:40:27PM +0100, Kieran Bingham wrote:
> >>>> On 06/09/2019 14:54, Jacopo Mondi wrote:
> >>>>> Enable/disable the CMM associated with a CRTC at CRTC start and stop
> >>>>> time and enable the CMM unit through the Display Extensional Functions
> >>>>> register at group setup time.
> >>>>>
> >>>>> Reviewed-by: Ulrich Hecht <[email protected]>
> >>>>> Reviewed-by: Laurent Pinchart <[email protected]>
> >>>>> Signed-off-by: Jacopo Mondi <[email protected]>
> >>>>> ---
> >>>>> drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 7 +++++++
> >>>>> drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++++++
> >>>>> drivers/gpu/drm/rcar-du/rcar_du_regs.h | 5 +++++
> >>>>> 3 files changed, 20 insertions(+)
> >>>>>
> >>>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> >>>>> index 23f1d6cc1719..3dac605c3a67 100644
> >>>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> >>>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> >>>>> @@ -21,6 +21,7 @@
> >>>>> #include <drm/drm_plane_helper.h>
> >>>>> #include <drm/drm_vblank.h>
> >>>>>
> >>>>> +#include "rcar_cmm.h"
> >>>>> #include "rcar_du_crtc.h"
> >>>>> #include "rcar_du_drv.h"
> >>>>> #include "rcar_du_encoder.h"
> >>>>> @@ -619,6 +620,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
> >>>>> if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
> >>>>> rcar_du_vsp_disable(rcrtc);
> >>>>>
> >>>>> + if (rcrtc->cmm)
> >>>>> + rcar_cmm_disable(rcrtc->cmm);
> >>>>> +
> >>>>> /*
> >>>>> * Select switch sync mode. This stops display operation and configures
> >>>>> * the HSYNC and VSYNC signals as inputs.
> >>>>> @@ -686,6 +690,9 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
> >>>>> }
> >>>>>
> >>>>> rcar_du_crtc_start(rcrtc);
> >>>>> +
> >>>>> + if (rcrtc->cmm)
> >>>>> + rcar_cmm_enable(rcrtc->cmm);
> >>>>> }
> >>>>>
> >>>>> static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
> >>>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> >>>>> index 9eee47969e77..25d0fc125d7a 100644
> >>>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
> >>>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
> >>>>> @@ -147,6 +147,14 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
> >>>>>
> >>>>> rcar_du_group_setup_pins(rgrp);
> >>>>>
> >>>>> + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
> >>>>> + u32 defr7 = DEFR7_CODE
> >>>>> + | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
> >>>>> + | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
> >>>>> +
> >>>>> + rcar_du_group_write(rgrp, DEFR7, defr7);
> >>>>> + }
> >>>>> +
> >>>>
> >>>> What's the effect here on platforms with a CMM, but with
> >>>> CONFIG_DRM_RCAR_CMM unset?
> >>>>
> >>>> Will this incorrectly configure the DU ?
> >>>>
> >>>> Will it stall the display if the DU tries to interact with another
> >>>> module which is not enabled?
> >>>
> >>> I recall I tested that (that's why I had to add stubs for CMM
> >>> functions, as I had linkage errors otherwise) and thing seems to be
> >>> fine as the CMM configuration/enblement resolve to an empty function.
> >>
> >> Yes, I see the stubs to allow for linkage, but it's the hardware I'm
> >> concerned about. If it passes the tests and doesn't break then that's
> >> probably ok ... but I'm really weary that we're enabling a hardware
> >> pipeline with a disabled component in the middle.
> >>
> >>> Would you prefer to have this guarded by an #if IS_ENABLED() ?
> >>
> >> I don't think we need a compile time conditional, but I'd say it
> >> probably needs to be more about whether the CMM has actually probed or not
> >>
> >> Aha, and I see that in rcar_du_cmm_init() we already do a
> >> call to rcar_cmm_init(), which if fails will leave rcdu->cmms[i] as
> >> NULL. So that's catered for, which results in the rgrp->cmms_mask being
> >> correctly representative of whether there is a CMM connected or not.
> >
> > Doesn't this result in probe failure ?
>
> I think I mis-spoke above, I didn't mean "if rcar_cmm_init() fails" I
> meant "if rcar_du_cmm_init() determines there are no connected CMM's or
> if they are disabled."
>
> If rcar_cmm_init() returns a failure, then yes we will fail to probe.
>
> But I think it's up to rcar_du_cmm_init() to determine if the CMM exists
> or not (or is enabled) and if that's not a failure case then it should
> not prevent the probing of the DU.
>
> In fact, I've now seen that if CONFIG_DRM_RCAR_CMM is not enabled,
> rcar_cmm_init() returns 0, and I think in fact it should return -ENODEV,
> with an exception on that return value in rcar_du_cmm_init() so that the
> DU continues with no CMM attached there.

I've replied to your other e-mail regarding this, and I agree with you.

> >> ... so I think that means the ...
> >> "if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM))" is somewhat redundant:
> >>
> >>
> >> This could be:
> >>
> >> if (rgrp->cmms_mask) {
> >> u32 defr7 = DEFR7_CODE
> >> | (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0)
> >> | (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
> >>
> >> rcar_du_group_write(rgrp, DEFR7, defr7);
> >>
> >> Or in fact, if we don't mind writing 0 to DEFR7 when there is no CMM
> >> (which is safe by the looks of things as DEFR7 is available on all
> >> platforms), then we can even remove the outer conditional, and leave
> >> this all up to the ternary operators to write the correct value to the
> >> defr7.
> >>
> >> Phew ... net result - your current code *is* safe with the
> >> CONFIG_DRM_RCAR_CMM option disabled. I'll leave it up to you if you want
> >> to simplify the code here and remove the RCAR_DU_FEATURE_CMM.
> >>
> >> As this RCAR_DU_FEATURE_CMM flag is only checked here, removing it would
> >> however simplify all of the rcar_du_device_info structures.
> >>
> >> So - with or without the _FEATURE_CMM" simplification, this patch looks
> >> functional and safe so:
> >>
> >>
> >> Reviewed-by: Kieran Bingham <[email protected]>
> >>
> >>>>> if (rcdu->info->gen >= 2) {
> >>>>> rcar_du_group_setup_defr8(rgrp);
> >>>>> rcar_du_group_setup_didsr(rgrp);
> >>>>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> >>>>> index bc87f080b170..fb9964949368 100644
> >>>>> --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> >>>>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
> >>>>> @@ -197,6 +197,11 @@
> >>>>> #define DEFR6_MLOS1 (1 << 2)
> >>>>> #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1)
> >>>>>
> >>>>> +#define DEFR7 0x000ec
> >>>>> +#define DEFR7_CODE (0x7779 << 16)
> >>>>> +#define DEFR7_CMME1 BIT(6)
> >>>>> +#define DEFR7_CMME0 BIT(4)
> >>>>> +
> >>>>> /* -----------------------------------------------------------------------------
> >>>>> * R8A7790-only Control Registers
> >>>>> */

--
Regards,

Laurent Pinchart

2019-09-23 13:21:22

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Kieran,

On Thu, Sep 19, 2019 at 09:59:39AM +0100, Kieran Bingham wrote:
> On 18/09/2019 23:55, Laurent Pinchart wrote:
> > On Fri, Sep 06, 2019 at 03:54:30PM +0200, Jacopo Mondi wrote:
> >> Add a driver for the R-Car Display Unit Color Correction Module.
> >>
> >> In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
> >> to perform image enhancement and color correction.
> >>
> >> Add support for CMM through a driver that supports configuration of
> >> the 1-dimensional LUT table. More advanced CMM feature will be
> >> implemented on top of this basic one.
> >>
> >> Signed-off-by: Jacopo Mondi <[email protected]>
> >> ---
> >> drivers/gpu/drm/rcar-du/Kconfig | 7 +
> >> drivers/gpu/drm/rcar-du/Makefile | 1 +
> >> drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
> >> drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
> >> 4 files changed, 320 insertions(+)
> >> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
> >> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
> >>
> >> diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
> >> index 1529849e217e..539d232790d1 100644
> >> --- a/drivers/gpu/drm/rcar-du/Kconfig
> >> +++ b/drivers/gpu/drm/rcar-du/Kconfig
> >> @@ -13,6 +13,13 @@ config DRM_RCAR_DU
> >> Choose this option if you have an R-Car chipset.
> >> If M is selected the module will be called rcar-du-drm.
> >>
> >> +config DRM_RCAR_CMM
> >> + bool "R-Car DU Color Management Module (CMM) Support"
> >> + depends on DRM && OF
> >> + depends on DRM_RCAR_DU
> >> + help
> >> + Enable support for R-Car Color Management Module (CMM).
> >> +
> >> config DRM_RCAR_DW_HDMI
> >> tristate "R-Car DU Gen3 HDMI Encoder Support"
> >> depends on DRM && OF
> >> diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
> >> index 6c2ed9c46467..4d1187ccc3e5 100644
> >> --- a/drivers/gpu/drm/rcar-du/Makefile
> >> +++ b/drivers/gpu/drm/rcar-du/Makefile
> >> @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
> >> rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
> >> rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
> >>
> >> +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
> >> obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
> >> obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
> >> obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
> >> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> >> new file mode 100644
> >> index 000000000000..3cacdc4474c7
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> >> @@ -0,0 +1,251 @@
> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/*
> >> + * rcar_cmm.c -- R-Car Display Unit Color Management Module
> >> + *
> >> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> >> + */
> >> +
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +
> >> +#include <drm/drm_color_mgmt.h>
> >> +
> >> +#include "rcar_cmm.h"
> >> +
> >> +#define CM2_LUT_CTRL 0x0000
> >> +#define CM2_LUT_CTRL_LUT_EN BIT(0)
> >> +#define CM2_LUT_TBL_BASE 0x0600
> >> +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
> >> +
> >> +struct rcar_cmm {
> >> + void __iomem *base;
> >> + bool enabled;
> >> +
> >> + /*
> >> + * @lut: 1D-LUT status
> >> + * @lut.enabled: 1D-LUT enabled flag
> >> + * @lut.table: Table of 1D-LUT entries scaled to hardware
> >> + * precision (8-bits per color component)
> >> + */
> >> + struct {
> >> + bool enabled;
> >> + u32 table[CM2_LUT_SIZE];
> >> + } lut;
> >> +};
> >> +
> >> +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
> >> +{
> >> + return ioread32(rcmm->base + reg);
> >> +}
> >> +
> >> +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
> >> +{
> >> + iowrite32(data, rcmm->base + reg);
> >> +}
> >> +
> >> +/*
> >> + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM LUT table
> >> + * entries and store them.
> >
> > "Scale the DRM LUT table entries to hardware precision and store them."
> >
> >> + * @rcmm: Pointer to the CMM device
> >> + * @drm_lut: Pointer to the DRM LUT table
> >> + */
> >> +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
> >> + const struct drm_color_lut *drm_lut)
> >> +{
> >> + unsigned int i;
> >> +
> >> + for (i = 0; i < CM2_LUT_SIZE; ++i) {
> >> + const struct drm_color_lut *lut = &drm_lut[i];
> >> +
> >> + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
> >> + | drm_color_lut_extract(lut->green, 8) << 8
> >> + | drm_color_lut_extract(lut->blue, 8);
> >> + }
> >> +}
> >> +
> >> +/*
> >> + * rcar_cmm_lut_write() - Write to hardware the LUT table entries from the
> >> + * local table.
> >
> > "Write the LUT table entries from the local table to the hardware."
> >
> >> + * @rcmm: Pointer to the CMM device
> >> + */
> >> +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
> >> +{
> >> + unsigned int i;
> >> +
> >> + for (i = 0; i < CM2_LUT_SIZE; ++i)
> >> + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
> >> +}
> >> +
> >> +/*
> >> + * rcar_cmm_setup() - Configure the CMM unit.
> >> + * @pdev: The platform device associated with the CMM instance
> >> + * @config: The CRTC-provided configuration.
> >> + *
> >> + * Configure the CMM unit with the CRTC-provided configuration.
> >> + * Currently enabling, disabling and programming of the 1-D LUT unit is
> >> + * supported.
> >> + */
> >> +int rcar_cmm_setup(struct platform_device *pdev,
> >> + const struct rcar_cmm_config *config)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> +
> >> + /*
> >> + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
> >> + * be called when the CMM is disabled. As we can't program the hardware
> >> + * in that case, store the configuration internally and apply it when
> >> + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
> >> + */
> >> + if (!rcmm->enabled) {
> >> + if (!config->lut.enable)
> >> + return 0;
> >> +
> >> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> >> + rcmm->lut.enabled = true;
> >> +
> >> + return 0;
> >> + }
> >> +
> >> + /* Stop LUT operations if requested. */
> >> + if (!config->lut.enable) {
> >> + if (rcmm->lut.enabled) {
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> >> + rcmm->lut.enabled = false;
> >> + }
> >> +
> >> + return 0;
> >> + }
> >> +
> >> + /*
> >> + * Enable LUT and program the new gamma table values.
> >> + *
> >> + * FIXME: In order to have stable operations it is required to first
> >> + * enable the 1D-LUT and then program its table entries. This seems to
> >> + * contradict what the chip manual reports, and will have to be
> >> + * reconsidered when implementing support for double buffering.
> >> + */
> >> + if (!rcmm->lut.enabled) {
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> >> + rcmm->lut.enabled = true;
> >> + }
> >> +
> >> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> >> + rcar_cmm_lut_write(rcmm);
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
> >> +
> >> +/*
> >> + * rcar_cmm_enable() - Enable the CMM unit.
> >> + * @pdev: The platform device associated with the CMM instance
> >> + *
> >> + * Enable the CMM unit by enabling the parent clock and enabling the CMM
> >> + * components, such as 1-D LUT, if requested.
> >> + */
> >> +int rcar_cmm_enable(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> + int ret;
> >> +
> >> + ret = pm_runtime_get_sync(&pdev->dev);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
> >> + if (rcmm->lut.enabled) {
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> >> + rcar_cmm_lut_write(rcmm);
> >> + }
> >> +
> >> + rcmm->enabled = true;
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
> >> +
> >> +/*
> >> + * rcar_cmm_disable() - Disable the CMM unit.
> >> + * @pdev: The platform device associated with the CMM instance
> >> + *
> >> + * Disable the CMM unit by stopping the parent clock.
> >> + */
> >> +void rcar_cmm_disable(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> +
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> >> +
> >> + pm_runtime_put(&pdev->dev);
> >> +
> >> + rcmm->lut.enabled = false;
> >> + rcmm->enabled = false;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
> >> +
> >> +/*
> >> + * rcar_cmm_init() - Make sure the CMM has probed.
> >
> > I would document this as "Intialize the CMM" to match the function name.
> > We may add more initialization in the future.
> >
> >> + * @pdev: The platform device associated with the CMM instance
> >> + *
> >> + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
> >
> > 0 on success, -EPROBE_DEFER is the CMM isn't availablet yet
>
> And possibly -ENODEV if the suggestion below in the header is worthy, to
> return no device if the CMM is disabled at the CONFIG_ level.
>
> >> + */
> >> +int rcar_cmm_init(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> +
> >> + if (!rcmm)
> >> + return -EPROBE_DEFER;
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_init);
> >> +
> >> +static int rcar_cmm_probe(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm;
> >> +
> >> + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
> >> + if (!rcmm)
> >> + return -ENOMEM;
> >> + platform_set_drvdata(pdev, rcmm);
> >> +
> >> + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
> >> + if (IS_ERR(rcmm->base))
> >> + return PTR_ERR(rcmm->base);
> >> +
> >> + pm_runtime_enable(&pdev->dev);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int rcar_cmm_remove(struct platform_device *pdev)
> >> +{
> >> + pm_runtime_disable(&pdev->dev);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct of_device_id rcar_cmm_of_table[] = {
> >> + { .compatible = "renesas,rcar-gen3-cmm", },
> >> + { .compatible = "renesas,rcar-gen2-cmm", },
> >> + { },
> >> +};
> >> +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
> >> +
> >> +static struct platform_driver rcar_cmm_platform_driver = {
> >> + .probe = rcar_cmm_probe,
> >> + .remove = rcar_cmm_remove,
> >> + .driver = {
> >> + .name = "rcar-cmm",
> >> + .of_match_table = rcar_cmm_of_table,
> >> + },
> >> +};
> >> +
> >> +module_platform_driver(rcar_cmm_platform_driver);
> >> +
> >> +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
> >> +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
> >> +MODULE_LICENSE("GPL v2");
> >> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> >> new file mode 100644
> >> index 000000000000..15a2c874b6a6
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> >> @@ -0,0 +1,61 @@
> >> +/* SPDX-License-Identifier: GPL-2.0+ */
> >> +/*
> >> + * rcar_cmm.h -- R-Car Display Unit Color Management Module
> >> + *
> >> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> >> + */
> >> +
> >> +#ifndef __RCAR_CMM_H__
> >> +#define __RCAR_CMM_H__
> >> +
> >> +#define CM2_LUT_SIZE 256
> >> +
> >> +struct drm_color_lut;
> >> +struct platform_device;
> >> +
> >> +/**
> >> + * struct rcar_cmm_config - CMM configuration
> >> + *
> >> + * @lut: 1D-LUT configuration
> >> + * @lut.enable: 1D-LUT enable flag
> >> + * @lut.table: 1D-LUT table entries. Might be set to NULL when the CMM has to
> >> + * be re-enabled but not re=programmed.
> >
> > s/re=programmed/re-programmed/
> >
> > As discussed offline this can't really happen as far as I can tell.
> > However, it will still be useful when we'll add CLU support, as then a
> > CLU reprogramming without a LUT reprogramming could happen.
> >
> > I think we should make the documentation a bit clearer:
> >
> > "1D-LUT table entries. Only valid when lut.enable is true, shall be NULL
> > otherwise. When non-NULL, the LUT table will be programmed with the new
> > values. Otherwise the LUT table will retain its previously programmed
> > values."
> >
> > This being said, the code in rcar_cmm_setup() will crash if table is
> > NULL. I would either drop the option of table being NULL (and thus
> > update the documentation here) if you don't need this yet in the DU
> > driver, or fix rcar_cmm_setup(). You've posted enough versions of this
> > series in my opinion, so please pick the easiest option, and we'll
> > rework the code when adding CLU support anyway.
> >
> > With those small issues fixes,
> >
> > Reviewed-by: Laurent Pinchart <[email protected]>
> >
> >> + */
> >> +struct rcar_cmm_config {
> >> + struct {
> >> + bool enable;
> >> + struct drm_color_lut *table;
> >> + } lut;
> >> +};
> >> +
> >> +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
> >> +int rcar_cmm_init(struct platform_device *pdev);
> >> +
> >> +int rcar_cmm_enable(struct platform_device *pdev);
> >> +void rcar_cmm_disable(struct platform_device *pdev);
> >> +
> >> +int rcar_cmm_setup(struct platform_device *pdev,
> >> + const struct rcar_cmm_config *config);
> >> +#else
> >> +static inline int rcar_cmm_init(struct platform_device *pdev)
> >> +{
> >> + return 0;
>
> I feel like here, this should return -ENODEV if CMM is disabled, and
> allow rcar_du_cmm_init() to succeed, but leave the rcdu->cmms[i]
> unpopulated. ...

I think that's a good idea, otherwise the DU driver will believe that
CMM initialisation succeeded, and will configure the DU to route its
outputs to the CMM. I don't think that would work nicely without a
driver :-) Although, in practice, for the processing (non-statistics)
path, the DU output clock may be all that is needed, so the CMM might be
operational in its default configuration without the MSTP clock. Still,
that's probably not a very safe path, so I'd rather see CMM being
disabled on the DU side in that case.

> But this could be updated later too if it's awkward ...

Hopefully that would be easy to do :-)

> >> +}
> >> +
> >> +static inline int rcar_cmm_enable(struct platform_device *pdev)
> >> +{
> >> + return 0;
> >> +}
> >> +
> >> +static inline void rcar_cmm_disable(struct platform_device *pdev)
> >> +{
> >> +}
> >> +
> >> +static int rcar_cmm_setup(struct platform_device *pdev,
> >> + const struct rcar_cmm_config *config)
> >> +{
> >> + return 0;
> >> +}
> >> +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
> >> +
> >> +#endif /* __RCAR_CMM_H__ */

--
Regards,

Laurent Pinchart

2019-09-30 20:55:18

by Ezequiel Garcia

[permalink] [raw]
Subject: Re: [PATCH v4 8/9] drm: rcar-du: kms: Update CMM in atomic commit tail

+Doug, Heiko:

On Fri, 2019-09-06 at 15:54 +0200, Jacopo Mondi wrote:
> Update CMM settings at in the atomic commit tail helper method.
> The CMM is updated with new gamma values provided to the driver
> in the GAMMA_LUT blob property.
>
> When resuming from system suspend, the DU driver is responsible for
> reprogramming and enabling the CMM unit if it was in use at the time the
> system entered the suspend state. Force the color_mgmt_changed flag to
> true if the DRM gamma lut color transformation property was set in the
> CRTC state duplicated at suspend time, as the CMM gets reprogrammed only
> if said flag is active in the rcar_du_atomic_commit_update_cmm() method.
>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Jacopo Mondi <[email protected]>
> ---
>
> Daniel could you have a look if resume bits are worth being moved to the
> DRM core? The color_mgmt_changed flag is set to false when the state is
> duplicated if I read the code correctly, but when this happens in a
> suspend/resume sequence its value should probably be restored to true if
> any color management property was set in the crtc state when system entered
> suspend.
>

Perhaps we can use the for_each_new_crtc_in_state() helper here,
and move it to the core like this:

--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -3234,8 +3234,20 @@ int drm_atomic_helper_resume(struct
drm_device *dev,
struct drm_atomic_state *state)
{
struct drm_modeset_acquire_ctx ctx;
+ struct drm_crtc_state
*crtc_state;
+ struct drm_crtc *crtc;
+ unsigned int i;
int err;

+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+
/*
+ * Force re-enablement of CMM after system resume if any
+ * of the DRM color transformation properties
was set in
+ * the state saved at system suspend time.
+ */
+ if (crtc_state->gamma_lut)
+
crtc_state->color_mgmt_changed = true;
+ }

This probably is wrong, and should be instead constrained to some
condition of some sort.

FWIW, the Rockchip DRM is going to need this as well.

Any ideas?

Thanks,
Ezequiel

2019-10-01 19:22:28

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 8/9] drm: rcar-du: kms: Update CMM in atomic commit tail

Hi Ezequiel,

On Mon, Sep 30, 2019 at 05:53:00PM -0300, Ezequiel Garcia wrote:
> +Doug, Heiko:
>
> On Fri, 2019-09-06 at 15:54 +0200, Jacopo Mondi wrote:
> > Update CMM settings at in the atomic commit tail helper method.
> > The CMM is updated with new gamma values provided to the driver
> > in the GAMMA_LUT blob property.
> >
> > When resuming from system suspend, the DU driver is responsible for
> > reprogramming and enabling the CMM unit if it was in use at the time the
> > system entered the suspend state. Force the color_mgmt_changed flag to
> > true if the DRM gamma lut color transformation property was set in the
> > CRTC state duplicated at suspend time, as the CMM gets reprogrammed only
> > if said flag is active in the rcar_du_atomic_commit_update_cmm() method.
> >
> > Reviewed-by: Ulrich Hecht <[email protected]>
> > Reviewed-by: Laurent Pinchart <[email protected]>
> > Signed-off-by: Jacopo Mondi <[email protected]>
> > ---
> >
> > Daniel could you have a look if resume bits are worth being moved to the
> > DRM core? The color_mgmt_changed flag is set to false when the state is
> > duplicated if I read the code correctly, but when this happens in a
> > suspend/resume sequence its value should probably be restored to true if
> > any color management property was set in the crtc state when system entered
> > suspend.
>
> Perhaps we can use the for_each_new_crtc_in_state() helper here,
> and move it to the core like this:
>
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -3234,8 +3234,20 @@ int drm_atomic_helper_resume(struct
> drm_device *dev,
> struct drm_atomic_state *state)
> {
> struct drm_modeset_acquire_ctx ctx;
> + struct drm_crtc_state
> *crtc_state;
> + struct drm_crtc *crtc;
> + unsigned int i;
> int err;
>
> + for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
> +
> /*
> + * Force re-enablement of CMM after system resume if any
> + * of the DRM color transformation properties
> was set in
> + * the state saved at system suspend time.
> + */
> + if (crtc_state->gamma_lut)
> +
> crtc_state->color_mgmt_changed = true;
> + }
>
> This probably is wrong, and should be instead constrained to some
> condition of some sort.
>
> FWIW, the Rockchip DRM is going to need this as well.
>
> Any ideas?

That's more or less what I had in mind, yes. The question is if
something like this would make sense. If there's a consensus it would, I
think it can be done as part of this series.

--
Regards,

Laurent Pinchart

2019-10-10 17:47:51

by jacopo mondi

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Laurent,

On Thu, Sep 19, 2019 at 01:55:35AM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Fri, Sep 06, 2019 at 03:54:30PM +0200, Jacopo Mondi wrote:
> > Add a driver for the R-Car Display Unit Color Correction Module.
> >
> > In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
> > to perform image enhancement and color correction.
> >
> > Add support for CMM through a driver that supports configuration of
> > the 1-dimensional LUT table. More advanced CMM feature will be
> > implemented on top of this basic one.
> >
> > Signed-off-by: Jacopo Mondi <[email protected]>
> > ---
> > drivers/gpu/drm/rcar-du/Kconfig | 7 +
> > drivers/gpu/drm/rcar-du/Makefile | 1 +
> > drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
> > drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
> > 4 files changed, 320 insertions(+)
> > create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
> > create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
> >
> > diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
> > index 1529849e217e..539d232790d1 100644
> > --- a/drivers/gpu/drm/rcar-du/Kconfig
> > +++ b/drivers/gpu/drm/rcar-du/Kconfig
> > @@ -13,6 +13,13 @@ config DRM_RCAR_DU
> > Choose this option if you have an R-Car chipset.
> > If M is selected the module will be called rcar-du-drm.
> >
> > +config DRM_RCAR_CMM
> > + bool "R-Car DU Color Management Module (CMM) Support"
> > + depends on DRM && OF
> > + depends on DRM_RCAR_DU
> > + help
> > + Enable support for R-Car Color Management Module (CMM).
> > +
> > config DRM_RCAR_DW_HDMI
> > tristate "R-Car DU Gen3 HDMI Encoder Support"
> > depends on DRM && OF
> > diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
> > index 6c2ed9c46467..4d1187ccc3e5 100644
> > --- a/drivers/gpu/drm/rcar-du/Makefile
> > +++ b/drivers/gpu/drm/rcar-du/Makefile
> > @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
> > rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
> > rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
> >
> > +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
> > obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
> > obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
> > obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> > new file mode 100644
> > index 000000000000..3cacdc4474c7
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> > @@ -0,0 +1,251 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * rcar_cmm.c -- R-Car Display Unit Color Management Module
> > + *
> > + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +
> > +#include <drm/drm_color_mgmt.h>
> > +
> > +#include "rcar_cmm.h"
> > +
> > +#define CM2_LUT_CTRL 0x0000
> > +#define CM2_LUT_CTRL_LUT_EN BIT(0)
> > +#define CM2_LUT_TBL_BASE 0x0600
> > +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
> > +
> > +struct rcar_cmm {
> > + void __iomem *base;
> > + bool enabled;
> > +
> > + /*
> > + * @lut: 1D-LUT status
> > + * @lut.enabled: 1D-LUT enabled flag
> > + * @lut.table: Table of 1D-LUT entries scaled to hardware
> > + * precision (8-bits per color component)
> > + */
> > + struct {
> > + bool enabled;
> > + u32 table[CM2_LUT_SIZE];
> > + } lut;
> > +};
> > +
> > +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
> > +{
> > + return ioread32(rcmm->base + reg);
> > +}
> > +
> > +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
> > +{
> > + iowrite32(data, rcmm->base + reg);
> > +}
> > +
> > +/*
> > + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM LUT table
> > + * entries and store them.
>
> "Scale the DRM LUT table entries to hardware precision and store them."
>
> > + * @rcmm: Pointer to the CMM device
> > + * @drm_lut: Pointer to the DRM LUT table
> > + */
> > +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
> > + const struct drm_color_lut *drm_lut)
> > +{
> > + unsigned int i;
> > +
> > + for (i = 0; i < CM2_LUT_SIZE; ++i) {
> > + const struct drm_color_lut *lut = &drm_lut[i];
> > +
> > + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
> > + | drm_color_lut_extract(lut->green, 8) << 8
> > + | drm_color_lut_extract(lut->blue, 8);
> > + }
> > +}
> > +
> > +/*
> > + * rcar_cmm_lut_write() - Write to hardware the LUT table entries from the
> > + * local table.
>
> "Write the LUT table entries from the local table to the hardware."
>
> > + * @rcmm: Pointer to the CMM device
> > + */
> > +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
> > +{
> > + unsigned int i;
> > +
> > + for (i = 0; i < CM2_LUT_SIZE; ++i)
> > + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
> > +}
> > +
> > +/*
> > + * rcar_cmm_setup() - Configure the CMM unit.
> > + * @pdev: The platform device associated with the CMM instance
> > + * @config: The CRTC-provided configuration.
> > + *
> > + * Configure the CMM unit with the CRTC-provided configuration.
> > + * Currently enabling, disabling and programming of the 1-D LUT unit is
> > + * supported.
> > + */
> > +int rcar_cmm_setup(struct platform_device *pdev,
> > + const struct rcar_cmm_config *config)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > +
> > + /*
> > + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
> > + * be called when the CMM is disabled. As we can't program the hardware
> > + * in that case, store the configuration internally and apply it when
> > + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
> > + */
> > + if (!rcmm->enabled) {
> > + if (!config->lut.enable)
> > + return 0;
> > +
> > + rcar_cmm_lut_extract(rcmm, config->lut.table);
> > + rcmm->lut.enabled = true;
> > +
> > + return 0;
> > + }
> > +
> > + /* Stop LUT operations if requested. */
> > + if (!config->lut.enable) {
> > + if (rcmm->lut.enabled) {
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> > + rcmm->lut.enabled = false;
> > + }
> > +
> > + return 0;
> > + }
> > +
> > + /*
> > + * Enable LUT and program the new gamma table values.
> > + *
> > + * FIXME: In order to have stable operations it is required to first
> > + * enable the 1D-LUT and then program its table entries. This seems to
> > + * contradict what the chip manual reports, and will have to be
> > + * reconsidered when implementing support for double buffering.
> > + */
> > + if (!rcmm->lut.enabled) {
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> > + rcmm->lut.enabled = true;
> > + }
> > +
> > + rcar_cmm_lut_extract(rcmm, config->lut.table);
> > + rcar_cmm_lut_write(rcmm);
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
> > +
> > +/*
> > + * rcar_cmm_enable() - Enable the CMM unit.
> > + * @pdev: The platform device associated with the CMM instance
> > + *
> > + * Enable the CMM unit by enabling the parent clock and enabling the CMM
> > + * components, such as 1-D LUT, if requested.
> > + */
> > +int rcar_cmm_enable(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > + int ret;
> > +
> > + ret = pm_runtime_get_sync(&pdev->dev);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
> > + if (rcmm->lut.enabled) {
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> > + rcar_cmm_lut_write(rcmm);
> > + }
> > +
> > + rcmm->enabled = true;
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
> > +
> > +/*
> > + * rcar_cmm_disable() - Disable the CMM unit.
> > + * @pdev: The platform device associated with the CMM instance
> > + *
> > + * Disable the CMM unit by stopping the parent clock.
> > + */
> > +void rcar_cmm_disable(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > +
> > + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> > +
> > + pm_runtime_put(&pdev->dev);
> > +
> > + rcmm->lut.enabled = false;
> > + rcmm->enabled = false;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
> > +
> > +/*
> > + * rcar_cmm_init() - Make sure the CMM has probed.
>
> I would document this as "Intialize the CMM" to match the function name.
> We may add more initialization in the future.
>
> > + * @pdev: The platform device associated with the CMM instance
> > + *
> > + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
>
> 0 on success, -EPROBE_DEFER is the CMM isn't availablet yet
>

Just to note I've take Kieran's suggestion in to return -ENODEV when
the CMM config option is not set.

> > + */
> > +int rcar_cmm_init(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> > +
> > + if (!rcmm)
> > + return -EPROBE_DEFER;
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(rcar_cmm_init);
> > +
> > +static int rcar_cmm_probe(struct platform_device *pdev)
> > +{
> > + struct rcar_cmm *rcmm;
> > +
> > + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
> > + if (!rcmm)
> > + return -ENOMEM;
> > + platform_set_drvdata(pdev, rcmm);
> > +
> > + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(rcmm->base))
> > + return PTR_ERR(rcmm->base);
> > +
> > + pm_runtime_enable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_cmm_remove(struct platform_device *pdev)
> > +{
> > + pm_runtime_disable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id rcar_cmm_of_table[] = {
> > + { .compatible = "renesas,rcar-gen3-cmm", },
> > + { .compatible = "renesas,rcar-gen2-cmm", },
> > + { },
> > +};
> > +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
> > +
> > +static struct platform_driver rcar_cmm_platform_driver = {
> > + .probe = rcar_cmm_probe,
> > + .remove = rcar_cmm_remove,
> > + .driver = {
> > + .name = "rcar-cmm",
> > + .of_match_table = rcar_cmm_of_table,
> > + },
> > +};
> > +
> > +module_platform_driver(rcar_cmm_platform_driver);
> > +
> > +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
> > +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> > new file mode 100644
> > index 000000000000..15a2c874b6a6
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> > @@ -0,0 +1,61 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * rcar_cmm.h -- R-Car Display Unit Color Management Module
> > + *
> > + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> > + */
> > +
> > +#ifndef __RCAR_CMM_H__
> > +#define __RCAR_CMM_H__
> > +
> > +#define CM2_LUT_SIZE 256
> > +
> > +struct drm_color_lut;
> > +struct platform_device;
> > +
> > +/**
> > + * struct rcar_cmm_config - CMM configuration
> > + *
> > + * @lut: 1D-LUT configuration
> > + * @lut.enable: 1D-LUT enable flag
> > + * @lut.table: 1D-LUT table entries. Might be set to NULL when the CMM has to
> > + * be re-enabled but not re=programmed.
>
> s/re=programmed/re-programmed/
>
> As discussed offline this can't really happen as far as I can tell.
> However, it will still be useful when we'll add CLU support, as then a
> CLU reprogramming without a LUT reprogramming could happen.

How are we going to program CLU ? Using which DRM property ?
I had a look at 'ctm', but it does not seems to apply, as it only
provides 9 64bits entries (why 9??)
https://elixir.bootlin.com/linux/latest/source/include/uapi/drm/drm_mode.h#L623

The CLU is programmed with pairs of 32bits entries, assembled using
8 bit data/addresses for each color components. Considering the LUT
entries have 16 bits per color components, we could re-use those, but
seems a bit of an abuse to me. Is there somethig more trivial I am
missing ?

Anyway, for now I think you are right, and if the 'crtc->state->gamma_lut'
field is set, then the table (provided it's of the right size) cannot be
NULL.

Disabling the CMM with:
req.add(crtc, {
{ "GAMMA_LUT", 0 },
});

results in 'crtc->state->gamma_lut' being not set, and this is already
handled by setting to false the rcar_cmm_config.lut.enable flag in
rcar_du_atomic_commit_update_cmm() so I would rather drop the table ==
NULL case from documentation and re-think about it once we handle CLU.

>
> I think we should make the documentation a bit clearer:
>
> "1D-LUT table entries. Only valid when lut.enable is true, shall be NULL
> otherwise. When non-NULL, the LUT table will be programmed with the new
> values. Otherwise the LUT table will retain its previously programmed
> values."
>
> This being said, the code in rcar_cmm_setup() will crash if table is
> NULL. I would either drop the option of table being NULL (and thus
> update the documentation here) if you don't need this yet in the DU
> driver, or fix rcar_cmm_setup(). You've posted enough versions of this
> series in my opinion, so please pick the easiest option, and we'll
> rework the code when adding CLU support anyway.

Unfortunately I would love to take your tag in and being done with
this, but considering Sean's advices on Ezequiel's series, I think
I'll now need to move CMM handling to crtc's begin/enable as well, so
another review round will likely be required.

>
> With those small issues fixes,
>
> Reviewed-by: Laurent Pinchart <[email protected]>
>
> > + */
> > +struct rcar_cmm_config {
> > + struct {
> > + bool enable;
> > + struct drm_color_lut *table;
> > + } lut;
> > +};
> > +
> > +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
> > +int rcar_cmm_init(struct platform_device *pdev);
> > +
> > +int rcar_cmm_enable(struct platform_device *pdev);
> > +void rcar_cmm_disable(struct platform_device *pdev);
> > +
> > +int rcar_cmm_setup(struct platform_device *pdev,
> > + const struct rcar_cmm_config *config);
> > +#else
> > +static inline int rcar_cmm_init(struct platform_device *pdev)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int rcar_cmm_enable(struct platform_device *pdev)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void rcar_cmm_disable(struct platform_device *pdev)
> > +{
> > +}
> > +
> > +static int rcar_cmm_setup(struct platform_device *pdev,
> > + const struct rcar_cmm_config *config)
> > +{
> > + return 0;
> > +}
> > +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
> > +
> > +#endif /* __RCAR_CMM_H__ */
>
> --
> Regards,
>
> Laurent Pinchart


Attachments:
(No filename) (14.82 kB)
signature.asc (849.00 B)
Download all attachments

2019-10-15 01:52:29

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] drm: rcar-du: Add support for CMM

Hi Jacopo,

On Thu, Oct 10, 2019 at 07:46:28PM +0200, Jacopo Mondi wrote:
> On Thu, Sep 19, 2019 at 01:55:35AM +0300, Laurent Pinchart wrote:
> > On Fri, Sep 06, 2019 at 03:54:30PM +0200, Jacopo Mondi wrote:
> >> Add a driver for the R-Car Display Unit Color Correction Module.
> >>
> >> In most of Gen3 SoCs, each DU output channel is provided with a CMM unit
> >> to perform image enhancement and color correction.
> >>
> >> Add support for CMM through a driver that supports configuration of
> >> the 1-dimensional LUT table. More advanced CMM feature will be
> >> implemented on top of this basic one.
> >>
> >> Signed-off-by: Jacopo Mondi <[email protected]>
> >> ---
> >> drivers/gpu/drm/rcar-du/Kconfig | 7 +
> >> drivers/gpu/drm/rcar-du/Makefile | 1 +
> >> drivers/gpu/drm/rcar-du/rcar_cmm.c | 251 +++++++++++++++++++++++++++++
> >> drivers/gpu/drm/rcar-du/rcar_cmm.h | 61 +++++++
> >> 4 files changed, 320 insertions(+)
> >> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c
> >> create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h
> >>
> >> diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
> >> index 1529849e217e..539d232790d1 100644
> >> --- a/drivers/gpu/drm/rcar-du/Kconfig
> >> +++ b/drivers/gpu/drm/rcar-du/Kconfig
> >> @@ -13,6 +13,13 @@ config DRM_RCAR_DU
> >> Choose this option if you have an R-Car chipset.
> >> If M is selected the module will be called rcar-du-drm.
> >>
> >> +config DRM_RCAR_CMM
> >> + bool "R-Car DU Color Management Module (CMM) Support"
> >> + depends on DRM && OF
> >> + depends on DRM_RCAR_DU
> >> + help
> >> + Enable support for R-Car Color Management Module (CMM).
> >> +
> >> config DRM_RCAR_DW_HDMI
> >> tristate "R-Car DU Gen3 HDMI Encoder Support"
> >> depends on DRM && OF
> >> diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
> >> index 6c2ed9c46467..4d1187ccc3e5 100644
> >> --- a/drivers/gpu/drm/rcar-du/Makefile
> >> +++ b/drivers/gpu/drm/rcar-du/Makefile
> >> @@ -15,6 +15,7 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
> >> rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
> >> rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
> >>
> >> +obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o
> >> obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
> >> obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
> >> obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
> >> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> >> new file mode 100644
> >> index 000000000000..3cacdc4474c7
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c
> >> @@ -0,0 +1,251 @@
> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/*
> >> + * rcar_cmm.c -- R-Car Display Unit Color Management Module
> >> + *
> >> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> >> + */
> >> +
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/pm_runtime.h>
> >> +
> >> +#include <drm/drm_color_mgmt.h>
> >> +
> >> +#include "rcar_cmm.h"
> >> +
> >> +#define CM2_LUT_CTRL 0x0000
> >> +#define CM2_LUT_CTRL_LUT_EN BIT(0)
> >> +#define CM2_LUT_TBL_BASE 0x0600
> >> +#define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4)
> >> +
> >> +struct rcar_cmm {
> >> + void __iomem *base;
> >> + bool enabled;
> >> +
> >> + /*
> >> + * @lut: 1D-LUT status
> >> + * @lut.enabled: 1D-LUT enabled flag
> >> + * @lut.table: Table of 1D-LUT entries scaled to hardware
> >> + * precision (8-bits per color component)
> >> + */
> >> + struct {
> >> + bool enabled;
> >> + u32 table[CM2_LUT_SIZE];
> >> + } lut;
> >> +};
> >> +
> >> +static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
> >> +{
> >> + return ioread32(rcmm->base + reg);
> >> +}
> >> +
> >> +static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
> >> +{
> >> + iowrite32(data, rcmm->base + reg);
> >> +}
> >> +
> >> +/*
> >> + * rcar_cmm_lut_extract() - Scale down to hardware precision the DRM LUT table
> >> + * entries and store them.
> >
> > "Scale the DRM LUT table entries to hardware precision and store them."
> >
> >> + * @rcmm: Pointer to the CMM device
> >> + * @drm_lut: Pointer to the DRM LUT table
> >> + */
> >> +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm,
> >> + const struct drm_color_lut *drm_lut)
> >> +{
> >> + unsigned int i;
> >> +
> >> + for (i = 0; i < CM2_LUT_SIZE; ++i) {
> >> + const struct drm_color_lut *lut = &drm_lut[i];
> >> +
> >> + rcmm->lut.table[i] = drm_color_lut_extract(lut->red, 8) << 16
> >> + | drm_color_lut_extract(lut->green, 8) << 8
> >> + | drm_color_lut_extract(lut->blue, 8);
> >> + }
> >> +}
> >> +
> >> +/*
> >> + * rcar_cmm_lut_write() - Write to hardware the LUT table entries from the
> >> + * local table.
> >
> > "Write the LUT table entries from the local table to the hardware."
> >
> >> + * @rcmm: Pointer to the CMM device
> >> + */
> >> +static void rcar_cmm_lut_write(struct rcar_cmm *rcmm)
> >> +{
> >> + unsigned int i;
> >> +
> >> + for (i = 0; i < CM2_LUT_SIZE; ++i)
> >> + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), rcmm->lut.table[i]);
> >> +}
> >> +
> >> +/*
> >> + * rcar_cmm_setup() - Configure the CMM unit.
> >> + * @pdev: The platform device associated with the CMM instance
> >> + * @config: The CRTC-provided configuration.
> >> + *
> >> + * Configure the CMM unit with the CRTC-provided configuration.
> >> + * Currently enabling, disabling and programming of the 1-D LUT unit is
> >> + * supported.
> >> + */
> >> +int rcar_cmm_setup(struct platform_device *pdev,
> >> + const struct rcar_cmm_config *config)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> +
> >> + /*
> >> + * As rcar_cmm_setup() is called by atomic commit tail helper, it might
> >> + * be called when the CMM is disabled. As we can't program the hardware
> >> + * in that case, store the configuration internally and apply it when
> >> + * the CMM will be enabled by the CRTC through rcar_cmm_enable().
> >> + */
> >> + if (!rcmm->enabled) {
> >> + if (!config->lut.enable)
> >> + return 0;
> >> +
> >> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> >> + rcmm->lut.enabled = true;
> >> +
> >> + return 0;
> >> + }
> >> +
> >> + /* Stop LUT operations if requested. */
> >> + if (!config->lut.enable) {
> >> + if (rcmm->lut.enabled) {
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> >> + rcmm->lut.enabled = false;
> >> + }
> >> +
> >> + return 0;
> >> + }
> >> +
> >> + /*
> >> + * Enable LUT and program the new gamma table values.
> >> + *
> >> + * FIXME: In order to have stable operations it is required to first
> >> + * enable the 1D-LUT and then program its table entries. This seems to
> >> + * contradict what the chip manual reports, and will have to be
> >> + * reconsidered when implementing support for double buffering.
> >> + */
> >> + if (!rcmm->lut.enabled) {
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> >> + rcmm->lut.enabled = true;
> >> + }
> >> +
> >> + rcar_cmm_lut_extract(rcmm, config->lut.table);
> >> + rcar_cmm_lut_write(rcmm);
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_setup);
> >> +
> >> +/*
> >> + * rcar_cmm_enable() - Enable the CMM unit.
> >> + * @pdev: The platform device associated with the CMM instance
> >> + *
> >> + * Enable the CMM unit by enabling the parent clock and enabling the CMM
> >> + * components, such as 1-D LUT, if requested.
> >> + */
> >> +int rcar_cmm_enable(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> + int ret;
> >> +
> >> + ret = pm_runtime_get_sync(&pdev->dev);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + /* Apply the LUT table values saved at rcar_cmm_setup() time. */
> >> + if (rcmm->lut.enabled) {
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
> >> + rcar_cmm_lut_write(rcmm);
> >> + }
> >> +
> >> + rcmm->enabled = true;
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_enable);
> >> +
> >> +/*
> >> + * rcar_cmm_disable() - Disable the CMM unit.
> >> + * @pdev: The platform device associated with the CMM instance
> >> + *
> >> + * Disable the CMM unit by stopping the parent clock.
> >> + */
> >> +void rcar_cmm_disable(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> +
> >> + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
> >> +
> >> + pm_runtime_put(&pdev->dev);
> >> +
> >> + rcmm->lut.enabled = false;
> >> + rcmm->enabled = false;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_disable);
> >> +
> >> +/*
> >> + * rcar_cmm_init() - Make sure the CMM has probed.
> >
> > I would document this as "Intialize the CMM" to match the function name.
> > We may add more initialization in the future.
> >
> >> + * @pdev: The platform device associated with the CMM instance
> >> + *
> >> + * Return: 0 if the CMM has probed, -EPROBE_DEFER otherwise
> >
> > 0 on success, -EPROBE_DEFER is the CMM isn't availablet yet
> >
>
> Just to note I've take Kieran's suggestion in to return -ENODEV when
> the CMM config option is not set.
>
> >> + */
> >> +int rcar_cmm_init(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
> >> +
> >> + if (!rcmm)
> >> + return -EPROBE_DEFER;
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rcar_cmm_init);
> >> +
> >> +static int rcar_cmm_probe(struct platform_device *pdev)
> >> +{
> >> + struct rcar_cmm *rcmm;
> >> +
> >> + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
> >> + if (!rcmm)
> >> + return -ENOMEM;
> >> + platform_set_drvdata(pdev, rcmm);
> >> +
> >> + rcmm->base = devm_platform_ioremap_resource(pdev, 0);
> >> + if (IS_ERR(rcmm->base))
> >> + return PTR_ERR(rcmm->base);
> >> +
> >> + pm_runtime_enable(&pdev->dev);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int rcar_cmm_remove(struct platform_device *pdev)
> >> +{
> >> + pm_runtime_disable(&pdev->dev);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct of_device_id rcar_cmm_of_table[] = {
> >> + { .compatible = "renesas,rcar-gen3-cmm", },
> >> + { .compatible = "renesas,rcar-gen2-cmm", },
> >> + { },
> >> +};
> >> +MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
> >> +
> >> +static struct platform_driver rcar_cmm_platform_driver = {
> >> + .probe = rcar_cmm_probe,
> >> + .remove = rcar_cmm_remove,
> >> + .driver = {
> >> + .name = "rcar-cmm",
> >> + .of_match_table = rcar_cmm_of_table,
> >> + },
> >> +};
> >> +
> >> +module_platform_driver(rcar_cmm_platform_driver);
> >> +
> >> +MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
> >> +MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
> >> +MODULE_LICENSE("GPL v2");
> >> diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> >> new file mode 100644
> >> index 000000000000..15a2c874b6a6
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h
> >> @@ -0,0 +1,61 @@
> >> +/* SPDX-License-Identifier: GPL-2.0+ */
> >> +/*
> >> + * rcar_cmm.h -- R-Car Display Unit Color Management Module
> >> + *
> >> + * Copyright (C) 2019 Jacopo Mondi <[email protected]>
> >> + */
> >> +
> >> +#ifndef __RCAR_CMM_H__
> >> +#define __RCAR_CMM_H__
> >> +
> >> +#define CM2_LUT_SIZE 256
> >> +
> >> +struct drm_color_lut;
> >> +struct platform_device;
> >> +
> >> +/**
> >> + * struct rcar_cmm_config - CMM configuration
> >> + *
> >> + * @lut: 1D-LUT configuration
> >> + * @lut.enable: 1D-LUT enable flag
> >> + * @lut.table: 1D-LUT table entries. Might be set to NULL when the CMM has to
> >> + * be re-enabled but not re=programmed.
> >
> > s/re=programmed/re-programmed/
> >
> > As discussed offline this can't really happen as far as I can tell.
> > However, it will still be useful when we'll add CLU support, as then a
> > CLU reprogramming without a LUT reprogramming could happen.
>
> How are we going to program CLU ? Using which DRM property ?
> I had a look at 'ctm', but it does not seems to apply, as it only
> provides 9 64bits entries (why 9??)
> https://elixir.bootlin.com/linux/latest/source/include/uapi/drm/drm_mode.h#L623
>
> The CLU is programmed with pairs of 32bits entries, assembled using
> 8 bit data/addresses for each color components. Considering the LUT
> entries have 16 bits per color components, we could re-use those, but
> seems a bit of an abuse to me. Is there somethig more trivial I am
> missing ?

You're not missing anything, we'll have to create a new property for
this. The CLU is a 17x17x17 matrix of 32-bit values, that's what we'll
have to pass.

> Anyway, for now I think you are right, and if the 'crtc->state->gamma_lut'
> field is set, then the table (provided it's of the right size) cannot be
> NULL.
>
> Disabling the CMM with:
> req.add(crtc, {
> { "GAMMA_LUT", 0 },
> });
>
> results in 'crtc->state->gamma_lut' being not set, and this is already
> handled by setting to false the rcar_cmm_config.lut.enable flag in
> rcar_du_atomic_commit_update_cmm() so I would rather drop the table ==
> NULL case from documentation and re-think about it once we handle CLU.
>
> > I think we should make the documentation a bit clearer:
> >
> > "1D-LUT table entries. Only valid when lut.enable is true, shall be NULL
> > otherwise. When non-NULL, the LUT table will be programmed with the new
> > values. Otherwise the LUT table will retain its previously programmed
> > values."
> >
> > This being said, the code in rcar_cmm_setup() will crash if table is
> > NULL. I would either drop the option of table being NULL (and thus
> > update the documentation here) if you don't need this yet in the DU
> > driver, or fix rcar_cmm_setup(). You've posted enough versions of this
> > series in my opinion, so please pick the easiest option, and we'll
> > rework the code when adding CLU support anyway.
>
> Unfortunately I would love to take your tag in and being done with
> this, but considering Sean's advices on Ezequiel's series, I think
> I'll now need to move CMM handling to crtc's begin/enable as well, so
> another review round will likely be required.
>
> > With those small issues fixes,
> >
> > Reviewed-by: Laurent Pinchart <[email protected]>
> >
> >> + */
> >> +struct rcar_cmm_config {
> >> + struct {
> >> + bool enable;
> >> + struct drm_color_lut *table;
> >> + } lut;
> >> +};
> >> +
> >> +#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
> >> +int rcar_cmm_init(struct platform_device *pdev);
> >> +
> >> +int rcar_cmm_enable(struct platform_device *pdev);
> >> +void rcar_cmm_disable(struct platform_device *pdev);
> >> +
> >> +int rcar_cmm_setup(struct platform_device *pdev,
> >> + const struct rcar_cmm_config *config);
> >> +#else
> >> +static inline int rcar_cmm_init(struct platform_device *pdev)
> >> +{
> >> + return 0;
> >> +}
> >> +
> >> +static inline int rcar_cmm_enable(struct platform_device *pdev)
> >> +{
> >> + return 0;
> >> +}
> >> +
> >> +static inline void rcar_cmm_disable(struct platform_device *pdev)
> >> +{
> >> +}
> >> +
> >> +static int rcar_cmm_setup(struct platform_device *pdev,
> >> + const struct rcar_cmm_config *config)
> >> +{
> >> + return 0;
> >> +}
> >> +#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
> >> +
> >> +#endif /* __RCAR_CMM_H__ */

--
Regards,

Laurent Pinchart