Commit 9b565edc authored by Dmitry Baryshkov's avatar Dmitry Baryshkov Committed by Dmitry Baryshkov
Browse files

drm/msm/mdp4: switch LVDS to use drm_bridge/_connector



LVDS support in MDP4 driver makes use of drm_connector directly. However
LCDC encoder and LVDS connector are wrappers around drm_panel. Switch
them to use drm_panel_bridge/drm_bridge_connector. This allows using
standard interface for the drm_panel and also inserting additional
bridges between encoder and panel.

Signed-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: default avatarAbhinav Kumar <quic_abhinavk@quicinc.com>
Patchwork: https://patchwork.freedesktop.org/patch/650290/
Link: https://lore.kernel.org/r/20250425-fd-mdp4-lvds-v4-6-6b212160b44c@oss.qualcomm.com


Signed-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
parent f26b8035
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -48,7 +48,6 @@ msm-display-$(CONFIG_DRM_MSM_MDP4) += \
	disp/mdp4/mdp4_dsi_encoder.o \
	disp/mdp4/mdp4_dtv_encoder.o \
	disp/mdp4/mdp4_lcdc_encoder.o \
	disp/mdp4/mdp4_lvds_connector.o \
	disp/mdp4/mdp4_lvds_pll.o \
	disp/mdp4/mdp4_irq.o \
	disp/mdp4/mdp4_kms.o \
+26 −8
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@

#include <linux/delay.h>

#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_vblank.h>

#include "msm_drv.h"
@@ -189,7 +191,7 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
	struct msm_drm_private *priv = dev->dev_private;
	struct drm_encoder *encoder;
	struct drm_connector *connector;
	struct device_node *panel_node;
	struct drm_bridge *next_bridge;
	int dsi_id;
	int ret;

@@ -199,27 +201,43 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
		 * bail out early if there is no panel node (no need to
		 * initialize LCDC encoder and LVDS connector)
		 */
		panel_node = of_graph_get_remote_node(dev->dev->of_node, 0, 0);
		if (!panel_node)
		next_bridge = devm_drm_of_get_bridge(dev->dev, dev->dev->of_node, 0, 0);
		if (IS_ERR(next_bridge)) {
			ret = PTR_ERR(next_bridge);
			if (ret == -ENODEV)
				return 0;
			return ret;
		}

		encoder = mdp4_lcdc_encoder_init(dev, panel_node);
		encoder = mdp4_lcdc_encoder_init(dev);
		if (IS_ERR(encoder)) {
			DRM_DEV_ERROR(dev->dev, "failed to construct LCDC encoder\n");
			of_node_put(panel_node);
			return PTR_ERR(encoder);
		}

		/* LCDC can be hooked to DMA_P (TODO: Add DMA_S later?) */
		encoder->possible_crtcs = 1 << DMA_P;

		connector = mdp4_lvds_connector_init(dev, panel_node, encoder);
		ret = drm_bridge_attach(encoder, next_bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
		if (ret) {
			DRM_DEV_ERROR(dev->dev, "failed to attach LVDS panel/bridge: %d\n", ret);

			return ret;
		}

		connector = drm_bridge_connector_init(dev, encoder);
		if (IS_ERR(connector)) {
			DRM_DEV_ERROR(dev->dev, "failed to initialize LVDS connector\n");
			of_node_put(panel_node);
			return PTR_ERR(connector);
		}

		ret = drm_connector_attach_encoder(connector, encoder);
		if (ret) {
			DRM_DEV_ERROR(dev->dev, "failed to attach LVDS connector: %d\n", ret);

			return ret;
		}

		break;
	case DRM_MODE_ENCODER_TMDS:
		encoder = mdp4_dtv_encoder_init(dev);
+1 −5
Original line number Diff line number Diff line
@@ -191,11 +191,7 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev);

struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
		struct device_node *panel_node);

struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
		struct device_node *panel_node, struct drm_encoder *encoder);
struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev);

#ifdef CONFIG_DRM_MSM_DSI
struct drm_encoder *mdp4_dsi_encoder_init(struct drm_device *dev);
+1 −19
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@

