Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | // SPDX-License-Identifier: GPL-2.0+ /* * R-Car Display Unit Color Management Module * * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org> */ #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; /* * @lut: 1D-LUT state * @lut.enabled: 1D-LUT enabled flag */ struct { bool enabled; } 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_write() - Scale the DRM LUT table entries to hardware precision * and write to the CMM registers * @rcmm: Pointer to the CMM device * @drm_lut: Pointer to the DRM LUT table */ static void rcar_cmm_lut_write(struct rcar_cmm *rcmm, const struct drm_color_lut *drm_lut) { unsigned int i; for (i = 0; i < CM2_LUT_SIZE; ++i) { u32 entry = drm_color_lut_extract(drm_lut[i].red, 8) << 16 | drm_color_lut_extract(drm_lut[i].green, 8) << 8 | drm_color_lut_extract(drm_lut[i].blue, 8); rcar_cmm_write(rcmm, CM2_LUT_TBL(i), entry); } } /* * rcar_cmm_setup() - Configure the CMM unit * @pdev: The platform device associated with the CMM instance * @config: The CMM unit configuration * * Configure the CMM unit with the given configuration. Currently enabling, * disabling and programming of the 1-D LUT unit is supported. * * As rcar_cmm_setup() accesses the CMM registers the unit should be powered * and its functional clock enabled. To guarantee this, before any call to * this function is made, the CMM unit has to be enabled by calling * rcar_cmm_enable() first. * * TODO: Add support for LUT double buffer operations to avoid updating the * LUT table entries while a frame is being displayed. */ int rcar_cmm_setup(struct platform_device *pdev, const struct rcar_cmm_config *config) { struct rcar_cmm *rcmm = platform_get_drvdata(pdev); /* Disable LUT if no table is provided. */ if (!config->lut.table) { 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. */ if (!rcmm->lut.enabled) { rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN); rcmm->lut.enabled = true; } rcar_cmm_lut_write(rcmm, config->lut.table); return 0; } EXPORT_SYMBOL_GPL(rcar_cmm_setup); /* * rcar_cmm_enable() - Enable the CMM unit * @pdev: The platform device associated with the CMM instance * * When the output of the corresponding DU channel is routed to the CMM unit, * the unit shall be enabled before the DU channel is started, and remain * enabled until the channel is stopped. The CMM unit shall be disabled with * rcar_cmm_disable(). * * Calls to rcar_cmm_enable() and rcar_cmm_disable() are not reference-counted. * It is an error to attempt to enable an already enabled CMM unit, or to * attempt to disable a disabled unit. */ int rcar_cmm_enable(struct platform_device *pdev) { int ret; ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; return 0; } EXPORT_SYMBOL_GPL(rcar_cmm_enable); /* * rcar_cmm_disable() - Disable the CMM unit * @pdev: The platform device associated with the CMM instance * * See rcar_cmm_enable() for usage information. * * Disabling the CMM unit disable all the internal processing blocks. The CMM * state shall thus be restored with rcar_cmm_setup() when re-enabling the CMM * unit after the next rcar_cmm_enable() call. */ void rcar_cmm_disable(struct platform_device *pdev) { struct rcar_cmm *rcmm = platform_get_drvdata(pdev); rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0); rcmm->lut.enabled = false; pm_runtime_put(&pdev->dev); } EXPORT_SYMBOL_GPL(rcar_cmm_disable); /* * rcar_cmm_init() - Initialize the CMM unit * @pdev: The platform device associated with the CMM instance * * Return: 0 on success, -EPROBE_DEFER if the CMM is not available yet, * -ENODEV if the DRM_RCAR_CMM config option is disabled */ 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 <jacopo+renesas@jmondi.org>"); MODULE_DESCRIPTION("Renesas R-Car CMM Driver"); MODULE_LICENSE("GPL v2"); |