162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Exynos DRM Parallel output support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 Samsung Electronics Co., Ltd 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Contacts: Andrzej Hajda <a.hajda@samsung.com> 862306a36Sopenharmony_ci*/ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_graph.h> 1262306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1562306a36Sopenharmony_ci#include <drm/drm_panel.h> 1662306a36Sopenharmony_ci#include <drm/drm_print.h> 1762306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 1862306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <video/of_videomode.h> 2162306a36Sopenharmony_ci#include <video/videomode.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "exynos_drm_crtc.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct exynos_dpi { 2662306a36Sopenharmony_ci struct drm_encoder encoder; 2762306a36Sopenharmony_ci struct device *dev; 2862306a36Sopenharmony_ci struct device_node *panel_node; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci struct drm_panel *panel; 3162306a36Sopenharmony_ci struct drm_connector connector; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci struct videomode *vm; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return container_of(e, struct exynos_dpi, encoder); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic enum drm_connector_status 4462306a36Sopenharmony_ciexynos_dpi_detect(struct drm_connector *connector, bool force) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return connector_status_connected; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void exynos_dpi_connector_destroy(struct drm_connector *connector) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci drm_connector_unregister(connector); 5262306a36Sopenharmony_ci drm_connector_cleanup(connector); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const struct drm_connector_funcs exynos_dpi_connector_funcs = { 5662306a36Sopenharmony_ci .detect = exynos_dpi_detect, 5762306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 5862306a36Sopenharmony_ci .destroy = exynos_dpi_connector_destroy, 5962306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 6062306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 6162306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int exynos_dpi_get_modes(struct drm_connector *connector) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct exynos_dpi *ctx = connector_to_dpi(connector); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* fimd timings gets precedence over panel modes */ 6962306a36Sopenharmony_ci if (ctx->vm) { 7062306a36Sopenharmony_ci struct drm_display_mode *mode; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci mode = drm_mode_create(connector->dev); 7362306a36Sopenharmony_ci if (!mode) { 7462306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 7562306a36Sopenharmony_ci "failed to create a new display mode\n"); 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci drm_display_mode_from_videomode(ctx->vm, mode); 7962306a36Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 8062306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 8162306a36Sopenharmony_ci return 1; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (ctx->panel) 8562306a36Sopenharmony_ci return drm_panel_get_modes(ctx->panel, connector); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { 9162306a36Sopenharmony_ci .get_modes = exynos_dpi_get_modes, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int exynos_dpi_create_connector(struct drm_encoder *encoder) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 9762306a36Sopenharmony_ci struct drm_connector *connector = &ctx->connector; 9862306a36Sopenharmony_ci int ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci ret = drm_connector_init(encoder->dev, connector, 10362306a36Sopenharmony_ci &exynos_dpi_connector_funcs, 10462306a36Sopenharmony_ci DRM_MODE_CONNECTOR_VGA); 10562306a36Sopenharmony_ci if (ret) { 10662306a36Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 10762306a36Sopenharmony_ci "failed to initialize connector with drm\n"); 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs); 11262306a36Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void exynos_dpi_mode_set(struct drm_encoder *encoder, 11862306a36Sopenharmony_ci struct drm_display_mode *mode, 11962306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void exynos_dpi_enable(struct drm_encoder *encoder) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (ctx->panel) { 12862306a36Sopenharmony_ci drm_panel_prepare(ctx->panel); 12962306a36Sopenharmony_ci drm_panel_enable(ctx->panel); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void exynos_dpi_disable(struct drm_encoder *encoder) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (ctx->panel) { 13862306a36Sopenharmony_ci drm_panel_disable(ctx->panel); 13962306a36Sopenharmony_ci drm_panel_unprepare(ctx->panel); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = { 14462306a36Sopenharmony_ci .mode_set = exynos_dpi_mode_set, 14562306a36Sopenharmony_ci .enable = exynos_dpi_enable, 14662306a36Sopenharmony_ci .disable = exynos_dpi_disable, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cienum { 15062306a36Sopenharmony_ci FIMD_PORT_IN0, 15162306a36Sopenharmony_ci FIMD_PORT_IN1, 15262306a36Sopenharmony_ci FIMD_PORT_IN2, 15362306a36Sopenharmony_ci FIMD_PORT_RGB, 15462306a36Sopenharmony_ci FIMD_PORT_WRB, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int exynos_dpi_parse_dt(struct exynos_dpi *ctx) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct device *dev = ctx->dev; 16062306a36Sopenharmony_ci struct device_node *dn = dev->of_node; 16162306a36Sopenharmony_ci struct device_node *np; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ctx->panel_node = of_graph_get_remote_node(dn, FIMD_PORT_RGB, 0); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci np = of_get_child_by_name(dn, "display-timings"); 16662306a36Sopenharmony_ci if (np) { 16762306a36Sopenharmony_ci struct videomode *vm; 16862306a36Sopenharmony_ci int ret; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci of_node_put(np); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL); 17362306a36Sopenharmony_ci if (!vm) 17462306a36Sopenharmony_ci return -ENOMEM; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = of_get_videomode(dn, vm, 0); 17762306a36Sopenharmony_ci if (ret < 0) { 17862306a36Sopenharmony_ci devm_kfree(dev, vm); 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ctx->vm = vm; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!ctx->panel_node) 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciint exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); 20262306a36Sopenharmony_ci if (ret < 0) 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = exynos_dpi_create_connector(encoder); 20662306a36Sopenharmony_ci if (ret) { 20762306a36Sopenharmony_ci DRM_DEV_ERROR(encoder_to_dpi(encoder)->dev, 20862306a36Sopenharmony_ci "failed to create connector ret = %d\n", ret); 20962306a36Sopenharmony_ci drm_encoder_cleanup(encoder); 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistruct drm_encoder *exynos_dpi_probe(struct device *dev) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct exynos_dpi *ctx; 21962306a36Sopenharmony_ci int ret; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 22262306a36Sopenharmony_ci if (!ctx) 22362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ctx->dev = dev; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = exynos_dpi_parse_dt(ctx); 22862306a36Sopenharmony_ci if (ret < 0) { 22962306a36Sopenharmony_ci devm_kfree(dev, ctx); 23062306a36Sopenharmony_ci return NULL; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (ctx->panel_node) { 23462306a36Sopenharmony_ci ctx->panel = of_drm_find_panel(ctx->panel_node); 23562306a36Sopenharmony_ci if (IS_ERR(ctx->panel)) 23662306a36Sopenharmony_ci return ERR_CAST(ctx->panel); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return &ctx->encoder; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciint exynos_dpi_remove(struct drm_encoder *encoder) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci exynos_dpi_disable(&ctx->encoder); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 250