From patchwork Sun Aug 25 13:51:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 11113769 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 30264174A for ; Mon, 26 Aug 2019 00:21:07 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1728C23429 for ; Mon, 26 Aug 2019 00:21:07 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1728C23429 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=jmondi.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=dri-devel-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DA59C6E165; Mon, 26 Aug 2019 00:20:59 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by gabe.freedesktop.org (Postfix) with ESMTPS id 745F86E0FE for ; Sun, 25 Aug 2019 13:51:03 +0000 (UTC) X-Originating-IP: 87.18.63.98 Received: from uno.homenet.telecomitalia.it (unknown [87.18.63.98]) (Authenticated sender: jacopo@jmondi.org) by relay6-d.mail.gandi.net (Postfix) with ESMTPSA id DEEF2C0005; Sun, 25 Aug 2019 13:50:58 +0000 (UTC) From: Jacopo Mondi To: laurent.pinchart@ideasonboard.com, kieran.bingham+renesas@ideasonboard.com, geert@linux-m68k.org, horms@verge.net.au, uli@fpond.eu, airlied@linux.ie, daniel@ffwll.ch Subject: [PATCH v3 08/14] drm: rcar-du: Add support for CMM Date: Sun, 25 Aug 2019 15:51:48 +0200 Message-Id: <20190825135154.11488-9-jacopo+renesas@jmondi.org> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190825135154.11488-1-jacopo+renesas@jmondi.org> References: <20190825135154.11488-1-jacopo+renesas@jmondi.org> MIME-Version: 1.0 X-Mailman-Approved-At: Mon, 26 Aug 2019 00:18:56 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: muroya@ksk.co.jp, koji.matsuoka.xm@renesas.com, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-renesas-soc@vger.kernel.org, VenkataRajesh.Kalakodima@in.bosch.com, Jacopo Mondi , Harsha.ManjulaMallikarjun@in.bosch.com Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 --- drivers/gpu/drm/rcar-du/Kconfig | 7 + drivers/gpu/drm/rcar-du/Makefile | 1 + drivers/gpu/drm/rcar-du/rcar_cmm.c | 262 +++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_cmm.h | 38 +++++ 4 files changed, 308 insertions(+) create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c create mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h -- 2.22.0 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..55361f5701e8 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * rcar_cmm.c -- R-Car Display Unit Color Management Module + * + * Copyright (C) 2019 Jacopo Mondi + */ + +#include +#include +#include +#include +#include + +#include + +#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.size: Number of entries in the LUT table + * @lut.table: Table of 1D-LUT entries scaled to HW support + * precision (8-bits per color component) + */ + struct { + bool enabled; + unsigned int size; + u32 table[CMM_GAMMA_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 hw precision the DRM LUT table + * entries and store them. + * @rcmm: Pointer to the CMM device + * @size: Number of entries in the table + * @drm_lut: DRM LUT table + */ +static void rcar_cmm_lut_extract(struct rcar_cmm *rcmm, size_t size, + const struct drm_color_lut *drm_lut) +{ + unsigned int i; + + for (i = 0; i < 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); + } + + rcmm->lut.size = size; +} + +/* + * rcar_cmm_lut_load() - Write to hw the LUT table entries from the local table. + * + * @rcmm: Pointer to the CMM device + */ +static void rcar_cmm_lut_load(struct rcar_cmm *rcmm) +{ + unsigned int i; + + for (i = 0; i < rcmm->lut.size; ++i) { + u32 entry = rcmm->lut.table[i]; + + 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 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); + + if (config->lut.size > CMM_GAMMA_LUT_SIZE) + return -EINVAL; + + /* + * 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.size, 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; + rcmm->lut.size = 0; + } + + 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.size, config->lut.table); + rcar_cmm_lut_load(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; + + if (!rcmm) + return -EPROBE_DEFER; + + 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_load(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->lut.size = 0; + + rcmm->enabled = false; +} +EXPORT_SYMBOL_GPL(rcar_cmm_disable); + +static int rcar_cmm_probe(struct platform_device *pdev) +{ + struct rcar_cmm *rcmm; + struct resource *res; + + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL); + if (!rcmm) + return -ENOMEM; + + platform_set_drvdata(pdev, rcmm); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rcmm->base = devm_ioremap_resource(&pdev->dev, res); + 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,cmm-r8a7795", }, + { .compatible = "renesas,cmm-r8a7796", }, + { .compatible = "renesas,cmm-r8a77965", }, + { .compatible = "renesas,cmm-r8a77990", }, + { .compatible = "renesas,cmm-r8a77995", }, + { .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 "); +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..b0bb7349ebaa --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_cmm.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * rcar_cmm.h -- R-Car Display Unit Color Management Module + * + * Copyright (C) 2019 Jacopo Mondi + */ + +#ifndef __RCAR_CMM_H__ +#define __RCAR_CMM_H__ + +#define CMM_GAMMA_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 + * @lut.size: Number of 1D-LUT (max 256) + */ +struct rcar_cmm_config { + struct { + bool enable; + struct drm_color_lut *table; + unsigned int size; + } lut; +}; + +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); + +#endif /* __RCAR_CMM_H__ */