From ace9429bb58fd418f0c81d4c2835699bddf6bde6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:27:49 +0200 Subject: Adding upstream version 6.6.15. Signed-off-by: Daniel Baumann --- drivers/gpu/drm/sun4i/sun4i_lvds.c | 156 +++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.c (limited to 'drivers/gpu/drm/sun4i/sun4i_lvds.c') diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c new file mode 100644 index 0000000000..6716e895ae --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Free Electrons + * Maxime Ripard + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sun4i_crtc.h" +#include "sun4i_tcon.h" +#include "sun4i_lvds.h" + +struct sun4i_lvds { + struct drm_connector connector; + struct drm_encoder encoder; + + struct drm_panel *panel; +}; + +static inline struct sun4i_lvds * +drm_connector_to_sun4i_lvds(struct drm_connector *connector) +{ + return container_of(connector, struct sun4i_lvds, + connector); +} + +static inline struct sun4i_lvds * +drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder) +{ + return container_of(encoder, struct sun4i_lvds, + encoder); +} + +static int sun4i_lvds_get_modes(struct drm_connector *connector) +{ + struct sun4i_lvds *lvds = + drm_connector_to_sun4i_lvds(connector); + + return drm_panel_get_modes(lvds->panel, connector); +} + +static const struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = { + .get_modes = sun4i_lvds_get_modes, +}; + +static void +sun4i_lvds_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs sun4i_lvds_con_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = sun4i_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 void sun4i_lvds_encoder_enable(struct drm_encoder *encoder) +{ + struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder); + + DRM_DEBUG_DRIVER("Enabling LVDS output\n"); + + if (lvds->panel) { + drm_panel_prepare(lvds->panel); + drm_panel_enable(lvds->panel); + } +} + +static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder) +{ + struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder); + + DRM_DEBUG_DRIVER("Disabling LVDS output\n"); + + if (lvds->panel) { + drm_panel_disable(lvds->panel); + drm_panel_unprepare(lvds->panel); + } +} + +static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = { + .disable = sun4i_lvds_encoder_disable, + .enable = sun4i_lvds_encoder_enable, +}; + +int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon) +{ + struct drm_encoder *encoder; + struct drm_bridge *bridge; + struct sun4i_lvds *lvds; + int ret; + + lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL); + if (!lvds) + return -ENOMEM; + encoder = &lvds->encoder; + + ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0, + &lvds->panel, &bridge); + if (ret) { + dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n"); + return 0; + } + + drm_encoder_helper_add(&lvds->encoder, + &sun4i_lvds_enc_helper_funcs); + ret = drm_simple_encoder_init(drm, &lvds->encoder, + DRM_MODE_ENCODER_LVDS); + if (ret) { + dev_err(drm->dev, "Couldn't initialise the lvds encoder\n"); + goto err_out; + } + + /* The LVDS encoder can only work with the TCON channel 0 */ + lvds->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc); + + if (lvds->panel) { + drm_connector_helper_add(&lvds->connector, + &sun4i_lvds_con_helper_funcs); + ret = drm_connector_init(drm, &lvds->connector, + &sun4i_lvds_con_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (ret) { + dev_err(drm->dev, "Couldn't initialise the lvds connector\n"); + goto err_cleanup_connector; + } + + drm_connector_attach_encoder(&lvds->connector, + &lvds->encoder); + } + + if (bridge) { + ret = drm_bridge_attach(encoder, bridge, NULL, 0); + if (ret) + goto err_cleanup_connector; + } + + return 0; + +err_cleanup_connector: + drm_encoder_cleanup(&lvds->encoder); +err_out: + return ret; +} +EXPORT_SYMBOL(sun4i_lvds_init); -- cgit v1.2.3