Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * R-Car Display Unit Color Management Module
0004  *
0005  * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org>
0006  */
0007 
0008 #include <linux/io.h>
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/pm_runtime.h>
0013 
0014 #include <drm/drm_color_mgmt.h>
0015 
0016 #include "rcar_cmm.h"
0017 
0018 #define CM2_LUT_CTRL        0x0000
0019 #define CM2_LUT_CTRL_LUT_EN BIT(0)
0020 #define CM2_LUT_TBL_BASE    0x0600
0021 #define CM2_LUT_TBL(__i)    (CM2_LUT_TBL_BASE + (__i) * 4)
0022 
0023 struct rcar_cmm {
0024     void __iomem *base;
0025 
0026     /*
0027      * @lut:        1D-LUT state
0028      * @lut.enabled:    1D-LUT enabled flag
0029      */
0030     struct {
0031         bool enabled;
0032     } lut;
0033 };
0034 
0035 static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
0036 {
0037     return ioread32(rcmm->base + reg);
0038 }
0039 
0040 static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
0041 {
0042     iowrite32(data, rcmm->base + reg);
0043 }
0044 
0045 /*
0046  * rcar_cmm_lut_write() - Scale the DRM LUT table entries to hardware precision
0047  *            and write to the CMM registers
0048  * @rcmm: Pointer to the CMM device
0049  * @drm_lut: Pointer to the DRM LUT table
0050  */
0051 static void rcar_cmm_lut_write(struct rcar_cmm *rcmm,
0052                    const struct drm_color_lut *drm_lut)
0053 {
0054     unsigned int i;
0055 
0056     for (i = 0; i < CM2_LUT_SIZE; ++i) {
0057         u32 entry = drm_color_lut_extract(drm_lut[i].red, 8) << 16
0058               | drm_color_lut_extract(drm_lut[i].green, 8) << 8
0059               | drm_color_lut_extract(drm_lut[i].blue, 8);
0060 
0061         rcar_cmm_write(rcmm, CM2_LUT_TBL(i), entry);
0062     }
0063 }
0064 
0065 /*
0066  * rcar_cmm_setup() - Configure the CMM unit
0067  * @pdev: The platform device associated with the CMM instance
0068  * @config: The CMM unit configuration
0069  *
0070  * Configure the CMM unit with the given configuration. Currently enabling,
0071  * disabling and programming of the 1-D LUT unit is supported.
0072  *
0073  * As rcar_cmm_setup() accesses the CMM registers the unit should be powered
0074  * and its functional clock enabled. To guarantee this, before any call to
0075  * this function is made, the CMM unit has to be enabled by calling
0076  * rcar_cmm_enable() first.
0077  *
0078  * TODO: Add support for LUT double buffer operations to avoid updating the
0079  * LUT table entries while a frame is being displayed.
0080  */
0081 int rcar_cmm_setup(struct platform_device *pdev,
0082            const struct rcar_cmm_config *config)
0083 {
0084     struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
0085 
0086     /* Disable LUT if no table is provided. */
0087     if (!config->lut.table) {
0088         if (rcmm->lut.enabled) {
0089             rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
0090             rcmm->lut.enabled = false;
0091         }
0092 
0093         return 0;
0094     }
0095 
0096     /* Enable LUT and program the new gamma table values. */
0097     if (!rcmm->lut.enabled) {
0098         rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
0099         rcmm->lut.enabled = true;
0100     }
0101 
0102     rcar_cmm_lut_write(rcmm, config->lut.table);
0103 
0104     return 0;
0105 }
0106 EXPORT_SYMBOL_GPL(rcar_cmm_setup);
0107 
0108 /*
0109  * rcar_cmm_enable() - Enable the CMM unit
0110  * @pdev: The platform device associated with the CMM instance
0111  *
0112  * When the output of the corresponding DU channel is routed to the CMM unit,
0113  * the unit shall be enabled before the DU channel is started, and remain
0114  * enabled until the channel is stopped. The CMM unit shall be disabled with
0115  * rcar_cmm_disable().
0116  *
0117  * Calls to rcar_cmm_enable() and rcar_cmm_disable() are not reference-counted.
0118  * It is an error to attempt to enable an already enabled CMM unit, or to
0119  * attempt to disable a disabled unit.
0120  */
0121 int rcar_cmm_enable(struct platform_device *pdev)
0122 {
0123     int ret;
0124 
0125     ret = pm_runtime_resume_and_get(&pdev->dev);
0126     if (ret < 0)
0127         return ret;
0128 
0129     return 0;
0130 }
0131 EXPORT_SYMBOL_GPL(rcar_cmm_enable);
0132 
0133 /*
0134  * rcar_cmm_disable() - Disable the CMM unit
0135  * @pdev: The platform device associated with the CMM instance
0136  *
0137  * See rcar_cmm_enable() for usage information.
0138  *
0139  * Disabling the CMM unit disable all the internal processing blocks. The CMM
0140  * state shall thus be restored with rcar_cmm_setup() when re-enabling the CMM
0141  * unit after the next rcar_cmm_enable() call.
0142  */
0143 void rcar_cmm_disable(struct platform_device *pdev)
0144 {
0145     struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
0146 
0147     rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
0148     rcmm->lut.enabled = false;
0149 
0150     pm_runtime_put(&pdev->dev);
0151 }
0152 EXPORT_SYMBOL_GPL(rcar_cmm_disable);
0153 
0154 /*
0155  * rcar_cmm_init() - Initialize the CMM unit
0156  * @pdev: The platform device associated with the CMM instance
0157  *
0158  * Return: 0 on success, -EPROBE_DEFER if the CMM is not available yet,
0159  *         -ENODEV if the DRM_RCAR_CMM config option is disabled
0160  */
0161 int rcar_cmm_init(struct platform_device *pdev)
0162 {
0163     struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
0164 
0165     if (!rcmm)
0166         return -EPROBE_DEFER;
0167 
0168     return 0;
0169 }
0170 EXPORT_SYMBOL_GPL(rcar_cmm_init);
0171 
0172 static int rcar_cmm_probe(struct platform_device *pdev)
0173 {
0174     struct rcar_cmm *rcmm;
0175 
0176     rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
0177     if (!rcmm)
0178         return -ENOMEM;
0179     platform_set_drvdata(pdev, rcmm);
0180 
0181     rcmm->base = devm_platform_ioremap_resource(pdev, 0);
0182     if (IS_ERR(rcmm->base))
0183         return PTR_ERR(rcmm->base);
0184 
0185     pm_runtime_enable(&pdev->dev);
0186 
0187     return 0;
0188 }
0189 
0190 static int rcar_cmm_remove(struct platform_device *pdev)
0191 {
0192     pm_runtime_disable(&pdev->dev);
0193 
0194     return 0;
0195 }
0196 
0197 static const struct of_device_id rcar_cmm_of_table[] = {
0198     { .compatible = "renesas,rcar-gen3-cmm", },
0199     { .compatible = "renesas,rcar-gen2-cmm", },
0200     { },
0201 };
0202 MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
0203 
0204 static struct platform_driver rcar_cmm_platform_driver = {
0205     .probe      = rcar_cmm_probe,
0206     .remove     = rcar_cmm_remove,
0207     .driver     = {
0208         .name   = "rcar-cmm",
0209         .of_match_table = rcar_cmm_of_table,
0210     },
0211 };
0212 
0213 module_platform_driver(rcar_cmm_platform_driver);
0214 
0215 MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
0216 MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
0217 MODULE_LICENSE("GPL v2");