[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <b7ed3209-b336-4f0b-84e5-8bd1c87cd74d@ideasonboard.com>
Date: Wed, 8 Oct 2025 15:44:06 +0300
From: Tomi Valkeinen <tomi.valkeinen@...asonboard.com>
To: Maxime Ripard <mripard@...nel.org>
Cc: Devarsh Thakkar <devarsht@...com>, dri-devel@...ts.freedesktop.org,
linux-kernel@...r.kernel.org,
Maarten Lankhorst <maarten.lankhorst@...ux.intel.com>,
Thomas Zimmermann <tzimmermann@...e.de>, David Airlie <airlied@...il.com>,
Simona Vetter <simona@...ll.ch>, Andrzej Hajda <andrzej.hajda@...el.com>,
Neil Armstrong <neil.armstrong@...aro.org>, Robert Foss <rfoss@...nel.org>,
Laurent Pinchart <Laurent.pinchart@...asonboard.com>,
Jonas Karlman <jonas@...boo.se>, Jernej Skrabec <jernej.skrabec@...il.com>,
Jyri Sarha <jyri.sarha@....fi>
Subject: Re: [PATCH 27/29] drm/tidss: Implement readout support
Hi,
On 02/09/2025 11:32, Maxime Ripard wrote:
> 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 <mripard@...nel.org>
> ---
> 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/tidss_crtc.c
> index 8fcc6a2f94770ae825eeb2a3b09856a2bf2d6a1e..454c4db92942c6c781440aff78c755e386b2edf3 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 <tomi.valkeinen@...com>
> */
>
> +#include <linux/clk.h>
> +
> #include <drm/drm_atomic.h>
> #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_atomic_uapi.h>
> #include <drm/drm_crtc.h>
> #include <drm/drm_gem_dma_helper.h>
> #include <drm/drm_vblank.h>
>
> #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"
>
> /* Page flip and frame done IRQs */
> @@ -350,24 +354,229 @@ static void tidss_crtc_destroy_state(struct drm_crtc *crtc,
>
> __drm_atomic_helper_crtc_destroy_state(&tstate->base);
> kfree(tstate);
> }
>
> -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 = (unsigned long)htotal * vtotal * refresh;
> +
> + return (rate * 1000) / freq_div;
> +}
> +
> +static const unsigned int refresh_tries[] = {30, 50, 60};
> +static const unsigned int refresh_factors_tries[] = {1000, 1001};
> +
> +static unsigned int tidss_find_closest_refresh_rate_from_clk(struct drm_device *dev,
> + struct clk *clk,
> + unsigned int htotal,
> + unsigned int vtotal,
> + unsigned long *pixel_clock_hz)
> +{
> + unsigned long actual_clk_rate = clk_get_rate(clk);
> + unsigned long best_clk_rate = 0;
> + unsigned long best_rate_diff = ULONG_MAX;
> + unsigned int best_refresh = 0;
> + unsigned int i, j;
> +
> + drm_dbg(dev, "Actual clock rate is %lu\n", actual_clk_rate);
> +
> + for (i = 0; i < ARRAY_SIZE(refresh_tries); i++) {
> + for (j = 0; j < ARRAY_SIZE(refresh_factors_tries); j++) {
> + unsigned int try_refresh = refresh_tries[i];
> + unsigned int try_factor = refresh_factors_tries[j];
> + unsigned long try_clk_rate = 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 == actual_clk_rate) {
> + drm_dbg(dev, "Found exact match. Stopping.\n");
> + best_refresh = try_refresh;
> + best_clk_rate = try_clk_rate;
> + goto out;
> + }
> +
> +
> + diff = 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 = try_refresh;
> + best_clk_rate = try_clk_rate;
> + best_rate_diff = diff;
> + }
> + }
> + }
> +
> +out:
> + drm_dbg(dev, "Best candidate is %u Hz, pixel clock rate %lu Hz", best_refresh, best_clk_rate);
> +
> + if (pixel_clock_hz)
> + *pixel_clock_hz = 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 = dispc->tidss;
> + struct drm_device *dev = &tidss->ddev;
> + unsigned long pixel_clock;
> + unsigned int refresh;
> + u16 hdisplay, hfp, hsw, hbp;
> + u16 vdisplay, vfp, vsw, vbp;
> + u32 vp = tcrtc->hw_videoport;
> + u32 val;
> +
> + val = dispc_vp_read(dispc, vp, DISPC_VP_SIZE_SCREEN);
> + hdisplay = FIELD_GET(DISPC_VP_SIZE_SCREEN_HDISPLAY_MASK, val) + 1;
> + vdisplay = FIELD_GET(DISPC_VP_SIZE_SCREEN_VDISPLAY_MASK, val) + 1;
> +
> + mode->hdisplay = hdisplay;
> + mode->vdisplay = vdisplay;
> +
> + val = dispc_vp_read(dispc, vp, DISPC_VP_TIMING_H);
> + hsw = FIELD_GET(DISPC_VP_TIMING_H_SYNC_PULSE_MASK, val) + 1;
> + hfp = FIELD_GET(DISPC_VP_TIMING_H_FRONT_PORCH_MASK, val) + 1;
> + hbp = FIELD_GET(DISPC_VP_TIMING_H_BACK_PORCH_MASK, val) + 1;
I don't like this. The idea has been that dispc.c is the controlling
file for the DSS DISPC registers, and I'd like to keep it that way.
Any reason why we couldn't add utility funcs to dispc.c that do the
readouts?
Tomi
> +
> + mode->hsync_start = hdisplay + hfp;
> + mode->hsync_end = hdisplay + hfp + hsw;
> + mode->htotal = hdisplay + hfp + hsw + hbp;
> +
> + val = dispc_vp_read(dispc, vp, DISPC_VP_TIMING_V);
> + vsw = FIELD_GET(DISPC_VP_TIMING_V_SYNC_PULSE_MASK, val) + 1;
> + vfp = FIELD_GET(DISPC_VP_TIMING_V_FRONT_PORCH_MASK, val);
> + vbp = FIELD_GET(DISPC_VP_TIMING_V_BACK_PORCH_MASK, val);
> +
> + mode->vsync_start = vdisplay + vfp;
> + mode->vsync_end = vdisplay + vfp + vsw;
> + mode->vtotal = vdisplay + vfp + vsw + vbp;
> +
> + refresh = tidss_find_closest_refresh_rate_from_clk(dev,
> + dispc->vp_clk[vp],
> + mode->htotal,
> + mode->vtotal,
> + &pixel_clock);
> + if (!refresh)
> + return -EINVAL;
> +
> + mode->clock = pixel_clock / 1000;
> +
> + val = dispc_vp_read(dispc, vp, DISPC_VP_POL_FREQ);
> + if (FIELD_GET(DISPC_VP_POL_FREQ_IVS_MASK, val))
> + mode->flags |= DRM_MODE_FLAG_NVSYNC;
> + else
> + mode->flags |= DRM_MODE_FLAG_PVSYNC;
> +
> + if (FIELD_GET(DISPC_VP_POL_FREQ_IHS_MASK, val))
> + mode->flags |= DRM_MODE_FLAG_NHSYNC;
> + else
> + mode->flags |= DRM_MODE_FLAG_PHSYNC;
> +
> + mode->type |= 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 = crtc->dev;
> + struct tidss_device *tidss = to_tidss(ddev);
> + struct dispc_device *dispc = tidss->dispc;
> struct tidss_crtc_state *tstate;
> + struct tidss_crtc *tcrtc =
> + to_tidss_crtc(crtc);
> + struct drm_display_mode mode;
> + u32 val;
> + int ret;
>
> if (crtc->state)
> tidss_crtc_destroy_state(crtc, crtc->state);
>
> tstate = kzalloc(sizeof(*tstate), GFP_KERNEL);
> if (!tstate) {
> - crtc->state = NULL;
> - return;
> + return ERR_PTR(-ENOMEM);
> }
>
> __drm_atomic_helper_crtc_reset(crtc, &tstate->base);
> +
> + tidss_runtime_get(tidss);
> +
> + val = 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 = 1;
> + tstate->base.enable = 1;
> +
> + ret = tidss_crtc_readout_mode(dispc, tcrtc, &mode);
> + if (ret)
> + goto err_runtime_put;
> +
> + ret = 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 = dispc_vp_read(dispc, tcrtc->hw_videoport, DISPC_VP_POL_FREQ);
> + if (FIELD_GET(DISPC_VP_POL_FREQ_IPC_MASK, val))
> + tstate->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
> +
> + if (FIELD_GET(DISPC_VP_POL_FREQ_IEO_MASK, val))
> + tstate->bus_flags |= DRM_BUS_FLAG_DE_LOW;
> +
> + if (FIELD_GET(DISPC_VP_POL_FREQ_RF_MASK, val))
> + tstate->bus_flags |= 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 = to_tidss_crtc_state(expected);
> + struct tidss_crtc_state *t_actual = to_tidss_crtc_state(actual);
> + int ret = drm_atomic_helper_crtc_compare_state(crtc, p, expected, actual);
> +
> + 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;
> }
>
> 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);
> }
>
> static const struct drm_crtc_funcs tidss_crtc_funcs = {
> - .reset = tidss_crtc_reset,
> .destroy = tidss_crtc_destroy,
> .set_config = drm_atomic_helper_set_config,
> .page_flip = drm_atomic_helper_page_flip,
> + .atomic_readout_state = tidss_crtc_readout_state,
> + .atomic_compare_state = tidss_crtc_compare_state,
> .atomic_duplicate_state = tidss_crtc_duplicate_state,
> .atomic_destroy_state = tidss_crtc_destroy_state,
> .enable_vblank = tidss_crtc_enable_vblank,
> .disable_vblank = tidss_crtc_disable_vblank,
> };
> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
> index 18b6beddfe51f9b5c164481ee2ef0fa289e63318..e7f6f047574f5b520b00195c2b14d98224db6f19 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 = true;
> dev_info(dispc->dev, "WA for erratum i2000: YUV formats disabled\n");
> }
> }
>
> -/*
> - * K2G display controller does not support soft reset, so we do a basic manual
> - * 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 = 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 == 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 = 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 = dispc->dev;
> int ret;
>
> @@ -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;
> }
>
> - ret = dispc_softreset(dispc);
> - if (ret)
> - goto err_clk_disable;
> -
> clk_disable_unprepare(dispc->fclk);
> ret = pm_runtime_set_suspended(dev);
> if (ret) {
> dev_err(dev, "Failed to set DSS PM to suspended\n");
> return ret;
> }
>
> return 0;
>
> -err_clk_disable:
> - clk_disable_unprepare(dispc->fclk);
> -
> err_runtime_suspend:
> ret = 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/tidss_kms.c
> index 86eb5d97410bedced57129c2bbcd35f1719424c2..38c90027c158a392f96012f88824ead091332fb7 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)
>
> tidss_runtime_put(tidss);
> }
>
> static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = {
> - .atomic_commit_tail = tidss_atomic_commit_tail,
> + .atomic_commit_tail = tidss_atomic_commit_tail,
> + .atomic_reset = drm_atomic_helper_readout_state,
> };
>
> 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/tidss_plane.c
> index bd10bc1b9961571e6c6dee26698149fc9dd135b0..037c21354dd170511868baca24960ff9295dbea5 100644
> --- a/drivers/gpu/drm/tidss/tidss_plane.c
> +++ b/drivers/gpu/drm/tidss/tidss_plane.c
> @@ -10,13 +10,15 @@
> #include <drm/drm_crtc.h>
> #include <drm/drm_fb_dma_helper.h>
> #include <drm/drm_fourcc.h>
> #include <drm/drm_framebuffer.h>
> #include <drm/drm_gem_atomic_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
>
> #include "tidss_crtc.h"
> #include "tidss_dispc.h"
> +#include "tidss_dispc_regs.h"
> #include "tidss_drv.h"
> #include "tidss_plane.h"
>
> void tidss_plane_error_irq(struct drm_plane *plane, u64 irqstatus)
> {
> @@ -173,17 +175,207 @@ static const struct drm_plane_helper_funcs tidss_primary_plane_helper_funcs = {
> .atomic_enable = tidss_plane_atomic_enable,
> .atomic_disable = tidss_plane_atomic_disable,
> .get_scanout_buffer = drm_fb_dma_get_scanout_buffer,
> };
>
> +static const struct drm_framebuffer_funcs tidss_plane_readout_fb_funcs = {
> + .destroy = drm_gem_fb_destroy,
> +};
> +
> +static struct drm_framebuffer *tidss_plane_readout_fb(struct drm_plane *plane)
> +{
> + struct drm_device *ddev = plane->dev;
> + struct tidss_device *tidss = to_tidss(ddev);
> + struct dispc_device *dispc = tidss->dispc;
> + struct tidss_plane *tplane = to_tidss_plane(plane);
> + const struct drm_format_info *info;
> + struct drm_framebuffer *fb;
> + u32 fourcc, val;
> + int ret;
> +
> + fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> + if (!fb)
> + return ERR_PTR(-ENOMEM);
> +
> + fb->dev = plane->dev;
> +
> + val = dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_ATTRIBUTES);
> + fourcc =
> + dispc_plane_find_fourcc_by_dss_code(FIELD_GET(DISPC_VID_ATTRIBUTES_FORMAT_MASK,
> + val));
> + if (!fourcc) {
> + ret = -EINVAL;
> + goto err_free_fb;
> + }
> +
> + info = drm_format_info(fourcc);
> + if (!info) {
> + ret = -EINVAL;
> + goto err_free_fb;
> + }
> +
> + // TODO: Figure out YUV and multiplanar formats
> + if (info->is_yuv) {
> + ret = -EINVAL;
> + goto err_free_fb;
> + }
> +
> + fb->format = info;
> +
> + val = dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_PICTURE_SIZE);
> + fb->width = FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEX_MASK, val) + 1;
> + fb->height = FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEY_MASK, val) + 1;
> +
> + // TODO: Figure that out.
> + val = dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_ROW_INC);
> + if (val != 1) {
> + ret = -EINVAL;
> + goto err_free_fb;
> + }
> +
> + fb->pitches[0] = fb->width * (drm_format_info_bpp(info, 0) / 8);
> +
> + // TODO: Figure out the offsets
> + fb->offsets[0] = 0;
> +
> + ret = drm_framebuffer_init(plane->dev, fb, &tidss_plane_readout_fb_funcs);
> + 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 = plane->dev;
> +
> + if (dev->num_crtcs != 1)
> + return ERR_PTR(-EINVAL);
> +
> + return list_first_entry(&dev->mode_config.crtc_list, struct drm_crtc, head);
> +}
> +
> +static struct drm_plane_state *tidss_plane_atomic_readout_state(struct drm_plane *plane,
> + struct drm_atomic_state *state)
> +{
> + struct drm_device *ddev = plane->dev;
> + struct tidss_device *tidss = to_tidss(ddev);
> + struct dispc_device *dispc = tidss->dispc;
> + struct tidss_plane *tplane = 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 = 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 = 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 = dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_ATTRIBUTES);
> + if (!FIELD_GET(DISPC_VID_ATTRIBUTES_ENABLE_MASK, val)) {
> + goto out;
> + }
> +
> + fb = tidss_plane_readout_fb(plane);
> + if (IS_ERR(fb)) {
> + ret = PTR_ERR(fb);
> + goto err_runtime_pm;
> + }
> +
> + crtc = tidss_plane_readout_crtc(plane);
> + if (IS_ERR(crtc)) {
> + ret = PTR_ERR(crtc);
> + goto err_runtime_pm;
> + }
> +
> + plane_state->fb = fb;
> + plane_state->crtc = crtc;
> + plane_state->visible = true;
> +
> + val = dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_PICTURE_SIZE);
> + in_w = FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEX_MASK, val) + 1;
> + in_h = FIELD_GET(DISPC_VID_PICTURE_SIZE_MEMSIZEY_MASK, val) + 1;
> + plane_state->src_w = in_w << 16;
> + plane_state->src_h = in_h << 16;
> +
> + if (!lite) {
> + val = dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_SIZE);
> + plane_state->crtc_w = FIELD_GET(DISPC_VID_SIZE_SIZEX_MASK, val) + 1;
> + plane_state->crtc_h = FIELD_GET(DISPC_VID_SIZE_SIZEY_MASK, val) + 1;
> + } else {
> + plane_state->crtc_w = in_w;
> + plane_state->crtc_h = 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 = 0;
> + plane_state->src.x2 = plane_state->src_w;
> + plane_state->src.y1 = 0;
> + plane_state->src.y2 = plane_state->src_h;
> + plane_state->dst.x1 = 0;
> + plane_state->dst.x2 = plane_state->crtc_w;
> + plane_state->dst.y1 = 0;
> + plane_state->dst.y2 = plane_state->crtc_h;
> +
> + val = dispc_vid_read(dispc, tplane->hw_plane_id, DISPC_VID_GLOBAL_ALPHA);
> + plane_state->alpha = FIELD_GET(DISPC_VID_GLOBAL_ALPHA_GLOBALALPHA_MASK, val) << 16;
> +
> + val = 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 = DRM_MODE_BLEND_PREMULTI;
> + else
> + plane_state->pixel_blend_mode = DRM_MODE_BLEND_COVERAGE;
> +
> + // TODO: If YUV, handle color encoding and range
> +
> + crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
> + if (!crtc_state) {
> + ret = -ENODEV;
> + goto err_runtime_pm;
> + }
> +
> + crtc_state->plane_mask |= 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 = {
> .update_plane = drm_atomic_helper_update_plane,
> .disable_plane = drm_atomic_helper_disable_plane,
> - .reset = drm_atomic_helper_plane_reset,
> .destroy = drm_plane_destroy,
> + .atomic_compare_state = drm_atomic_helper_plane_compare_state,
> .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> + .atomic_readout_state = tidss_plane_atomic_readout_state,
> };
>
> struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
> u32 hw_plane_id, u32 plane_type,
> u32 crtc_mask, const u32 *formats,
>
Powered by blists - more mailing lists