mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
The KMS framework uses two slightly different definitions for the state concept. For a given object (plane, CRTC, encoder, etc., so drm_$OBJECT_state), the state is the entire state of that object. However, at the device level, drm_atomic_state refers to a state update for a limited number of objects. Thus, drm_atomic_state isn't the entire device state, but only the full state of some objects in that device. This has been an endless source of confusion and thus bugs. We can rename the drm_atomic_state structure to drm_atomic_commit to make it less confusing. This patch was created using: rg -l drm_atomic_state | \ xargs sed -i 's/drm_atomic_state/drm_atomic_commit/g; s/drm_atomic_commit_helper/drm_atomic_state_helper/g' mv drivers/gpu/drm/tests/drm_atomic_state_test.c drivers/gpu/drm/tests/drm_atomic_commit_test.c Acked-by: Simona Vetter <simona.vetter@ffwll.ch> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Acked-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com> Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com> Signed-off-by: Maxime Ripard <mripard@kernel.org> Signed-off-by: Dave Airlie <airlied@redhat.com> Link: https://patch.msgid.link/20260427-drm-drm-atomic-update-v4-1-c0e713bfdf25@kernel.org
171 lines
5.0 KiB
C
171 lines
5.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2016 BayLibre, SAS
|
|
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
|
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_graph.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_simple_kms_helper.h>
|
|
#include <drm/drm_bridge.h>
|
|
#include <drm/drm_bridge_connector.h>
|
|
#include <drm/drm_device.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
|
|
#include "meson_drv.h"
|
|
#include "meson_encoder_dsi.h"
|
|
#include "meson_registers.h"
|
|
#include "meson_venc.h"
|
|
#include "meson_vclk.h"
|
|
|
|
struct meson_encoder_dsi {
|
|
struct drm_encoder encoder;
|
|
struct drm_bridge bridge;
|
|
struct meson_drm *priv;
|
|
};
|
|
|
|
#define bridge_to_meson_encoder_dsi(x) \
|
|
container_of(x, struct meson_encoder_dsi, bridge)
|
|
|
|
static int meson_encoder_dsi_attach(struct drm_bridge *bridge,
|
|
struct drm_encoder *encoder,
|
|
enum drm_bridge_attach_flags flags)
|
|
{
|
|
struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge);
|
|
|
|
return drm_bridge_attach(encoder, encoder_dsi->bridge.next_bridge,
|
|
&encoder_dsi->bridge, flags);
|
|
}
|
|
|
|
static void meson_encoder_dsi_atomic_enable(struct drm_bridge *bridge,
|
|
struct drm_atomic_commit *state)
|
|
{
|
|
struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge);
|
|
struct meson_drm *priv = encoder_dsi->priv;
|
|
struct drm_connector_state *conn_state;
|
|
struct drm_crtc_state *crtc_state;
|
|
struct drm_connector *connector;
|
|
|
|
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
|
|
if (WARN_ON(!connector))
|
|
return;
|
|
|
|
conn_state = drm_atomic_get_new_connector_state(state, connector);
|
|
if (WARN_ON(!conn_state))
|
|
return;
|
|
|
|
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
|
|
if (WARN_ON(!crtc_state))
|
|
return;
|
|
|
|
/* ENCL clock setup is handled by CCF */
|
|
|
|
meson_venc_mipi_dsi_mode_set(priv, &crtc_state->adjusted_mode);
|
|
meson_encl_load_gamma(priv);
|
|
|
|
writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));
|
|
|
|
writel_bits_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN, ENCL_VIDEO_MODE_ADV_VFIFO_EN,
|
|
priv->io_base + _REG(ENCL_VIDEO_MODE_ADV));
|
|
writel_relaxed(0, priv->io_base + _REG(ENCL_TST_EN));
|
|
|
|
writel_bits_relaxed(BIT(0), 0, priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
|
|
|
|
writel_relaxed(1, priv->io_base + _REG(ENCL_VIDEO_EN));
|
|
}
|
|
|
|
static void meson_encoder_dsi_atomic_disable(struct drm_bridge *bridge,
|
|
struct drm_atomic_commit *state)
|
|
{
|
|
struct meson_encoder_dsi *meson_encoder_dsi =
|
|
bridge_to_meson_encoder_dsi(bridge);
|
|
struct meson_drm *priv = meson_encoder_dsi->priv;
|
|
|
|
writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));
|
|
|
|
writel_bits_relaxed(BIT(0), BIT(0), priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
|
|
}
|
|
|
|
static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = {
|
|
.attach = meson_encoder_dsi_attach,
|
|
.atomic_enable = meson_encoder_dsi_atomic_enable,
|
|
.atomic_disable = meson_encoder_dsi_atomic_disable,
|
|
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
|
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
|
.atomic_reset = drm_atomic_helper_bridge_reset,
|
|
};
|
|
|
|
int meson_encoder_dsi_probe(struct meson_drm *priv)
|
|
{
|
|
struct meson_encoder_dsi *meson_encoder_dsi;
|
|
struct device_node *remote;
|
|
int ret;
|
|
|
|
meson_encoder_dsi = devm_drm_bridge_alloc(priv->dev,
|
|
struct meson_encoder_dsi,
|
|
bridge,
|
|
&meson_encoder_dsi_bridge_funcs);
|
|
if (IS_ERR(meson_encoder_dsi))
|
|
return PTR_ERR(meson_encoder_dsi);
|
|
|
|
/* DSI Transceiver Bridge */
|
|
remote = of_graph_get_remote_node(priv->dev->of_node, 2, 0);
|
|
if (!remote) {
|
|
dev_err(priv->dev, "DSI transceiver device is disabled");
|
|
return 0;
|
|
}
|
|
|
|
meson_encoder_dsi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
|
|
if (!meson_encoder_dsi->bridge.next_bridge)
|
|
return dev_err_probe(priv->dev, -EPROBE_DEFER,
|
|
"Failed to find DSI transceiver bridge\n");
|
|
|
|
/* DSI Encoder Bridge */
|
|
meson_encoder_dsi->bridge.of_node = priv->dev->of_node;
|
|
meson_encoder_dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
|
|
|
|
drm_bridge_add(&meson_encoder_dsi->bridge);
|
|
|
|
meson_encoder_dsi->priv = priv;
|
|
|
|
/* Encoder */
|
|
ret = drm_simple_encoder_init(priv->drm, &meson_encoder_dsi->encoder,
|
|
DRM_MODE_ENCODER_DSI);
|
|
if (ret)
|
|
return dev_err_probe(priv->dev, ret,
|
|
"Failed to init DSI encoder\n");
|
|
|
|
meson_encoder_dsi->encoder.possible_crtcs = BIT(0);
|
|
|
|
/* Attach DSI Encoder Bridge to Encoder */
|
|
ret = drm_bridge_attach(&meson_encoder_dsi->encoder, &meson_encoder_dsi->bridge, NULL, 0);
|
|
if (ret)
|
|
return dev_err_probe(priv->dev, ret,
|
|
"Failed to attach bridge\n");
|
|
|
|
/*
|
|
* We should have now in place:
|
|
* encoder->[dsi encoder bridge]->[dw-mipi-dsi bridge]->[panel bridge]->[panel]
|
|
*/
|
|
|
|
priv->encoders[MESON_ENC_DSI] = meson_encoder_dsi;
|
|
|
|
dev_dbg(priv->dev, "DSI encoder initialized\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void meson_encoder_dsi_remove(struct meson_drm *priv)
|
|
{
|
|
struct meson_encoder_dsi *meson_encoder_dsi;
|
|
|
|
if (priv->encoders[MESON_ENC_DSI]) {
|
|
meson_encoder_dsi = priv->encoders[MESON_ENC_DSI];
|
|
drm_bridge_remove(&meson_encoder_dsi->bridge);
|
|
}
|
|
}
|