struct mdp4_lcdc_encoder {
	struct drm_encoder base;
	struct device_node *panel_node;
	struct drm_panel *panel;
	struct clk *lcdc_clk;
	unsigned long int pixclock;
@@ -262,19 +261,12 @@ static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder)
	struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
			to_mdp4_lcdc_encoder(encoder);
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	struct drm_panel *panel;

	if (WARN_ON(!mdp4_lcdc_encoder->enabled))
		return;

	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);

	panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
	if (!IS_ERR(panel)) {
		drm_panel_disable(panel);
		drm_panel_unprepare(panel);
	}

	/*
	 * Wait for a vsync so we know the ENABLE=0 latched before
	 * the (connector) source of the vsync's gets disabled,
@@ -300,7 +292,6 @@ static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
			to_mdp4_lcdc_encoder(encoder);
	unsigned long pc = mdp4_lcdc_encoder->pixclock;
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	struct drm_panel *panel;
	uint32_t config;
	int ret;

@@ -335,12 +326,6 @@ static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
	if (ret)
		DRM_DEV_ERROR(dev->dev, "failed to enable lcdc_clk: %d\n", ret);

	panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
	if (!IS_ERR(panel)) {
		drm_panel_prepare(panel);
		drm_panel_enable(panel);
	}

	setup_phy(encoder);

	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1);
@@ -375,8 +360,7 @@ static const struct drm_encoder_helper_funcs mdp4_lcdc_encoder_helper_funcs = {
};

/* initialize encoder */
struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
		struct device_node *panel_node)
struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev)
{
	struct drm_encoder *encoder;
	struct mdp4_lcdc_encoder *mdp4_lcdc_encoder;
@@ -387,8 +371,6 @@ struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
	if (IS_ERR(mdp4_lcdc_encoder))
		return ERR_CAST(mdp4_lcdc_encoder);

	mdp4_lcdc_encoder->panel_node = panel_node;

	encoder = &mdp4_lcdc_encoder->base;

	drm_encoder_helper_add(encoder, &mdp4_lcdc_encoder_helper_funcs);
+0 −100
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2014 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 * Author: Vinay Simha <vinaysimha@inforcecomputing.com>
 */

#include "mdp4_kms.h"

struct mdp4_lvds_connector {
	struct drm_connector base;
	struct drm_encoder *encoder;
	struct device_node *panel_node;
	struct drm_panel *panel;
};
#define to_mdp4_lvds_connector(x) container_of(x, struct mdp4_lvds_connector, base)

static enum drm_connector_status mdp4_lvds_connector_detect(
		struct drm_connector *connector, bool force)
{
	struct mdp4_lvds_connector *mdp4_lvds_connector =
			to_mdp4_lvds_connector(connector);

	if (!mdp4_lvds_connector->panel) {
		mdp4_lvds_connector->panel =
			of_drm_find_panel(mdp4_lvds_connector->panel_node);
		if (IS_ERR(mdp4_lvds_connector->panel))
			mdp4_lvds_connector->panel = NULL;
	}

	return mdp4_lvds_connector->panel ?
			connector_status_connected :
			connector_status_disconnected;
}

static void mdp4_lvds_connector_destroy(struct drm_connector *connector)
{
	struct mdp4_lvds_connector *mdp4_lvds_connector =
			to_mdp4_lvds_connector(connector);

	drm_connector_cleanup(connector);

	kfree(mdp4_lvds_connector);
}

static int mdp4_lvds_connector_get_modes(struct drm_connector *connector)
{
	struct mdp4_lvds_connector *mdp4_lvds_connector =
			to_mdp4_lvds_connector(connector);
	struct drm_panel *panel = mdp4_lvds_connector->panel;
	int ret = 0;

	if (panel)
		ret = drm_panel_get_modes(panel, connector);

	return ret;
}

static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
	.detect = mdp4_lvds_connector_detect,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = mdp4_lvds_connector_destroy,
	.reset = drm_atomic_helper_connector_reset,
	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};

static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = {
	.get_modes = mdp4_lvds_connector_get_modes,
};

/* initialize connector */
struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
		struct device_node *panel_node, struct drm_encoder *encoder)
{
	struct drm_connector *connector = NULL;
	struct mdp4_lvds_connector *mdp4_lvds_connector;

	mdp4_lvds_connector = kzalloc(sizeof(*mdp4_lvds_connector), GFP_KERNEL);
	if (!mdp4_lvds_connector)
		return ERR_PTR(-ENOMEM);

	mdp4_lvds_connector->encoder = encoder;
	mdp4_lvds_connector->panel_node = panel_node;

	connector = &mdp4_lvds_connector->base;

	drm_connector_init(dev, connector, &mdp4_lvds_connector_funcs,
			DRM_MODE_CONNECTOR_LVDS);
	drm_connector_helper_add(connector, &mdp4_lvds_connector_helper_funcs);

	connector->polled = 0;

	connector->interlace_allowed = 0;
	connector->doublescan_allowed = 0;

	drm_connector_attach_encoder(connector, encoder);

	return connector;
}