cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rcar_cmm.c (5634B)


      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
     23struct 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
     35static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
     36{
     37	return ioread32(rcmm->base + reg);
     38}
     39
     40static 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 */
     51static 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 */
     81int 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}
    106EXPORT_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 */
    121int rcar_cmm_enable(struct platform_device *pdev)
    122{
    123	int ret;
    124
    125	ret = pm_runtime_resume_and_get(&pdev->dev);
    126	if (ret < 0)
    127		return ret;
    128
    129	return 0;
    130}
    131EXPORT_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 */
    143void 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}
    152EXPORT_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 */
    161int 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}
    170EXPORT_SYMBOL_GPL(rcar_cmm_init);
    171
    172static 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
    190static int rcar_cmm_remove(struct platform_device *pdev)
    191{
    192	pm_runtime_disable(&pdev->dev);
    193
    194	return 0;
    195}
    196
    197static const struct of_device_id rcar_cmm_of_table[] = {
    198	{ .compatible = "renesas,rcar-gen3-cmm", },
    199	{ .compatible = "renesas,rcar-gen2-cmm", },
    200	{ },
    201};
    202MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
    203
    204static 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
    213module_platform_driver(rcar_cmm_platform_driver);
    214
    215MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
    216MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
    217MODULE_LICENSE("GPL v2");