From nobody Fri Oct 3 12:12:48 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E51F2E9759 for ; Tue, 2 Sep 2025 08:34:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756802061; cv=none; b=eoAhFLjf6THtE/rWIAyuGTaoXG111UaJUbSg9OBdQvTc4VlLwteyvpOOkQGSuD86jg94dj4GaOoWbeqx3ck3FqvX9rQwF87S+sgvnCYHsJtk3Nvihj9U7Sw2DdyuZBZdzZqAEAm/k9Av9GuPd3UXafExMpsNFSUEaMooh4BdFIw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756802061; c=relaxed/simple; bh=PYnVTOPYjHAB+rr1Ybb2vBoDMtO6tDXn5T1miNMp3sU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ClhlDXF+ZUh50Djh0gJpcwFA7xSY+HpMfJKRLeQ2fp/J2MGMKWggrF7w6Td8C7naalc5xbz55rzvT061jFqsM+7AHl6duxw5mxIMLZTJuFPjn2XHRBjq5KzbUI41Aov/Bg2tlj4j4sXyr8AdB0z6YOfJsFka5+YdJsYCURayApQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bkU68Fes; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bkU68Fes" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 26417C4CEF7; Tue, 2 Sep 2025 08:34:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1756802061; bh=PYnVTOPYjHAB+rr1Ybb2vBoDMtO6tDXn5T1miNMp3sU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=bkU68FesY4AURN/Z1sZ96P6sYCcunBj5g7onNnWkhKwQN+x+7TGXbYhg5et75YzFq UWBLlyz0QNiHURlfHyNoZ37Ttvs+qkjXR5nEzWdV7USkqgtysm4ovqqbHdVFnywAah jUDON8KWs+WS1NQuG4TPTimvC4jDw9SmIlBeoOrRGyBvsOplBoK8lVnH5qABRBCidz TENbs04fkgKuc7BhrnNTcgwpxBp+RTmid3Nf0pvjgtaSSLCKDN+aNVo8GBnrVNwFzT /y95DYDXZ8g7IhYXyGqT32Stdo7Imtngi5q5ckP2FRm3bC0nyyHPn88WIAymXJaDf2 MerRqF2Klu4Ag== From: Maxime Ripard Date: Tue, 02 Sep 2025 10:32:55 +0200 Subject: [PATCH 27/29] drm/tidss: Implement readout support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250902-drm-state-readout-v1-27-14ad5315da3f@kernel.org> References: <20250902-drm-state-readout-v1-0-14ad5315da3f@kernel.org> In-Reply-To: <20250902-drm-state-readout-v1-0-14ad5315da3f@kernel.org> To: Maarten Lankhorst , Thomas Zimmermann , David Airlie , Simona Vetter , Andrzej Hajda , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Jyri Sarha , Tomi Valkeinen Cc: Devarsh Thakkar , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Maxime Ripard X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=19395; i=mripard@kernel.org; h=from:subject:message-id; bh=PYnVTOPYjHAB+rr1Ybb2vBoDMtO6tDXn5T1miNMp3sU=; b=owGbwMvMwCmsHn9OcpHtvjLG02pJDBnbVh/YXrhLvWnai5wJin2JUtsvpjLOspl231tv+umHT sy7f9137JjKwiDMySArpsjyRCbs9PL2xVUO9it/wMxhZQIZwsDFKQATeXWcseF48Yk7nlkLezJX rXCyMPeVMtL4KKi0IHZB3OmnbtxZzvdLf7wx5PivUrFh95fTpl7X7zI2XAp6Ysgr/FJI9EBAitK Nlk9zn/Aov1u81nr+eyE1uf9NalN5W64Hr39RLn4zom73AdV2AA== X-Developer-Key: i=mripard@kernel.org; a=openpgp; fpr=BE5675C37E818C8B5764241C254BCFC56BF6CE8D With the hardware readout infrastructure now in place in the KMS framework, we can provide it for the tidss driver. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tidss/tidss_crtc.c | 218 ++++++++++++++++++++++++++++++++= +++- drivers/gpu/drm/tidss/tidss_dispc.c | 48 -------- drivers/gpu/drm/tidss/tidss_kms.c | 3 +- drivers/gpu/drm/tidss/tidss_plane.c | 194 +++++++++++++++++++++++++++++++- 4 files changed, 409 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tid= ss_crtc.c index 8fcc6a2f94770ae825eeb2a3b09856a2bf2d6a1e..454c4db92942c6c781440aff78c= 755e386b2edf3 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.c +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -2,18 +2,22 @@ /* * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ =20 +#include + #include #include +#include #include #include #include =20 #include "tidss_crtc.h" #include "tidss_dispc.h" +#include "tidss_dispc_regs.h" #include "tidss_drv.h" #include "tidss_irq.h" #include "tidss_plane.h" =20 /* Page flip and frame done IRQs */ @@ -350,24 +354,229 @@ static void tidss_crtc_destroy_state(struct drm_crtc= *crtc, =20 __drm_atomic_helper_crtc_destroy_state(&tstate->base); kfree(tstate); } =20 -static void tidss_crtc_reset(struct drm_crtc *crtc) +static unsigned long calc_pixel_clock_hz(unsigned int htotal, + unsigned int vtotal, + unsigned int refresh, + unsigned int freq_div) { + unsigned long rate =3D (unsigned long)htotal * vtotal * refresh; + + return (rate * 1000) / freq_div; +} + +static const unsigned int refresh_tries[] =3D {30, 50, 60}; +static const unsigned int refresh_factors_tries[] =3D {1000, 1001}; + +static unsigned int tidss_find_closest_refresh_rate_from_clk(struct drm_de= vice *dev, + struct clk *clk, + unsigned int htotal, + unsigned int vtotal, + unsigned long *pixel_clock_hz) +{ + unsigned long actual_clk_rate =3D clk_get_rate(clk); + unsigned long best_clk_rate =3D 0; + unsigned long best_rate_diff =3D ULONG_MAX; + unsigned int best_refresh =3D 0; + unsigned int i, j; + + drm_dbg(dev, "Actual clock rate is %lu\n", actual_clk_rate); + + for (i =3D 0; i < ARRAY_SIZE(refresh_tries); i++) { + for (j =3D 0; j < ARRAY_SIZE(refresh_factors_tries); j++) { + unsigned int try_refresh =3D refresh_tries[i]; + unsigned int try_factor =3D refresh_factors_tries[j]; + unsigned long try_clk_rate =3D calc_pixel_clock_hz(htotal, + vtotal, + try_refresh, + try_factor); + unsigned long diff; + + drm_dbg(dev, "Evaluating refresh %u, factor %u, rate %lu\n", + try_refresh, try_factor, try_clk_rate); + + if (try_clk_rate =3D=3D actual_clk_rate) { + drm_dbg(dev, "Found exact match. Stopping.\n"); + best_refresh =3D try_refresh; + best_clk_rate =3D try_clk_rate; + goto out; + } + + + diff =3D abs_diff(actual_clk_rate, try_clk_rate); + if (diff < best_rate_diff) { + drm_dbg(dev, "Found new candidate. Difference is %lu\n", diff); + best_refresh =3D try_refresh; + best_clk_rate =3D try_clk_rate; + best_rate_diff =3D diff; + } + } + } + +out: + drm_dbg(dev, "Best candidate is %u Hz, pixel clock rate %lu Hz", best_ref= resh, best_clk_rate); + + if (pixel_clock_hz) + *pixel_clock_hz =3D best_clk_rate; + + return best_refresh; +} + +static int tidss_crtc_readout_mode(struct dispc_device *dispc, + struct tidss_crtc *tcrtc, + struct drm_display_mode *mode) +{ + struct tidss_device *tidss =3D dispc->tidss; + struct drm_device *dev =3D &tidss->ddev; + unsigned long pixel_clock; + unsigned int refresh; + u16 hdisplay, hfp, hsw, hbp; + u16 vdisplay, vfp, vsw, vbp; + u32 vp =3D tcrtc->hw_videoport; + u32 val; + + val =3D dispc_vp_read(dispc, vp, DISPC_VP_SIZE_SCREEN); + hdisplay =3D FIELD_GET(DISPC_VP_SIZE_SCREEN_HDISPLAY_MASK, val) + 1; + vdisplay =3D FIELD_GET(DISPC_VP_SIZE_SCREEN_VDISPLAY_MASK, val) + 1; + + mode->hdisplay =3D hdisplay; + mode->vdisplay =3D vdisplay; + + val =3D dispc_vp_read(dispc, vp, DISPC_VP_TIMING_H); + hsw =3D FIELD_GET(DISPC_VP_TIMING_H_SYNC_PULSE_MASK, val) + 1; + hfp =3D FIELD_GET(DISPC_VP_TIMING_H_FRONT_PORCH_MASK, val) + 1; + hbp =3D FIELD_GET(DISPC_VP_TIMING_H_BACK_PORCH_MASK, val) + 1; + + mode->hsync_start =3D hdisplay + hfp; + mode->hsync_end =3D hdisplay + hfp + hsw; + mode->htotal =3D hdisplay + hfp + hsw + hbp; + + val =3D dispc_vp_read(dispc, vp, DISPC_VP_TIMING_V); + vsw =3D FIELD_GET(DISPC_VP_TIMING_V_SYNC_PULSE_MASK, val) + 1; + vfp =3D FIELD_GET(DISPC_VP_TIMING_V_FRONT_PORCH_MASK, val); + vbp =3D FIELD_GET(DISPC_VP_TIMING_V_BACK_PORCH_MASK, val); + + mode->vsync_start =3D vdisplay + vfp; + mode->vsync_end =3D vdisplay + vfp + vsw; + mode->vtotal =3D vdisplay + vfp + vsw + vbp; + + refresh =3D tidss_find_closest_refresh_rate_from_clk(dev, + dispc->vp_clk[vp], + mode->htotal, + mode->vtotal, + &pixel_clock); + if (!refresh) + return -EINVAL; + + mode->clock =3D pixel_clock / 1000; + + val =3D dispc_vp_read(dispc, vp, DISPC_VP_POL_FREQ); + if (FIELD_GET(DISPC_VP_POL_FREQ_IVS_MASK, val)) + mode->flags |=3D DRM_MODE_FLAG_NVSYNC; + else + mode->flags |=3D DRM_MODE_FLAG_PVSYNC; + + if (FIELD_GET(DISPC_VP_POL_FREQ_IHS_MASK, val)) + mode->flags |=3D DRM_MODE_FLAG_NHSYNC; + else + mode->flags |=3D DRM_MODE_FLAG_PHSYNC; + + mode->type |=3D DRM_MODE_TYPE_DRIVER; + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + return 0; +} + +static struct drm_crtc_state * +tidss_crtc_readout_state(struct drm_crtc *crtc) +{ + struct drm_device *ddev =3D crtc->dev; + struct tidss_device *tidss =3D to_tidss(ddev); + struct dispc_device *dispc =3D tidss->dispc; struct tidss_crtc_state *tstate; + struct tidss_crtc *tcrtc =3D + to_tidss_crtc(crtc); + struct drm_display_mode mode; + u32 val; + int ret; =20 if (crtc->state) tidss_crtc_destroy_state(crtc, crtc->state); =20 tstate =3D kzalloc(sizeof(*tstate), GFP_KERNEL); if (!tstate) { - crtc->state =3D NULL; - return; + return ERR_PTR(-ENOMEM); } =20 __drm_atomic_helper_crtc_reset(crtc, &tstate->base); + + tidss_runtime_get(tidss); + + val =3D dispc_vp_read(dispc, tcrtc->hw_videoport, DISPC_VP_CONTROL); + if (!FIELD_GET(DISPC_VP_CONTROL_ENABLE_MASK, val)) + goto out; + + /* + * The display is active, we need to enable our clock to have + * proper reference count. + */ + WARN_ON(dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport)); + + tstate->base.active =3D 1; + tstate->base.enable =3D 1; + + ret =3D tidss_crtc_readout_mode(dispc, tcrtc, &mode); + if (ret) + goto err_runtime_put; + + ret =3D drm_atomic_set_mode_for_crtc(&tstate->base, &mode); + if (WARN_ON(ret)) + goto err_runtime_put; + + drm_mode_copy(&tstate->base.adjusted_mode, &mode); + + val =3D dispc_vp_read(dispc, tcrtc->hw_videoport, DISPC_VP_POL_FREQ); + if (FIELD_GET(DISPC_VP_POL_FREQ_IPC_MASK, val)) + tstate->bus_flags |=3D DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; + + if (FIELD_GET(DISPC_VP_POL_FREQ_IEO_MASK, val)) + tstate->bus_flags |=3D DRM_BUS_FLAG_DE_LOW; + + if (FIELD_GET(DISPC_VP_POL_FREQ_RF_MASK, val)) + tstate->bus_flags |=3D DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; + + /* + * The active connectors and planes will be filled by their + * respective readout callbacks. + */ + +out: + tidss_runtime_put(tidss); + return &tstate->base; + +err_runtime_put: + tidss_runtime_put(tidss); + kfree(tstate); + return ERR_PTR(ret); +} + +static bool tidss_crtc_compare_state(struct drm_crtc *crtc, + struct drm_printer *p, + struct drm_crtc_state *expected, + struct drm_crtc_state *actual) +{ + struct tidss_crtc_state *t_expected =3D to_tidss_crtc_state(expected); + struct tidss_crtc_state *t_actual =3D to_tidss_crtc_state(actual); + int ret =3D drm_atomic_helper_crtc_compare_state(crtc, p, expected, actua= l); + + STATE_CHECK_U32_X(ret, p, crtc->name, t_expected, t_actual, bus_format); + STATE_CHECK_U32_X(ret, p, crtc->name, t_expected, t_actual, bus_flags); + + return ret; } =20 static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *= crtc) { struct tidss_crtc_state *state, *current_state; @@ -398,14 +607,15 @@ static void tidss_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); kfree(tcrtc); } =20 static const struct drm_crtc_funcs tidss_crtc_funcs =3D { - .reset =3D tidss_crtc_reset, .destroy =3D tidss_crtc_destroy, .set_config =3D drm_atomic_helper_set_config, .page_flip =3D drm_atomic_helper_page_flip, + .atomic_readout_state =3D tidss_crtc_readout_state, + .atomic_compare_state =3D tidss_crtc_compare_state, .atomic_duplicate_state =3D tidss_crtc_duplicate_state, .atomic_destroy_state =3D tidss_crtc_destroy_state, .enable_vblank =3D tidss_crtc_enable_vblank, .disable_vblank =3D tidss_crtc_disable_vblank, }; diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/ti= dss_dispc.c index 18b6beddfe51f9b5c164481ee2ef0fa289e63318..e7f6f047574f5b520b00195c2b1= 4d98224db6f19 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -2916,51 +2916,10 @@ static void dispc_init_errata(struct dispc_device *= dispc) dispc->errata.i2000 =3D true; dev_info(dispc->dev, "WA for erratum i2000: YUV formats disabled\n"); } } =20 -/* - * K2G display controller does not support soft reset, so we do a basic ma= nual - * reset here: make sure the IRQs are masked and VPs are disabled. - */ -static void dispc_softreset_k2g(struct dispc_device *dispc) -{ - unsigned long flags; - - spin_lock_irqsave(&dispc->tidss->irq_lock, flags); - dispc_set_irqenable(dispc, 0); - dispc_read_and_clear_irqstatus(dispc); - spin_unlock_irqrestore(&dispc->tidss->irq_lock, flags); - - for (unsigned int vp_idx =3D 0; vp_idx < dispc->feat->num_vps; ++vp_idx) - VP_REG_FLD_MOD(dispc, vp_idx, DISPC_VP_CONTROL, 0, - DISPC_VP_CONTROL_ENABLE_MASK); -} - -static int dispc_softreset(struct dispc_device *dispc) -{ - u32 val; - int ret; - - if (dispc->feat->subrev =3D=3D DISPC_K2G) { - dispc_softreset_k2g(dispc); - return 0; - } - - /* Soft reset */ - REG_FLD_MOD(dispc, DSS_SYSCONFIG, 1, DSS_SYSCONFIG_SOFTRESET_MASK); - /* Wait for reset to complete */ - ret =3D readl_poll_timeout(dispc->base_common + DSS_SYSSTATUS, - val, val & 1, 100, 5000); - if (ret) { - dev_err(dispc->dev, "failed to reset dispc\n"); - return ret; - } - - return 0; -} - static int dispc_init_hw(struct dispc_device *dispc) { struct device *dev =3D dispc->dev; int ret; =20 @@ -2974,26 +2933,19 @@ static int dispc_init_hw(struct dispc_device *dispc) if (ret) { dev_err(dev, "Failed to enable DSS fclk\n"); goto err_runtime_suspend; } =20 - ret =3D dispc_softreset(dispc); - if (ret) - goto err_clk_disable; - clk_disable_unprepare(dispc->fclk); ret =3D pm_runtime_set_suspended(dev); if (ret) { dev_err(dev, "Failed to set DSS PM to suspended\n"); return ret; } =20 return 0; =20 -err_clk_disable: - clk_disable_unprepare(dispc->fclk); - err_runtime_suspend: ret =3D pm_runtime_set_suspended(dev); if (ret) { dev_err(dev, "Failed to set DSS PM to suspended\n"); return ret; diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tids= s_kms.c index 86eb5d97410bedced57129c2bbcd35f1719424c2..38c90027c158a392f96012f8882= 4ead091332fb7 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -37,11 +37,12 @@ static void tidss_atomic_commit_tail(struct drm_atomic_= state *old_state) =20 tidss_runtime_put(tidss); } =20 static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = =3D { - .atomic_commit_tail =3D tidss_atomic_commit_tail, + .atomic_commit_tail =3D tidss_atomic_commit_tail, + .atomic_reset =3D drm_atomic_helper_readout_state, }; =20 static int tidss_atomic_check(struct drm_device *ddev, struct drm_atomic_state *state) { diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/ti= dss_plane.c index bd10bc1b9961571e6c6dee26698149fc9dd135b0..037c21354dd170511868baca249= 60ff9295dbea5 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -10,13 +10,15 @@ #include #include #include #include #include +#include =20 #include "tidss_crtc.h" #include "tidss_dispc.h" +#include "tidss_dispc_regs.h" #include "tidss_drv.h" #include "tidss_plane.h" =20 void tidss_plane_error_irq(struct drm_plane *plane, u64 irqstatus) { @@ -173,17 +175,207 @@ static const struct drm_plane_helper_funcs tidss_pri= mary_plane_helper_funcs =3D { .atomic_enable =3D tidss_plane_atomic_enable, .atomic_disable =3D tidss_plane_atomic_disable, .get_scanout_buffer =3D drm_fb_dma_get_scanout_buffer, }; =20 +static const struct drm_framebuffer_funcs tidss_plane_readout_fb_funcs =3D= { + .destroy =3D drm_gem_fb_destroy, +}; + +static struct drm_framebuffer *tidss_plane_readout_fb(struct drm_plane *pl= ane) +{ + struct drm_device *ddev =3D plane->dev; + struct tidss_device *tidss =3D to_tidss(ddev); + struct dispc_device *dispc =3D tidss->dispc; + struct tidss_plane *tplane =3D to_tidss_plane(plane); + const struct drm_format_info *info; + struct drm_framebuffer *fb; + u32 fourcc, val; + int ret; + + fb =3D kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) + return ERR_PTR(-ENOMEM); + + fb->dev =3D plane->dev; + + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_ATTRIBUTES); + fourcc =3D + dispc_plane_find_fourcc_by_dss_code(FIELD_GET(DISPC_VID_ATTRIBUTES_FORMA= T_MASK, + val)); + if (!fourcc) { + ret =3D -EINVAL; + goto err_free_fb; + } + + info =3D drm_format_info(fourcc); + if (!info) { + ret =3D -EINVAL; + goto err_free_fb; + } + + // TODO: Figure out YUV and multiplanar formats + if (info->is_yuv) { + ret =3D -EINVAL; + goto err_free_fb; + } + + fb->format =3D info; + + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_PICTURE_SIZE= ); + fb->width =3D FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEX_MASK, val) + 1; + fb->height =3D FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEY_MASK, val) + 1; + + // TODO: Figure that out. + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_ROW_INC); + if (val !=3D 1) { + ret =3D -EINVAL; + goto err_free_fb; + } + + fb->pitches[0] =3D fb->width * (drm_format_info_bpp(info, 0) / 8); + + // TODO: Figure out the offsets + fb->offsets[0] =3D 0; + + ret =3D drm_framebuffer_init(plane->dev, fb, &tidss_plane_readout_fb_func= s); + if (ret) { + kfree(fb); + return ERR_PTR(ret); + } + + return fb; + +err_free_fb: + kfree(fb); + return ERR_PTR(ret); +} + +static struct drm_crtc *tidss_plane_readout_crtc(struct drm_plane *plane) +{ + struct drm_device *dev =3D plane->dev; + + if (dev->num_crtcs !=3D 1) + return ERR_PTR(-EINVAL); + + return list_first_entry(&dev->mode_config.crtc_list, struct drm_crtc, hea= d); +} + +static struct drm_plane_state *tidss_plane_atomic_readout_state(struct drm= _plane *plane, + struct drm_atomic_state *state) +{ + struct drm_device *ddev =3D plane->dev; + struct tidss_device *tidss =3D to_tidss(ddev); + struct dispc_device *dispc =3D tidss->dispc; + struct tidss_plane *tplane =3D to_tidss_plane(plane); + struct drm_plane_state *plane_state; + struct drm_crtc_state *crtc_state; + struct drm_framebuffer *fb; + struct drm_crtc *crtc; + bool lite =3D dispc->feat->vid_info[tplane->hw_plane_id].is_lite; + u16 in_w, in_h; + u32 val; + int ret; + + if (plane->state) + drm_atomic_helper_plane_destroy_state(plane, plane->state); + + plane_state =3D kzalloc(sizeof(*plane_state), GFP_KERNEL); + if (!plane_state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_plane_state_reset(plane_state, plane); + + tidss_runtime_get(tidss); + + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_ATTRIBUTES); + if (!FIELD_GET(DISPC_VID_ATTRIBUTES_ENABLE_MASK, val)) { + goto out; + } + + fb =3D tidss_plane_readout_fb(plane); + if (IS_ERR(fb)) { + ret =3D PTR_ERR(fb); + goto err_runtime_pm; + } + + crtc =3D tidss_plane_readout_crtc(plane); + if (IS_ERR(crtc)) { + ret =3D PTR_ERR(crtc); + goto err_runtime_pm; + } + + plane_state->fb =3D fb; + plane_state->crtc =3D crtc; + plane_state->visible =3D true; + + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_PICTURE_SIZE= ); + in_w =3D FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEX_MASK, val) + 1; + in_h =3D FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEY_MASK, val) + 1; + plane_state->src_w =3D in_w << 16; + plane_state->src_h =3D in_h << 16; + + if (!lite) { + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_SIZE); + plane_state->crtc_w =3D FIELD_GET(DISPC_VID_SIZE_SIZEX_MASK, val) + 1; + plane_state->crtc_h =3D FIELD_GET(DISPC_VID_SIZE_SIZEY_MASK, val) + 1; + } else { + plane_state->crtc_w =3D in_w; + plane_state->crtc_h =3D in_h; + } + + // TODO: Handle crtc_x/crtc_x/src_x/src_y + // crtc_x/crtc_y are handled by DISPC_OVR_ATTRIBUTES / OVR1_DSS_ATTRIBUTES + + // TODO: Handle zpos, see DISPC_OVR_ATTRIBUTES / OVR1_DSS_ATTRIBUTES + + plane_state->src.x1 =3D 0; + plane_state->src.x2 =3D plane_state->src_w; + plane_state->src.y1 =3D 0; + plane_state->src.y2 =3D plane_state->src_h; + plane_state->dst.x1 =3D 0; + plane_state->dst.x2 =3D plane_state->crtc_w; + plane_state->dst.y1 =3D 0; + plane_state->dst.y2 =3D plane_state->crtc_h; + + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_GLOBAL_ALPHA= ); + plane_state->alpha =3D FIELD_GET(DISPC_VID_GLOBAL_ALPHA_GLOBALALPHA_MASK,= val) << 16; + + val =3D dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_ATTRIBUTES); + if (FIELD_GET(DISPC_VID_ATTRIBUTES_PREMULTIPLYALPHA_MASK, val)) + plane_state->pixel_blend_mode =3D DRM_MODE_BLEND_PREMULTI; + else + plane_state->pixel_blend_mode =3D DRM_MODE_BLEND_COVERAGE; + + // TODO: If YUV, handle color encoding and range + + crtc_state =3D drm_atomic_get_old_crtc_state(state, crtc); + if (!crtc_state) { + ret =3D -ENODEV; + goto err_runtime_pm; + } + + crtc_state->plane_mask |=3D drm_plane_mask(plane); + +out: + tidss_runtime_put(tidss); + return plane_state; + +err_runtime_pm: + tidss_runtime_put(tidss); + kfree(plane_state); + return ERR_PTR(ret); +} + static const struct drm_plane_funcs tidss_plane_funcs =3D { .update_plane =3D drm_atomic_helper_update_plane, .disable_plane =3D drm_atomic_helper_disable_plane, - .reset =3D drm_atomic_helper_plane_reset, .destroy =3D drm_plane_destroy, + .atomic_compare_state =3D drm_atomic_helper_plane_compare_state, .atomic_duplicate_state =3D drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state =3D drm_atomic_helper_plane_destroy_state, + .atomic_readout_state =3D tidss_plane_atomic_readout_state, }; =20 struct tidss_plane *tidss_plane_create(struct tidss_device *tidss, u32 hw_plane_id, u32 plane_type, u32 crtc_mask, const u32 *formats, --=20 2.50.1