Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 features will be
implemented on top of this initial one.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

authored by

Jacopo Mondi and committed by
Laurent Pinchart
e08e934d 6e2258b5

+284
+8
drivers/gpu/drm/rcar-du/Kconfig
··· 4 4 depends on DRM && OF 5 5 depends on ARM || ARM64 6 6 depends on ARCH_RENESAS || COMPILE_TEST 7 + imply DRM_RCAR_CMM 7 8 imply DRM_RCAR_LVDS 8 9 select DRM_KMS_HELPER 9 10 select DRM_KMS_CMA_HELPER ··· 13 12 help 14 13 Choose this option if you have an R-Car chipset. 15 14 If M is selected the module will be called rcar-du-drm. 15 + 16 + config DRM_RCAR_CMM 17 + tristate "R-Car DU Color Management Module (CMM) Support" 18 + depends on DRM && OF 19 + depends on DRM_RCAR_DU 20 + help 21 + Enable support for R-Car Color Management Module (CMM). 16 22 17 23 config DRM_RCAR_DW_HDMI 18 24 tristate "R-Car DU Gen3 HDMI Encoder Support"
+1
drivers/gpu/drm/rcar-du/Makefile
··· 15 15 rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o 16 16 rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o 17 17 18 + obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o 18 19 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o 19 20 obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o 20 21 obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
+217
drivers/gpu/drm/rcar-du/rcar_cmm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * rcar_cmm.c -- R-Car Display Unit Color Management Module 4 + * 5 + * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org> 6 + */ 7 + 8 + #include <linux/io.h> 9 + #include <linux/module.h> 10 + #include <linux/of.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/pm_runtime.h> 13 + 14 + #include <drm/drm_color_mgmt.h> 15 + 16 + #include "rcar_cmm.h" 17 + 18 + #define CM2_LUT_CTRL 0x0000 19 + #define CM2_LUT_CTRL_LUT_EN BIT(0) 20 + #define CM2_LUT_TBL_BASE 0x0600 21 + #define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4) 22 + 23 + struct rcar_cmm { 24 + void __iomem *base; 25 + 26 + /* 27 + * @lut: 1D-LUT state 28 + * @lut.enabled: 1D-LUT enabled flag 29 + */ 30 + struct { 31 + bool enabled; 32 + } lut; 33 + }; 34 + 35 + static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg) 36 + { 37 + return ioread32(rcmm->base + reg); 38 + } 39 + 40 + static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data) 41 + { 42 + iowrite32(data, rcmm->base + reg); 43 + } 44 + 45 + /* 46 + * rcar_cmm_lut_write() - Scale the DRM LUT table entries to hardware precision 47 + * and write to the CMM registers 48 + * @rcmm: Pointer to the CMM device 49 + * @drm_lut: Pointer to the DRM LUT table 50 + */ 51 + static void rcar_cmm_lut_write(struct rcar_cmm *rcmm, 52 + const struct drm_color_lut *drm_lut) 53 + { 54 + unsigned int i; 55 + 56 + for (i = 0; i < CM2_LUT_SIZE; ++i) { 57 + u32 entry = drm_color_lut_extract(drm_lut[i].red, 8) << 16 58 + | drm_color_lut_extract(drm_lut[i].green, 8) << 8 59 + | drm_color_lut_extract(drm_lut[i].blue, 8); 60 + 61 + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), entry); 62 + } 63 + } 64 + 65 + /* 66 + * rcar_cmm_setup() - Configure the CMM unit 67 + * @pdev: The platform device associated with the CMM instance 68 + * @config: The CMM unit configuration 69 + * 70 + * Configure the CMM unit with the given configuration. Currently enabling, 71 + * disabling and programming of the 1-D LUT unit is supported. 72 + * 73 + * As rcar_cmm_setup() accesses the CMM registers the unit should be powered 74 + * and its functional clock enabled. To guarantee this, before any call to 75 + * this function is made, the CMM unit has to be enabled by calling 76 + * rcar_cmm_enable() first. 77 + * 78 + * TODO: Add support for LUT double buffer operations to avoid updating the 79 + * LUT table entries while a frame is being displayed. 80 + */ 81 + int rcar_cmm_setup(struct platform_device *pdev, 82 + const struct rcar_cmm_config *config) 83 + { 84 + struct rcar_cmm *rcmm = platform_get_drvdata(pdev); 85 + 86 + /* Disable LUT if no table is provided. */ 87 + if (!config->lut.table) { 88 + if (rcmm->lut.enabled) { 89 + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0); 90 + rcmm->lut.enabled = false; 91 + } 92 + 93 + return 0; 94 + } 95 + 96 + /* Enable LUT and program the new gamma table values. */ 97 + if (!rcmm->lut.enabled) { 98 + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN); 99 + rcmm->lut.enabled = true; 100 + } 101 + 102 + rcar_cmm_lut_write(rcmm, config->lut.table); 103 + 104 + return 0; 105 + } 106 + EXPORT_SYMBOL_GPL(rcar_cmm_setup); 107 + 108 + /* 109 + * rcar_cmm_enable() - Enable the CMM unit 110 + * @pdev: The platform device associated with the CMM instance 111 + * 112 + * When the output of the corresponding DU channel is routed to the CMM unit, 113 + * the unit shall be enabled before the DU channel is started, and remain 114 + * enabled until the channel is stopped. The CMM unit shall be disabled with 115 + * rcar_cmm_disable(). 116 + * 117 + * Calls to rcar_cmm_enable() and rcar_cmm_disable() are not reference-counted. 118 + * It is an error to attempt to enable an already enabled CMM unit, or to 119 + * attempt to disable a disabled unit. 120 + */ 121 + int rcar_cmm_enable(struct platform_device *pdev) 122 + { 123 + int ret; 124 + 125 + ret = pm_runtime_get_sync(&pdev->dev); 126 + if (ret < 0) 127 + return ret; 128 + 129 + return 0; 130 + } 131 + EXPORT_SYMBOL_GPL(rcar_cmm_enable); 132 + 133 + /* 134 + * rcar_cmm_disable() - Disable the CMM unit 135 + * @pdev: The platform device associated with the CMM instance 136 + * 137 + * See rcar_cmm_enable() for usage information. 138 + * 139 + * Disabling the CMM unit disable all the internal processing blocks. The CMM 140 + * state shall thus be restored with rcar_cmm_setup() when re-enabling the CMM 141 + * unit after the next rcar_cmm_enable() call. 142 + */ 143 + void rcar_cmm_disable(struct platform_device *pdev) 144 + { 145 + struct rcar_cmm *rcmm = platform_get_drvdata(pdev); 146 + 147 + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0); 148 + rcmm->lut.enabled = false; 149 + 150 + pm_runtime_put(&pdev->dev); 151 + } 152 + EXPORT_SYMBOL_GPL(rcar_cmm_disable); 153 + 154 + /* 155 + * rcar_cmm_init() - Initialize the CMM unit 156 + * @pdev: The platform device associated with the CMM instance 157 + * 158 + * Return: 0 on success, -EPROBE_DEFER if the CMM is not available yet, 159 + * -ENODEV if the DRM_RCAR_CMM config option is disabled 160 + */ 161 + int rcar_cmm_init(struct platform_device *pdev) 162 + { 163 + struct rcar_cmm *rcmm = platform_get_drvdata(pdev); 164 + 165 + if (!rcmm) 166 + return -EPROBE_DEFER; 167 + 168 + return 0; 169 + } 170 + EXPORT_SYMBOL_GPL(rcar_cmm_init); 171 + 172 + static int rcar_cmm_probe(struct platform_device *pdev) 173 + { 174 + struct rcar_cmm *rcmm; 175 + 176 + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL); 177 + if (!rcmm) 178 + return -ENOMEM; 179 + platform_set_drvdata(pdev, rcmm); 180 + 181 + rcmm->base = devm_platform_ioremap_resource(pdev, 0); 182 + if (IS_ERR(rcmm->base)) 183 + return PTR_ERR(rcmm->base); 184 + 185 + pm_runtime_enable(&pdev->dev); 186 + 187 + return 0; 188 + } 189 + 190 + static int rcar_cmm_remove(struct platform_device *pdev) 191 + { 192 + pm_runtime_disable(&pdev->dev); 193 + 194 + return 0; 195 + } 196 + 197 + static const struct of_device_id rcar_cmm_of_table[] = { 198 + { .compatible = "renesas,rcar-gen3-cmm", }, 199 + { .compatible = "renesas,rcar-gen2-cmm", }, 200 + { }, 201 + }; 202 + MODULE_DEVICE_TABLE(of, rcar_cmm_of_table); 203 + 204 + static struct platform_driver rcar_cmm_platform_driver = { 205 + .probe = rcar_cmm_probe, 206 + .remove = rcar_cmm_remove, 207 + .driver = { 208 + .name = "rcar-cmm", 209 + .of_match_table = rcar_cmm_of_table, 210 + }, 211 + }; 212 + 213 + module_platform_driver(rcar_cmm_platform_driver); 214 + 215 + MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>"); 216 + MODULE_DESCRIPTION("Renesas R-Car CMM Driver"); 217 + MODULE_LICENSE("GPL v2");
+58
drivers/gpu/drm/rcar-du/rcar_cmm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * rcar_cmm.h -- R-Car Display Unit Color Management Module 4 + * 5 + * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org> 6 + */ 7 + 8 + #ifndef __RCAR_CMM_H__ 9 + #define __RCAR_CMM_H__ 10 + 11 + #define CM2_LUT_SIZE 256 12 + 13 + struct drm_color_lut; 14 + struct platform_device; 15 + 16 + /** 17 + * struct rcar_cmm_config - CMM configuration 18 + * 19 + * @lut: 1D-LUT configuration 20 + * @lut.table: 1D-LUT table entries. Disable LUT operations when NULL 21 + */ 22 + struct rcar_cmm_config { 23 + struct { 24 + struct drm_color_lut *table; 25 + } lut; 26 + }; 27 + 28 + #if IS_ENABLED(CONFIG_DRM_RCAR_CMM) 29 + int rcar_cmm_init(struct platform_device *pdev); 30 + 31 + int rcar_cmm_enable(struct platform_device *pdev); 32 + void rcar_cmm_disable(struct platform_device *pdev); 33 + 34 + int rcar_cmm_setup(struct platform_device *pdev, 35 + const struct rcar_cmm_config *config); 36 + #else 37 + static inline int rcar_cmm_init(struct platform_device *pdev) 38 + { 39 + return -ENODEV; 40 + } 41 + 42 + static inline int rcar_cmm_enable(struct platform_device *pdev) 43 + { 44 + return 0; 45 + } 46 + 47 + static inline void rcar_cmm_disable(struct platform_device *pdev) 48 + { 49 + } 50 + 51 + static inline int rcar_cmm_setup(struct platform_device *pdev, 52 + const struct rcar_cmm_config *config) 53 + { 54 + return 0; 55 + } 56 + #endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */ 57 + 58 + #endif /* __RCAR_CMM_H__ */