From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2EA25C432C1 for ; Tue, 24 Sep 2019 08:57:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A7FD2214DA for ; Tue, 24 Sep 2019 08:57:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="W4Fex1mR" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729331AbfIXI5H (ORCPT ); Tue, 24 Sep 2019 04:57:07 -0400 Received: from mail-oi1-f195.google.com ([209.85.167.195]:39578 "EHLO mail-oi1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726311AbfIXI5G (ORCPT ); Tue, 24 Sep 2019 04:57:06 -0400 Received: by mail-oi1-f195.google.com with SMTP id w144so954640oia.6 for ; Tue, 24 Sep 2019 01:57:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=0bceCoJ4CrrGyDiUUmg7EYBxulQKywZalfn3OUAE0w4=; b=W4Fex1mROlVtGetkBZWzB4rbVrcuiBun/Q/gtmCurgEGWoRqMGNA+VlM3ESUAcsKs6 NwNvn2peEXmFbJYreDZLvnAQLGStfGp/oI61FmAAn9WLahZXmkCygdraaKuTkCCPJGbF 84s94TypC+V42gsRIiGNbunjxxYS5o3SGIt4wLkysL0xFnjcDWXZjqAJ83ll8jVNaCF6 KsrKLIYo3ZWGRKgaDXfPMmGhkwNA7cpUIAIGjZDHVpIcyKYU7T9mFMeBcWPGME3oSNDm cF1/53OGSHm7h8JUc2VmENdPOmkhTPPhxC6Svhx/VgBG+PLYdJhExuZl4Kbww+F0HPVp 6eNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=0bceCoJ4CrrGyDiUUmg7EYBxulQKywZalfn3OUAE0w4=; b=fUZO8cFs4OL49j5hVqr04dZ6hp5ltmuEXBjfvKdBlSdfsFYVMG3HmJtWQ7Lb7kmmGP /nGFZjeVMEd4JXnqB/HHob3LnyIk0x+BFSXyy1zm1wNHMDStsR2f20DaeY9tTrc8RT+M 7px8G7bFdxC7ejN/pEGn3aGcFDW2KW/b1Aiyn6VJMqpzLitCrlEWKuooRKIpIbdzsGdd xYg0ESbA8ZVQvsDNwrw4LDlD2Q38I/iySiMSM+2S580SD/kKdIFnSxhbgMFXEkcRCBnb T42rvv1hFIzKuOsFhZorD9i+7vh3PxCw/7bNWsgLMNSs41KJGg9PxHrTflAptsN95NYK Urww== X-Gm-Message-State: APjAAAXY/FbLU2Obabfa5yuEjBnHXKgKumNtTT6Pe0iLxeQpXwzdCVzR JxDH2CFxbgyAs6dry/9InSolQGA8+Cw9Sj74WznFfA== X-Google-Smtp-Source: APXvYqzdW3lUl05NiIKi44wjYTkeT3dz4exsLzsDYtWpw/Ix3TTUXSnx4IBmX5PVq/egNGivrbQ41KGSghEajOprZ9Y= X-Received: by 2002:aca:ec06:: with SMTP id k6mr1141446oih.78.1569315420884; Tue, 24 Sep 2019 01:57:00 -0700 (PDT) MIME-Version: 1.0 References: <1569248002-2485-1-git-send-email-laurentiu.palcu@nxp.com> <1569248002-2485-4-git-send-email-laurentiu.palcu@nxp.com> <20190924080355.GC25260@fsr-ub1664-121> In-Reply-To: <20190924080355.GC25260@fsr-ub1664-121> From: Ying Liu Date: Tue, 24 Sep 2019 16:56:49 +0800 Message-ID: Subject: Re: Re: [PATCH 3/5] drm/imx: Add initial support for DCSS on iMX8MQ To: Laurentiu Palcu Cc: Philipp Zabel , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , dl-linux-imx , "agx@sigxcpu.org" , "linux-kernel@vger.kernel.org" , DRI Development , "linux-arm-kernel@lists.infradead.org" Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, Sep 24, 2019 at 4:04 PM Laurentiu Palcu w= rote: > > Hi, > > On Tue, Sep 24, 2019 at 11:56:07AM +0800, Ying Liu wrote: > > Hi, > > > > On Mon, Sep 23, 2019 at 10:22 PM Laurentiu Palcu > > wrote: > > > > > > This adds initial support for iMX8MQ's Display Controller Subsystem (= DCSS). > > > Some of its capabilities include: > > > * 4K@60fps; > > > * HDR10; > > > * one graphics and 2 video pipelines; > > > * on-the-fly decompression of compressed video and graphics; > > > > > > The reference manual can be found here: > > > https://eur01.safelinks.protection.outlook.com/?url=3Dhttps%3A%2F%2Fw= ww.nxp.com%2Fwebapp%2FDownload%3FcolCode%3DIMX8MDQLQRM&data=3D02%7C01%7= Claurentiu.palcu%40nxp.com%7C94b752dadd2e413c70d008d740a32979%7C686ea1d3bc2= b4c6fa92cd99c5c301635%7C0%7C0%7C637048941849367464&sdata=3DrsFNo5MGwdFG= TAheU8YrJeRLvThpwbGG9bC9hKxCsm0%3D&reserved=3D0 > > > > > > The current patch adds only basic functionality: one primary plane fo= r > > > graphics, linear, tiled and super-tiled buffers support (no graphics > > > decompression yet), no HDR10 and no video planes. > > > > > > Video planes support and HDR10 will be added in subsequent patches on= ce > > > per-plane de-gamma/CSC/gamma support is in. > > > > > > Signed-off-by: Laurentiu Palcu > > > --- > > > drivers/gpu/drm/imx/Kconfig | 2 + > > > drivers/gpu/drm/imx/Makefile | 1 + > > > drivers/gpu/drm/imx/dcss/Kconfig | 7 + > > > drivers/gpu/drm/imx/dcss/Makefile | 6 + > > > drivers/gpu/drm/imx/dcss/dcss-blkctl.c | 75 +++ > > > drivers/gpu/drm/imx/dcss/dcss-crtc.c | 223 +++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-ctxld.c | 447 ++++++++++++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-dev.c | 286 ++++++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-dev.h | 195 ++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-dpr.c | 552 ++++++++++++++++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-drv.c | 182 ++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-dtg.c | 438 +++++++++++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-kms.c | 321 +++++++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-kms.h | 52 +++ > > > drivers/gpu/drm/imx/dcss/dcss-plane.c | 418 +++++++++++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-scaler.c | 830 +++++++++++++++++++++++= ++++++++++ > > > drivers/gpu/drm/imx/dcss/dcss-ss.c | 179 +++++++ > > > 17 files changed, 4214 insertions(+) > > > create mode 100644 drivers/gpu/drm/imx/dcss/Kconfig > > > create mode 100644 drivers/gpu/drm/imx/dcss/Makefile > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-blkctl.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-crtc.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-ctxld.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-dev.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-dev.h > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-dpr.c > > > > DPR embedded in i.MX8QM & i.MX8QXP SoCs has > > compatible register definitions with the one in i.MX8MQ. > > Together with PRG(Prefetch Resolve Gasket), DPR serves > > the display controller DPU of i.MX8QM & i.MX8QXP SoCs > > as the prefetch engine. IMHO, there should be a separate > > DPR driver which stands out side of the dcss directory and > > has DT compatible strings for i.MX8MQ, i.MX8QM and > > i.MX8QXP SoCs. > > Yes, DPR is pretty much the same for those platforms, register wise. > However, DCSS will do all DPR programming through the context loader > engine (DCSS specific), whereas DPU does not. DPU's DPR can handle VPU The different ways of accessing register can be wrapped by some helpers, so it doesn't seem to bring much code for platform sense. > tiled formats, whereas DCSS's does not. For that, DCSS has a DTRC > (decompression and tile to raster conversion) module in front of DPR, > hence the DPR programming needs some adjustments when > tiled/tiled-compressed video buffers are used. What kind of adjustments? Is it complicated enough to break the idea of one DPR driver solution? > > Your idea is great, when it comes to removing some code duplication. > But if, after this process, we end up with an overly complicated piece > of code that needs to address different platform particularities, I'd > prefer to keep things separate. The code will be easier to read and > maintain. Understand. But, it's worth evaluating how complicated it can be. > > Thanks, > laurentiu > > > > > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-drv.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-dtg.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-kms.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-kms.h > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-plane.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-scaler.c > > > create mode 100644 drivers/gpu/drm/imx/dcss/dcss-ss.c > > > > > > diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfi= g > > > index 207bf74..6231048 100644 > > > --- a/drivers/gpu/drm/imx/Kconfig > > > +++ b/drivers/gpu/drm/imx/Kconfig > > > @@ -39,3 +39,5 @@ config DRM_IMX_HDMI > > > depends on DRM_IMX > > > help > > > Choose this if you want to use HDMI on i.MX6. > > > + > > > +source "drivers/gpu/drm/imx/dcss/Kconfig" > > > diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makef= ile > > > index 21cdcc2..b644def 100644 > > > --- a/drivers/gpu/drm/imx/Makefile > > > +++ b/drivers/gpu/drm/imx/Makefile > > > @@ -9,3 +9,4 @@ obj-$(CONFIG_DRM_IMX_TVE) +=3D imx-tve.o > > > obj-$(CONFIG_DRM_IMX_LDB) +=3D imx-ldb.o > > > > > > obj-$(CONFIG_DRM_IMX_HDMI) +=3D dw_hdmi-imx.o > > > +obj-$(CONFIG_DRM_IMX_DCSS) +=3D dcss/ > > > diff --git a/drivers/gpu/drm/imx/dcss/Kconfig b/drivers/gpu/drm/imx/d= css/Kconfig > > > new file mode 100644 > > > index 00000000..6399c12 > > > --- /dev/null > > > +++ b/drivers/gpu/drm/imx/dcss/Kconfig > > > @@ -0,0 +1,7 @@ > > > +config DRM_IMX_DCSS > > > + tristate "i.MX8MQ DCSS" > > > + select RESET_CONTROLLER > > > + select IMX_IRQSTEER > > > + help > > > + Choose this if you have a NXP i.MX8MQ based system and want= to use the > > > + Display Controller Subsystem. This option enables DCSS supp= ort. > > > diff --git a/drivers/gpu/drm/imx/dcss/Makefile b/drivers/gpu/drm/imx/= dcss/Makefile > > > new file mode 100644 > > > index 00000000..8c7c8da > > > --- /dev/null > > > +++ b/drivers/gpu/drm/imx/dcss/Makefile > > > @@ -0,0 +1,6 @@ > > > +imx-dcss-objs :=3D dcss-drv.o dcss-dev.o dcss-blkctl.o dcss-ctxld.o = dcss-dtg.o \ > > > + dcss-ss.o dcss-dpr.o dcss-scaler.o d= css-kms.o dcss-crtc.o \ > > > + dcss-plane.o > > > + > > > +obj-$(CONFIG_DRM_IMX_DCSS) +=3D imx-dcss.o > > > + > > > diff --git a/drivers/gpu/drm/imx/dcss/dcss-blkctl.c b/drivers/gpu/drm= /imx/dcss/dcss-blkctl.c > > > new file mode 100644 > > > index 00000000..ee7ffa1 > > > --- /dev/null > > > +++ b/drivers/gpu/drm/imx/dcss/dcss-blkctl.c > > > @@ -0,0 +1,75 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * Copyright 2019 NXP. > > > + */ > > > + > > > +#include > > > +#include > > > + > > > +#include "dcss-dev.h" > > > + > > > +#define DCSS_BLKCTL_RESET_CTRL 0x00 > > > +#define B_CLK_RESETN BIT(0) > > > +#define APB_CLK_RESETN BIT(1) > > > +#define P_CLK_RESETN BIT(2) > > > +#define RTR_CLK_RESETN BIT(3) > > > +#define DCSS_BLKCTL_CONTROL0 0x10 > > > +#define HDMI_MIPI_CLK_SEL BIT(0) > > > +#define DISPMIX_REFCLK_SEL_POS 4 > > > +#define DISPMIX_REFCLK_SEL_MASK GENMASK(5, 4) > > > +#define DISPMIX_PIXCLK_SEL BIT(8) > > > +#define HDMI_SRC_SECURE_EN BIT(16) > > > + > > > +struct dcss_blkctl { > > > + struct device *dev; > > > + void __iomem *base_reg; > > > + > > > + bool hdmi_output; > > > +}; > > > + > > > +void dcss_blkctl_cfg(struct dcss_blkctl *blkctl) > > > +{ > > > + if (blkctl->hdmi_output) > > > + dcss_writel(0, blkctl->base_reg + DCSS_BLKCTL_CONTROL= 0); > > > + else > > > + dcss_writel(DISPMIX_PIXCLK_SEL, > > > + blkctl->base_reg + DCSS_BLKCTL_CONTROL0); > > > + > > > + dcss_set(B_CLK_RESETN | APB_CLK_RESETN | P_CLK_RESETN | RTR_C= LK_RESETN, > > > + blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL); > > > +} > > > + > > > +int dcss_blkctl_init(struct dcss_dev *dcss, unsigned long blkctl_bas= e) > > > +{ > > > + struct dcss_blkctl *blkctl; > > > + > > > + blkctl =3D devm_kzalloc(dcss->dev, sizeof(*blkctl), GFP_KERNE= L); > > > + if (!blkctl) > > > + return -ENOMEM; > > > + > > > + blkctl->base_reg =3D devm_ioremap(dcss->dev, blkctl_base, SZ_= 4K); > > > + if (!blkctl->base_reg) { > > > + dev_err(dcss->dev, "unable to remap BLK CTRL base\n")= ; > > > + devm_kfree(dcss->dev, blkctl); > > > + return -ENOMEM; > > > + } > > > + > > > + dcss->blkctl =3D blkctl; > > > + blkctl->dev =3D dcss->dev; > > > + blkctl->hdmi_output =3D dcss->hdmi_output; > > > + > > > + dcss_blkctl_cfg(blkctl); > > > + > > > + return 0; > > > +} > > > + > > > +void dcss_blkctl_exit(struct dcss_blkctl *blkctl) > > > +{ > > > + dcss_clr(P_CLK_RESETN | RTR_CLK_RESETN, > > > + blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL); > > > + > > > + if (blkctl->base_reg) > > > + devm_iounmap(blkctl->dev, blkctl->base_reg); > > > + > > > + devm_kfree(blkctl->dev, blkctl); > > > +} > > > diff --git a/drivers/gpu/drm/imx/dcss/dcss-crtc.c b/drivers/gpu/drm/i= mx/dcss/dcss-crtc.c > > > new file mode 100644 > > > index 00000000..6abb40c > > > --- /dev/null > > > +++ b/drivers/gpu/drm/imx/dcss/dcss-crtc.c > > > @@ -0,0 +1,223 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * Copyright 2019 NXP. > > > + */ > > > + > > > +#include > > > +#include > > > +#include > > > + > > > +#include "dcss-dev.h" > > > +#include "dcss-kms.h" > > > + > > > +static int dcss_enable_vblank(struct drm_crtc *crtc) > > > +{ > > > + struct dcss_crtc *dcss_crtc =3D container_of(crtc, struct dcs= s_crtc, > > > + base); > > > + struct dcss_dev *dcss =3D crtc->dev->dev_private; > > > + > > > + if (dcss_crtc->irq_enabled) > > > + return 0; > > > + > > > + dcss_crtc->irq_enabled =3D true; > > > + > > > + dcss_dtg_vblank_irq_enable(dcss->dtg, true); > > > + > > > + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); > > > + > > > + enable_irq(dcss_crtc->irq); > > > + > > > + return 0; > > > +} > > > + > > > +static void dcss_disable_vblank(struct drm_crtc *crtc) > > > +{ > > > + struct dcss_crtc *dcss_crtc =3D container_of(crtc, struct dcs= s_crtc, > > > + base); > > > + struct dcss_dev *dcss =3D dcss_crtc->base.dev->dev_private; > > > + > > > + disable_irq_nosync(dcss_crtc->irq); > > > + > > > + dcss_dtg_vblank_irq_enable(dcss->dtg, false); > > > + > > > + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false); > > > + > > > + dcss_crtc->irq_enabled =3D false; > > > +} > > > + > > > +static const struct drm_crtc_funcs dcss_crtc_funcs =3D { > > > + .set_config =3D drm_atomic_helper_set_config, > > > + .destroy =3D drm_crtc_cleanup, > > > + .page_flip =3D drm_atomic_helper_page_flip, > > > + .reset =3D drm_atomic_helper_crtc_reset, > > > + .atomic_duplicate_state =3D drm_atomic_helper_crtc_duplicate_= state, > > > + .atomic_destroy_state =3D drm_atomic_helper_crtc_destroy_stat= e, > > > + .enable_vblank =3D dcss_enable_vblank, > > > + .disable_vblank =3D dcss_disable_vblank, > > > +}; > > > + > > > +static void dcss_crtc_atomic_begin(struct drm_crtc *crtc, > > > + struct drm_crtc_state *old_crtc_st= ate) > > > +{ > > > + drm_crtc_vblank_on(crtc); > > > + > > > + spin_lock_irq(&crtc->dev->event_lock); > > > + if (crtc->state->event) { > > > + WARN_ON(drm_crtc_vblank_get(crtc)); > > > + drm_crtc_arm_vblank_event(crtc, crtc->state->event); > > > + crtc->state->event =3D NULL; > > > + } > > > + spin_unlock_irq(&crtc->dev->event_lock); > > > +} > > > + > > > +static void dcss_crtc_atomic_flush(struct drm_crtc *crtc, > > > + struct drm_crtc_state *old_crtc_st= ate) > > > +{ > > > + struct dcss_crtc *dcss_crtc =3D container_of(crtc, struct dcs= s_crtc, > > > + base); > > > + struct dcss_dev *dcss =3D dcss_crtc->base.dev->dev_private; > > > + > > > + if (dcss_dtg_is_enabled(dcss->dtg)) > > > + dcss_ctxld_enable(dcss->ctxld); > > > +} > > > + > > > +static void dcss_crtc_atomic_enable(struct drm_crtc *crtc, > > > + struct drm_crtc_state *old_crtc_s= tate) > > > +{ > > > + struct dcss_crtc *dcss_crtc =3D container_of(crtc, struct dcs= s_crtc, > > > + base); > > > + struct dcss_dev *dcss =3D dcss_crtc->base.dev->dev_private; > > > + struct drm_display_mode *mode =3D &crtc->state->adjusted_mode= ; > > > + struct videomode vm; > > > + > > > + drm_display_mode_to_videomode(mode, &vm); > > > + > > > + pm_runtime_get_sync(dcss->dev); > > > + > > > + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); > > > + > > > + vm.pixelclock =3D mode->crtc_clock * 1000; > > > + > > > + dcss_dtg_sync_set(dcss->dtg, &vm); > > > + > > > + dcss_ss_subsam_set(dcss->ss); > > > + dcss_ss_sync_set(dcss->ss, &vm, mode->flags & DRM_MODE_FLAG_P= HSYNC, > > > + mode->flags & DRM_MODE_FLAG_PVSYNC); > > > + > > > + dcss_dtg_css_set(dcss->dtg); > > > + > > > + dcss_ss_enable(dcss->ss); > > > + dcss_dtg_enable(dcss->dtg, true, NULL); > > > + dcss_ctxld_enable(dcss->ctxld); > > > + > > > + reinit_completion(&dcss_crtc->en_completion); > > > + wait_for_completion_timeout(&dcss_crtc->en_completion, > > > + msecs_to_jiffies(500)); > > > +} > > > + > > > +static void dcss_crtc_atomic_disable(struct drm_crtc *crtc, > > > + struct drm_crtc_state *old_crtc_= state) > > > +{ > > > + struct dcss_crtc *dcss_crtc =3D container_of(crtc, struct dcs= s_crtc, > > > + base); > > > + struct dcss_dev *dcss =3D dcss_crtc->base.dev->dev_private; > > > + > > > + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, fals= e); > > > + > > > + spin_lock_irq(&crtc->dev->event_lock); > > > + if (crtc->state->event) { > > > + drm_crtc_send_vblank_event(crtc, crtc->state->event); > > > + crtc->state->event =3D NULL; > > > + } > > > + spin_unlock_irq(&crtc->dev->event_lock); > > > + > > > + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); > > > + > > > + dcss_ss_disable(dcss->ss); > > > + dcss_dtg_enable(dcss->dtg, false, &dcss_crtc->dis_completion)= ; > > > + dcss_ctxld_enable(dcss->ctxld); > > > + > > > + reinit_completion(&dcss_crtc->dis_completion); > > > + wait_for_completion_timeout(&dcss_crtc->dis_completion, > > > + msecs_to_jiffies(100)); > > > + > > > + drm_crtc_vblank_off(crtc); > > > + > > > + dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false); > > > + > > > + pm_runtime_put_sync(dcss->dev); > > > +} > > > + > > > +static const struct drm_crtc_helper_funcs dcss_helper_funcs =3D { > > > + .atomic_begin =3D dcss_crtc_atomic_begin, > > > + .atomic_flush =3D dcss_crtc_atomic_flush, > > > + .atomic_enable =3D dcss_crtc_atomic_enable, > > > + .atomic_disable =3D dcss_crtc_atomic_disable, > > > +}; > > > + > > > +static irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id) > > > +{ > > > + struct dcss_crtc *dcss_crtc =3D dev_id; > > > + struct dcss_dev *dcss =3D dcss_crtc->base.dev->dev_private; > > > + > > > + if (!dcss_dtg_vblank_irq_valid(dcss->dtg)) > > > + return IRQ_HANDLED; > > > + > > > + complete(&dcss_crtc->en_completion); > > > + > > > + if (dcss_ctxld_is_flushed(dcss->ctxld)) > > > + drm_crtc_handle_vblank(&dcss_crtc->base); > > > + > > > + dcss_dtg_vblank_irq_clear(dcss->dtg); > > > + > > > + return IRQ_HANDLED; > > > +} > > > + > > > +int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm) > > > +{ > > > + struct dcss_dev *dcss =3D drm->dev_private; > > > + struct platform_device *pdev =3D to_platform_device(dcss->dev= ); > > > + int ret; > > > + > > > + crtc->plane[0] =3D dcss_plane_init(drm, drm_crtc_mask(&crtc->= base), > > > + DRM_PLANE_TYPE_PRIMARY, 0); > > > + if (IS_ERR(crtc->plane[0])) > > > + return PTR_ERR(crtc->plane[0]); > > > + > > > + crtc->base.port =3D dcss->of_port; > > > + > > > + drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs); > > > + ret =3D drm_crtc_init_with_planes(drm, &crtc->base, &crtc->pl= ane[0]->base, > > > + NULL, &dcss_crtc_funcs, NULL)= ; > > > + if (ret) { > > > + dev_err(dcss->dev, "failed to init crtc\n"); > > > + return ret; > > > + } > > > + > > > + crtc->irq =3D platform_get_irq_byname(pdev, "vblank"); > > > + if (crtc->irq < 0) { > > > + dev_err(dcss->dev, "unable to get vblank interrupt\n"= ); > > > + return crtc->irq; > > > + } > > > + > > > + init_completion(&crtc->en_completion); > > > + init_completion(&crtc->dis_completion); > > > + > > > + ret =3D devm_request_irq(dcss->dev, crtc->irq, dcss_crtc_irq_= handler, > > > + IRQF_TRIGGER_RISING, "dcss_drm", crtc)= ; > > > + if (ret) { > > > + dev_err(dcss->dev, "irq request failed with %d.\n", r= et); > > > + return ret; > > > + } > > > + > > > + disable_irq(crtc->irq); > > > + > > > + return 0; > > > +} > > > + > > > +void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm= ) > > > +{ > > > + struct dcss_dev *dcss =3D drm->dev_private; > > > + > > > + devm_free_irq(dcss->dev, crtc->irq, crtc); > > > +} > > > diff --git a/drivers/gpu/drm/imx/dcss/dcss-ctxld.c b/drivers/gpu/drm/= imx/dcss/dcss-ctxld.c > > > new file mode 100644 > > > index 00000000..4fe35b2b > > > --- /dev/null > > > +++ b/drivers/gpu/drm/imx/dcss/dcss-ctxld.c > > > @@ -0,0 +1,447 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * Copyright 2019 NXP. > > > + */ > > > + > > > +#include > > > +#include > > > +#include > > > +#include > > > + > > > +#include "dcss-dev.h" > > > + > > > +#define DCSS_CTXLD_DEVNAME "dcss_ctxld" > > > + > > > +#define DCSS_CTXLD_CONTROL_STATUS 0x0 > > > +#define CTXLD_ENABLE BIT(0) > > > +#define ARB_SEL BIT(1) > > > +#define RD_ERR_EN BIT(2) > > > +#define DB_COMP_EN BIT(3) > > > +#define SB_HP_COMP_EN BIT(4) > > > +#define SB_LP_COMP_EN BIT(5) > > > +#define DB_PEND_SB_REC_EN BIT(6) > > > +#define SB_PEND_DISP_ACTIVE_EN BIT(7) > > > +#define AHB_ERR_EN BIT(8) > > > +#define RD_ERR BIT(16) > > > +#define DB_COMP BIT(17) > > > +#define SB_HP_COMP BIT(18) > > > +#define SB_LP_COMP BIT(19) > > > +#define DB_PEND_SB_REC BIT(20) > > > +#define SB_PEND_DISP_ACTIVE BIT(21) > > > +#define AHB_ERR BIT(22) > > > +#define DCSS_CTXLD_DB_BASE_ADDR 0x10 > > > +#define DCSS_CTXLD_DB_COUNT 0x14 > > > +#define DCSS_CTXLD_SB_BASE_ADDR 0x18 > > > +#define DCSS_CTXLD_SB_COUNT 0x1C > > > +#define SB_HP_COUNT_POS 0 > > > +#define SB_HP_COUNT_MASK 0xffff > > > +#define SB_LP_COUNT_POS 16 > > > +#define SB_LP_COUNT_MASK 0xffff0000 > > > +#define DCSS_AHB_ERR_ADDR 0x20 > > > + > > > +#define CTXLD_IRQ_NAME "ctx_ld" > > > +#define CTXLD_IRQ_COMPLETION (DB_COMP | SB_HP_COMP | SB_LP= _COMP) > > > +#define CTXLD_IRQ_ERROR (RD_ERR | DB_PEND_SB_= REC | AHB_ERR) > > > + > > > +/* The following sizes are in context loader entries, 8 bytes each. = */ > > > +#define CTXLD_DB_CTX_ENTRIES 1024 /* max 65536 */ > > > +#define CTXLD_SB_LP_CTX_ENTRIES 10240 /* max 65536 = */ > > > +#define CTXLD_SB_HP_CTX_ENTRIES 20000 /* max 65536 = */ > > > +#define CTXLD_SB_CTX_ENTRIES (CTXLD_SB_LP_CTX_ENTRIES + \ > > > + CTXLD_SB_HP_CTX_ENTRIES) > > > + > > > +/* Sizes, in entries, of the DB, SB_HP and SB_LP context regions. */ > > > +static u16 dcss_ctxld_ctx_size[3] =3D { > > > + CTXLD_DB_CTX_ENTRIES, > > > + CTXLD_SB_HP_CTX_ENTRIES, > > > + CTXLD_SB_LP_CTX_ENTRIES > > > +}; > > > + > > > +/* this represents an entry in the context loader map */ > > > +struct dcss_ctxld_item { > > > + u32 val; > > > + u32 ofs; > > > +}; > > > + > > > +#define CTX_ITEM_SIZE sizeof(struct dcss_ctxld_item= ) > > > + > > > +struct dcss_ctxld { > > > + struct device *dev; > > > + void __iomem *ctxld_reg; > > > + int irq; > > > + bool irq_en; > > > + > > > + struct dcss_ctxld_item *db[2]; > > > + struct dcss_ctxld_item *sb_hp[2]; > > > + struct dcss_ctxld_item *sb_lp[2]; > > > + > > > + dma_addr_t db_paddr[2]; > > > + dma_addr_t sb_paddr[2]; > > > + > > > + u16 ctx_size[2][3]; /* holds the sizes of DB, SB_HP and SB_LP= ctx */ > > > + u8 current_ctx; > > > + > > > + bool in_use; > > > + bool armed; > > > + > > > + spinlock_t lock; /* protects concurent access to private data= */ > > > + > > > + void (*dtg_disable_cb)(void *data); > > > + void *dtg_disable_data; > > > +}; > > > + > > > +static int __dcss_ctxld_enable(struct dcss_ctxld *ctxld); > > > + > > > +static irqreturn_t dcss_ctxld_irq_handler(int irq, void *data) > > > +{ > > > + struct dcss_ctxld *ctxld =3D data; > > > + u32 irq_status; > > > + > > > + irq_status =3D dcss_readl(ctxld->ctxld_reg + DCSS_CTXLD_CONTR= OL_STATUS); > > > + > > > + if (irq_status & CTXLD_IRQ_COMPLETION && > > > + !(irq_status & CTXLD_ENABLE) && ctxld->in_use) { > > > + ctxld->in_use =3D false; > > > + > > > + if (ctxld->dtg_disable_cb) { > > > + ctxld->dtg_disable_cb(ctxld->dtg_disable_data= ); > > > + ctxld->dtg_disable_cb =3D NULL; > > > + ctxld->dtg_disable_data =3D NULL; > > > + } > > > + } else if (irq_status & CTXLD_IRQ_ERROR) { > > > + /* > > > + * Except for throwing an error message and clearing = the status > > > + * register, there's not much we can do here. > > > + */ > > > + dev_err(ctxld->dev, "ctxld: error encountered: %08x\n= ", > > > + irq_status); > > > + dev_err(ctxld->dev, "ctxld: db=3D%d, sb_hp=3D%d, sb_l= p=3D%d\n", > > > + ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_D= B], > > > + ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_S= B_HP], > > > + ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_S= B_LP]); > > > + } > > > + > > > + dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION= ), > > > + ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS); > > > + > > > + return IRQ_HANDLED; > > > +} > > > + > > > +static int dcss_ctxld_irq_config(struct dcss_ctxld *ctxld, > > > + struct platform_device *pdev) > > > +{ > > > + int ret; > > > + > > > + ctxld->irq =3D platform_get_irq_byname(pdev, CTXLD_IRQ_NAME); > > > + if (ctxld->irq < 0) { > > > + dev_err(ctxld->dev, "ctxld: can't get irq number\n"); > > > + return ctxld->irq; > > > + } > > > + > > > + ret =3D devm_request_irq(ctxld->dev, ctxld->irq, > > > + dcss_ctxld_irq_handler, > > > + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, > > > + DCSS_CTXLD_DEVNAME, ctxld); > > > + if (ret) { > > > + dev_err(ctxld->dev, "ctxld: irq request failed.\n"); > > > + return ret; > > > + } > > > + > > > + ctxld->irq_en =3D true; > > > + > > > + return 0; > > > +} > > > + > > > +void dcss_ctxld_hw_cfg(struct dcss_ctxld *ctxld) > > > +{ > > > + dcss_writel(RD_ERR_EN | SB_HP_COMP_EN | > > > + DB_PEND_SB_REC_EN | AHB_ERR_EN | RD_ERR | AHB_ERR= , > > > + ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS); > > > +} > > > + > > > +static void dcss_ctxld_free_ctx(struct dcss_ctxld *ctxld) > > > +{ > > > + struct dcss_ctxld_item *ctx; > > > + int i; > > > + > > > + for (i =3D 0; i < 2; i++) { > > > + if (ctxld->db[i]) { > > > + dmam_free_coherent(ctxld->dev, > > > + CTXLD_DB_CTX_ENTRIES * siz= eof(*ctx), > > > + ctxld->db[i], ctxld->db_pa= ddr[i]); > > > + ctxld->db[i] =3D NULL; > > > + ctxld->db_paddr[i] =3D 0; > > > + } > > > + > > > + if (ctxld->sb_hp[i]) { > > > + dmam_free_coherent(ctxld->dev, > > > + CTXLD_SB_CTX_ENTRIES * siz= eof(*ctx), > > > + ctxld->sb_hp[i], ctxld->sb= _paddr[i]); > > > + ctxld->sb_hp[i] =3D NULL; > > > + ctxld->sb_paddr[i] =3D 0; > > > + } > > > + } > > > +} > > > + > > > +static int dcss_ctxld_alloc_ctx(struct dcss_ctxld *ctxld) > > > +{ > > > + struct dcss_ctxld_item *ctx; > > > + int i; > > > + dma_addr_t dma_handle; > > > + > > > + for (i =3D 0; i < 2; i++) { > > > + ctx =3D dmam_alloc_coherent(ctxld->dev, > > > + CTXLD_DB_CTX_ENTRIES * size= of(*ctx), > > > + &dma_handle, GFP_KERNEL); > > > + if (!ctx) > > > + return -ENOMEM; > > > + > > > + ctxld->db[i] =3D ctx; > > > + ctxld->db_paddr[i] =3D dma_handle; > > > + > > > + ctx =3D dmam_alloc_coherent(ctxld->dev, > > > + CTXLD_SB_CTX_ENTRIES * size= of(*ctx), > > > + &dma_handle, GFP_KERNEL); > > > + if (!ctx) > > > + return -ENOMEM; > > > + > > > + ctxld->sb_hp[i] =3D ctx; > > > + ctxld->sb_lp[i] =3D ctx + CTXLD_SB_HP_CTX_ENTRIES; > > > + > > > + ctxld->sb_paddr[i] =3D dma_handle; > > > + } > > > + > > > + return 0; > > > +} > > > + > > > +int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base) > > > +{ > > > + struct dcss_ctxld *ctxld; > > > + int ret; > > > + > > > + ctxld =3D devm_kzalloc(dcss->dev, sizeof(struct dcss_ctxld), > > > + GFP_KERNEL); > > > + if (!ctxld) > > > + return -ENOMEM; > > > + > > > + dcss->ctxld =3D ctxld; > > > + ctxld->dev =3D dcss->dev; > > > + > > > + spin_lock_init(&ctxld->lock); > > > + > > > + ret =3D dcss_ctxld_alloc_ctx(ctxld); > > > + if (ret) { > > > + dev_err(dcss->dev, "ctxld: cannot allocate context me= mory.\n"); > > > + goto err; > > > + } > > > + > > > + ctxld->ctxld_reg =3D devm_ioremap(dcss->dev, ctxld_base, SZ_4= K); > > > + if (!ctxld->ctxld_reg) { > > > + dev_err(dcss->dev, "ctxld: unable to remap ctxld base= \n"); > > > + ret =3D -ENOMEM; > > > + goto err; > > > + } > > > + > > > + ret =3D dcss_ctxld_irq_config(ctxld, to_platform_device(dcss-= >dev)); > > > + if (ret) > > > + goto err_irq; > > > + > > > + dcss_ctxld_hw_cfg(ctxld); > > > + > > > + return 0; > > > + > > > +err_irq: > > > + devm_iounmap(ctxld->dev, ctxld->ctxld_reg); > > > + > > > +err: > > > + dcss_ctxld_free_ctx(ctxld); > > > + devm_kfree(ctxld->dev, ctxld); > > > + > > > + return ret; > > > +} > > > + > > > +void dcss_ctxld_exit(struct dcss_ctxld *ctxld) > > > +{ > > > + devm_free_irq(ctxld->dev, ctxld->irq, ctxld); > > > + > > > + if (ctxld->ctxld_reg) > > > + devm_iounmap(ctxld->dev, ctxld->ctxld_reg); > > > + > > > + dcss_ctxld_free_ctx(ctxld); > > > + devm_kfree(ctxld->dev, ctxld); > > > +} > > > + > > > +static int __dcss_ctxld_enable(struct dcss_ctxld *ctxld) > > > +{ > > > + int curr_ctx =3D ctxld->current_ctx; > > > + u32 db_base, sb_base, sb_count; > > > + u32 sb_hp_cnt, sb_lp_cnt, db_cnt; > > > + struct dcss_dev *dcss =3D dcss_drv_dev_to_dcss(ctxld->dev); > > > + > > > + dcss_dpr_write_sysctrl(dcss->dpr); > > > + > > > + dcss_scaler_write_sclctrl(dcss->scaler); > > > + > > > + sb_hp_cnt =3D ctxld->ctx_size[curr_ctx][CTX_SB_HP]; > > > + sb_lp_cnt =3D ctxld->ctx_size[curr_ctx][CTX_SB_LP]; > > > + db_cnt =3D ctxld->ctx_size[curr_ctx][CTX_DB]; > > > + > > > + /* make sure SB_LP context area comes after SB_HP */ > > > + if (sb_lp_cnt && > > > + ctxld->sb_lp[curr_ctx] !=3D ctxld->sb_hp[curr_ctx] + sb_h= p_cnt) { > > > + struct dcss_ctxld_item *sb_lp_adjusted; > > > + > > > + sb_lp_adjusted =3D ctxld->sb_hp[curr_ctx] + sb_hp_cnt= ; > > > + > > > + memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx], > > > + sb_lp_cnt * CTX_ITEM_SIZE); > > > + } > > > + > > > + db_base =3D db_cnt ? ctxld->db_paddr[curr_ctx] : 0; > > > + > > > + dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_AD= DR); > > > + dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT); > > > + > > > + if (sb_hp_cnt) > > > + sb_count =3D ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_= COUNT_MASK) | > > > + ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_CO= UNT_MASK); > > > + else > > > + sb_count =3D (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_C= OUNT_MASK; > > > + > > > + sb_base =3D sb_count ? ctxld->sb_paddr[curr_ctx] : 0; > > > + > > > + dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_AD= DR); > > > + dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT)= ; > > > + > > > + /* enable the context loader */ > > > + dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_= STATUS); > > > + > > > + ctxld->in_use =3D true; > > > + > > > + /* > > > + * Toggle the current context to the alternate one so that an= y updates > > > + * in the modules' settings take place there. > > > + */ > > > + ctxld->current_ctx ^=3D 1; > > > + > > > + ctxld->ctx_size[ctxld->current_ctx][CTX_DB] =3D 0; > > > + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] =3D 0; > > > + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] =3D 0; > > > + > > > + return 0; > > > +} > > > + > > > +int dcss_ctxld_enable(struct dcss_ctxld *ctxld) > > > +{ > > > + unsigned long flags; > > > + > > > + spin_lock_irqsave(&ctxld->lock, flags); > > > + ctxld->armed =3D true; > > > + spin_unlock_irqrestore(&ctxld->lock, flags); > > > + > > > + return 0; > > > +} > > > + > > > +void dcss_ctxld_kick(struct dcss_ctxld *ctxld) > > > +{ > > > + unsigned long flags; > > > + > > > + spin_lock_irqsave(&ctxld->lock, flags); > > > + if (ctxld->armed && !ctxld->in_use) { > > > + ctxld->armed =3D false; > > > + __dcss_ctxld_enable(ctxld); > > > + } > > > + spin_unlock_irqrestore(&ctxld->lock, flags); > > > +} > > > + > > > +void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, = u32 val, > > > + u32 reg_ofs) > > > +{ > > > + int curr_ctx =3D ctxld->current_ctx; > > > + struct dcss_ctxld_item *ctx[] =3D { > > > + [CTX_DB] =3D ctxld->db[curr_ctx], > > > + [CTX_SB_HP] =3D ctxld->sb_hp[curr_ctx], > > > + [CTX_SB_LP] =3D ctxld->sb_lp[curr_ctx] > > > + }; > > > + int item_idx =3D ctxld->ctx_size[curr_ctx][ctx_id]; > > > + > > > + if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) { > > > + WARN_ON(1); > > > + return; > > > + } > > > + > > > + ctx[ctx_id][item_idx].val =3D val; > > > + ctx[ctx_id][item_idx].ofs =3D reg_ofs; > > > + ctxld->ctx_size[curr_ctx][ctx_id] +=3D 1; > > > +} > > > + > > > +void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id, > > > + u32 val, u32 reg_ofs) > > > +{ > > > + unsigned long flags; > > > + > > > + spin_lock_irqsave(&ctxld->lock, flags); > > > + dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs); > > > + spin_unlock_irqrestore(&ctxld->lock, flags); > > > +} > > > + > > > +bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld) > > > +{ > > > + return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] =3D=3D 0 &= & > > > + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] =3D=3D= 0 && > > > + ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] =3D=3D= 0; > > > +} > > > + > > > +int dcss_ctxld_resume(struct dcss_ctxld *ctxld) > > > +{ > > > + dcss_ctxld_hw_cfg(ctxld); > > > + > > > + if (!ctxld->irq_en) { > > > + enable_irq(ctxld->irq); > > > + ctxld->irq_en =3D true; > > > + } > > > + > > > + return 0; > > > +} > > > + > > > +int dcss_ctxld_suspend(struct dcss_ctxld *ctxld) > > > +{ > > > + int ret =3D 0; > > > + int wait_time_ms =3D 0; > > > + unsigned long flags; > > > + > > > + dcss_ctxld_kick(ctxld); > > > + > > > + while (ctxld->in_use && wait_time_ms < 500) { > > > + msleep(20); > > > + wait_time_ms +=3D 20; > > > + } > > > + > > > + if (wait_time_ms > 500) > > > + return -ETIMEDOUT; > > > + > > > + spin_lock_irqsave(&ctxld->lock, flags); > > > + > > > + if (ctxld->irq_en) { > > > + disable_irq_nosync(ctxld->irq); > > > + ctxld->irq_en =3D false; > > > + } > > > + > > > + /* reset context region and sizes */ > > > + ctxld->current_ctx =3D 0; > > > + ctxld->ctx_size[0][CTX_DB] =3D 0; > > > + ctxld->ctx_size[0][CTX_SB_HP] =3D 0; > > > + ctxld->ctx_size[0][CTX_SB_LP] =3D 0; > > > + > > > + spin_unlock_irqrestore(&ctxld->lock, flags); > > > + > > > + return ret; > > > +} > > > + > > > +void dcss_ctxld_register_dtg_disable_cb(struct dcss_ctxld *ctxld, > > > + void (*cb)(void *), > > > + void *data) > > > +{ > > > + ctxld->dtg_disable_cb =3D cb; > > > + ctxld->dtg_disable_data =3D data; > > > +} > > > diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.c b/drivers/gpu/drm/im= x/dcss/dcss-dev.c > > > new file mode 100644 > > > index 00000000..265bf3c > > > --- /dev/null > > > +++ b/drivers/gpu/drm/imx/dcss/dcss-dev.c > > > @@ -0,0 +1,286 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * Copyright 2019 NXP. > > > + */ > > > + > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > + > > > +#include "dcss-dev.h" > > > + > > > +static void dcss_clocks_enable(struct dcss_dev *dcss) > > > +{ > > > + if (dcss->clks_on) > > > + return; > > > + > > > + clk_prepare_enable(dcss->axi_clk); > > > + clk_prepare_enable(dcss->apb_clk); > > > + clk_prepare_enable(dcss->rtrm_clk); > > > + clk_prepare_enable(dcss->dtrc_clk); > > > + clk_prepare_enable(dcss->pix_clk); > > > + > > > + dcss->clks_on =3D true; > > > +} > > > + > > > +static void dcss_clocks_disable(struct dcss_dev *dcss) > > > +{ > > > + if (!dcss->clks_on) > > > + return; > > > + > > > + clk_disable_unprepare(dcss->pix_clk); > > > + clk_disable_unprepare(dcss->dtrc_clk); > > > + clk_disable_unprepare(dcss->rtrm_clk); > > > + clk_disable_unprepare(dcss->apb_clk); > > > + clk_disable_unprepare(dcss->axi_clk); > > > + > > > + dcss->clks_on =3D false; > > > +} > > > + > > > +static int dcss_submodules_init(struct dcss_dev *dcss) > > > +{ > > > + int ret =3D 0; > > > + u32 base_addr =3D dcss->start_addr; > > > + const struct dcss_type_data *devtype =3D dcss->devtype; > > > + > > > + dcss_clocks_enable(dcss); > > > + > > > + ret =3D dcss_blkctl_init(dcss, base_addr + devtype->blkctl_of= s); > > > + if (ret) > > > + return ret; > > > + > > > + ret =3D dcss_ctxld_init(dcss, base_addr + devtype->ctxld_ofs)= ; > > > + if (ret) > > > + goto ctxld_err; > > > + > > > + ret =3D dcss_dtg_init(dcss, base_addr + devtype->dtg_ofs); > > > + if (ret) > > > + goto dtg_err; > > > + > > > + ret =3D dcss_ss_init(dcss, base_addr + devtype->ss_ofs); > > > + if (ret) > > > + goto ss_err; > > > + > > > + ret =3D dcss_dpr_init(dcss, base_addr + devtype->dpr_ofs); > > > + if (ret) > > > + goto dpr_err; > > > + > > > + ret =3D dcss_scaler_init(dcss, base_addr + devtype->scaler_of= s); > > > + if (ret) > > > + goto scaler_err; > > > + > > > + return 0; > > > + > > > +scaler_err: > > > + dcss_dpr_exit(dcss->dpr); > > > + > > > +dpr_err: > > > + dcss_ss_exit(dcss->ss); > > > + > > > +ss_err: > > > + dcss_dtg_exit(dcss->dtg); > > > + > > > +dtg_err: > > > + dcss_ctxld_exit(dcss->ctxld); > > > + > > > +ctxld_err: > > > + dcss_blkctl_exit(dcss->blkctl); > > > + > > > + dcss_clocks_disable(dcss); > > > + > > > + return ret; > > > +} > > > + > > > +static void dcss_submodules_stop(struct dcss_dev *dcss) > > > +{ > > > + dcss_clocks_enable(dcss); > > > + dcss_scaler_exit(dcss->scaler); > > > + dcss_dpr_exit(dcss->dpr); > > > + dcss_ss_exit(dcss->ss); > > > + dcss_dtg_exit(dcss->dtg); > > > + dcss_ctxld_exit(dcss->ctxld); > > > + dcss_blkctl_exit(dcss->blkctl); > > > + dcss_clocks_disable(dcss); > > > +} > > > + > > > +static int dcss_clks_init(struct dcss_dev *dcss) > > > +{ > > > + int i; > > > + struct { > > > + const char *id; > > > + struct clk **clk; > > > + } clks[] =3D { > > > + {"apb", &dcss->apb_clk}, > > > + {"axi", &dcss->axi_clk}, > > > + {"pix", &dcss->pix_clk}, > > > + {"rtrm", &dcss->rtrm_clk}, > > > + {"dtrc", &dcss->dtrc_clk}, > > > + }; > > > + > > > + for (i =3D 0; i < ARRAY_SIZE(clks); i++) { > > > + *clks[i].clk =3D devm_clk_get(dcss->dev, clks[i].id); > > > + if (IS_ERR(*clks[i].clk)) { > > > + dev_err(dcss->dev, "failed to get %s clock\n"= , > > > + clks[i].id); > > > + return PTR_ERR(*clks[i].clk); > > > + } > > > + } > > > + > > > + dcss->clks_on =3D false; > > > + > > > + return 0; > > > +} > > > + > > > +static void dcss_clks_release(struct dcss_dev *dcss) > > > +{ > > > + devm_clk_put(dcss->dev, dcss->dtrc_clk); > > > + devm_clk_put(dcss->dev, dcss->rtrm_clk); > > > + devm_clk_put(dcss->dev, dcss->pix_clk); > > > + devm_clk_put(dcss->dev, dcss->axi_clk); > > > + devm_clk_put(dcss->dev, dcss->apb_clk); > > > +} > > > + > > > +struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_outpu= t) > > > +{ > > > + struct platform_device *pdev =3D to_platform_device(dev); > > > + int ret; > > > + struct resource *res; > > > + struct dcss_dev *dcss; > > > + const struct dcss_type_data *devtype; > > > + > > > + devtype =3D of_device_get_match_data(dev); > > > + if (!devtype) { > > > + dev_err(dev, "no device match found\n"); > > > + return ERR_PTR(-ENODEV); > > > + } > > > + > > > + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); > > > + if (!res) { > > > + dev_err(dev, "cannot get memory resource\n"); > > > + return ERR_PTR(-EINVAL); > > > + } > > > + > > > + dcss =3D devm_kzalloc(dev, sizeof(struct dcss_dev), GFP_KERNE= L); > > > + if (!dcss) > > > + return ERR_PTR(-ENOMEM); > > > + > > > + dcss->dev =3D dev; > > > + dcss->devtype =3D devtype; > > > + dcss->hdmi_output =3D hdmi_output; > > > + > > > + ret =3D dcss_clks_init(dcss); > > > + if (ret) { > > > + dev_err(dev, "clocks initialization failed\n"); > > > + goto err; > > > + } > > > + > > > + dcss->of_port =3D of_graph_get_port_by_id(dev->of_node, 0); > > > + if (!dcss->of_port) { > > > + dev_err(dev, "no port@0 node in %s\n", dev->of_node->= full_name); > > > + ret =3D -ENODEV; > > > + goto clks_err; > > > + } > > > + > > > + dcss->start_addr =3D res->start; > > > + > > > + ret =3D dcss_submodules_init(dcss); > > > + if (ret) { > > > + dev_err(dev, "submodules initialization failed\n"); > > > + goto clks_err; > > > + } > > > + > > > + pm_runtime_enable(dev); > > > + > > > + return dcss; > > > + > > > +clks_err: > > > + dcss_clks_release(dcss); > > > + > > > +err: > > > + devm_kfree(dcss->dev, dcss); > > > + > > > + return ERR_PTR(ret); > > > +} > > > + > > > +void dcss_dev_destroy(struct dcss_dev *dcss) > > > +{ > > > + pm_runtime_disable(dcss->dev); > > > + > > > + dcss_submodules_stop(dcss); > > > + > > > + dcss_clks_release(dcss); > > > + > > > + devm_kfree(dcss->dev, dcss); > > > +} > > > + > > > +#ifdef CONFIG_PM_SLEEP > > > +int dcss_dev_suspend(struct device *dev) > > > +{ > > > + struct dcss_dev *dcss =3D dcss_drv_dev_to_dcss(dev); > > > + int ret; > > > + > > > + drm_mode_config_helper_suspend(dcss_drv_dev_to_drm(dev)); > > > + > > > + if (pm_runtime_suspended(dev)) > > > + return 0; > > > + > > > + ret =3D dcss_ctxld_suspend(dcss->ctxld); > > > + if (ret) > > > + return ret; > > > + > > > + dcss_clocks_disable(dcss); > > > + > > > + return 0; > > > +} > > > + > > > +int dcss_dev_resume(struct device *dev) > > > +{ > > > + struct dcss_dev *dcss =3D dcss_drv_dev_to_dcss(dev); > > > + > > > + if (pm_runtime_suspended(dev)) { > > > + drm_mode_config_helper_resume(dcss_drv_dev_to_drm(dev= )); > > > + return 0; > > > + } > > > + > > > + dcss_clocks_enable(dcss); > > > + > > > + dcss_blkctl_cfg(dcss->blkctl); > > > + > > > + dcss_ctxld_resume(dcss->ctxld); > > > + > > > + drm_mode_config_helper_resume(dcss_drv_dev_to_drm(dev)); > > > + > > > + return 0; > > > +} > > > +#endif /* CONFIG_PM_SLEEP */ > > > + > > > +#ifdef CONFIG_PM > > > +int dcss_dev_runtime_suspend(struct device *dev) > > > +{ > > > + struct dcss_dev *dcss =3D dcss_drv_dev_to_dcss(dev); > > > + int ret; > > > + > > > + ret =3D dcss_ctxld_suspend(dcss->ctxld); > > > + if (ret) > > > + return ret; > > > + > > > + dcss_clocks_disable(dcss); > > > + > > > + return 0; > > > +} > > > + > > > +int dcss_dev_runtime_resume(struct device *dev) > > > +{ > > > + struct dcss_dev *dcss =3D dcss_drv_dev_to_dcss(dev); > > > + > > > + dcss_clocks_enable(dcss); > > > + > > > + dcss_blkctl_cfg(dcss->blkctl); > > > + > > > + dcss_ctxld_resume(dcss->ctxld); > > > + > > > + return 0; > > > +} > > > +#endif /* CONFIG_PM */ > > > diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.h b/drivers/gpu/drm/im= x/dcss/dcss-dev.h > > > new file mode 100644 > > > index 00000000..15c5de3 > > > --- /dev/null > > > +++ b/drivers/gpu/drm/imx/dcss/dcss-dev.h > > > @@ -0,0 +1,195 @@ > > > +/* SPDX-License-Identifier: GPL-2.0 */ > > > +/* > > > + * Copyright 2019 NXP. > > > + */ > > > + > > > +#ifndef __DCSS_PRV_H__ > > > +#define __DCSS_PRV_H__ > > > + > > > +#include > > > +#include > > > +#include