18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Exynos DRM Parallel output support. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Samsung Electronics Co., Ltd 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contacts: Andrzej Hajda <a.hajda@samsung.com> 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 118c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <video/of_videomode.h> 208c2ecf20Sopenharmony_ci#include <video/videomode.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "exynos_drm_crtc.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct exynos_dpi { 258c2ecf20Sopenharmony_ci struct drm_encoder encoder; 268c2ecf20Sopenharmony_ci struct device *dev; 278c2ecf20Sopenharmony_ci struct device_node *panel_node; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci struct drm_panel *panel; 308c2ecf20Sopenharmony_ci struct drm_connector connector; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci struct videomode *vm; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci return container_of(e, struct exynos_dpi, encoder); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic enum drm_connector_status 438c2ecf20Sopenharmony_ciexynos_dpi_detect(struct drm_connector *connector, bool force) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return connector_status_connected; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void exynos_dpi_connector_destroy(struct drm_connector *connector) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci drm_connector_unregister(connector); 518c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs exynos_dpi_connector_funcs = { 558c2ecf20Sopenharmony_ci .detect = exynos_dpi_detect, 568c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 578c2ecf20Sopenharmony_ci .destroy = exynos_dpi_connector_destroy, 588c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 598c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 608c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int exynos_dpi_get_modes(struct drm_connector *connector) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct exynos_dpi *ctx = connector_to_dpi(connector); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* fimd timings gets precedence over panel modes */ 688c2ecf20Sopenharmony_ci if (ctx->vm) { 698c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mode = drm_mode_create(connector->dev); 728c2ecf20Sopenharmony_ci if (!mode) { 738c2ecf20Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 748c2ecf20Sopenharmony_ci "failed to create a new display mode\n"); 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci drm_display_mode_from_videomode(ctx->vm, mode); 788c2ecf20Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 798c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 808c2ecf20Sopenharmony_ci return 1; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ctx->panel) 848c2ecf20Sopenharmony_ci return drm_panel_get_modes(ctx->panel, connector); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { 908c2ecf20Sopenharmony_ci .get_modes = exynos_dpi_get_modes, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int exynos_dpi_create_connector(struct drm_encoder *encoder) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 968c2ecf20Sopenharmony_ci struct drm_connector *connector = &ctx->connector; 978c2ecf20Sopenharmony_ci int ret; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ret = drm_connector_init(encoder->dev, connector, 1028c2ecf20Sopenharmony_ci &exynos_dpi_connector_funcs, 1038c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_VGA); 1048c2ecf20Sopenharmony_ci if (ret) { 1058c2ecf20Sopenharmony_ci DRM_DEV_ERROR(ctx->dev, 1068c2ecf20Sopenharmony_ci "failed to initialize connector with drm\n"); 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs); 1118c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void exynos_dpi_mode_set(struct drm_encoder *encoder, 1178c2ecf20Sopenharmony_ci struct drm_display_mode *mode, 1188c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void exynos_dpi_enable(struct drm_encoder *encoder) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (ctx->panel) { 1278c2ecf20Sopenharmony_ci drm_panel_prepare(ctx->panel); 1288c2ecf20Sopenharmony_ci drm_panel_enable(ctx->panel); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void exynos_dpi_disable(struct drm_encoder *encoder) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (ctx->panel) { 1378c2ecf20Sopenharmony_ci drm_panel_disable(ctx->panel); 1388c2ecf20Sopenharmony_ci drm_panel_unprepare(ctx->panel); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = { 1438c2ecf20Sopenharmony_ci .mode_set = exynos_dpi_mode_set, 1448c2ecf20Sopenharmony_ci .enable = exynos_dpi_enable, 1458c2ecf20Sopenharmony_ci .disable = exynos_dpi_disable, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cienum { 1498c2ecf20Sopenharmony_ci FIMD_PORT_IN0, 1508c2ecf20Sopenharmony_ci FIMD_PORT_IN1, 1518c2ecf20Sopenharmony_ci FIMD_PORT_IN2, 1528c2ecf20Sopenharmony_ci FIMD_PORT_RGB, 1538c2ecf20Sopenharmony_ci FIMD_PORT_WRB, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int exynos_dpi_parse_dt(struct exynos_dpi *ctx) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 1598c2ecf20Sopenharmony_ci struct device_node *dn = dev->of_node; 1608c2ecf20Sopenharmony_ci struct device_node *np; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ctx->panel_node = of_graph_get_remote_node(dn, FIMD_PORT_RGB, 0); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci np = of_get_child_by_name(dn, "display-timings"); 1658c2ecf20Sopenharmony_ci if (np) { 1668c2ecf20Sopenharmony_ci struct videomode *vm; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci of_node_put(np); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL); 1728c2ecf20Sopenharmony_ci if (!vm) 1738c2ecf20Sopenharmony_ci return -ENOMEM; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = of_get_videomode(dn, vm, 0); 1768c2ecf20Sopenharmony_ci if (ret < 0) { 1778c2ecf20Sopenharmony_ci devm_kfree(dev, vm); 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ctx->vm = vm; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!ctx->panel_node) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciint exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci int ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD); 2018c2ecf20Sopenharmony_ci if (ret < 0) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = exynos_dpi_create_connector(encoder); 2058c2ecf20Sopenharmony_ci if (ret) { 2068c2ecf20Sopenharmony_ci DRM_DEV_ERROR(encoder_to_dpi(encoder)->dev, 2078c2ecf20Sopenharmony_ci "failed to create connector ret = %d\n", ret); 2088c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistruct drm_encoder *exynos_dpi_probe(struct device *dev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct exynos_dpi *ctx; 2188c2ecf20Sopenharmony_ci int ret; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 2218c2ecf20Sopenharmony_ci if (!ctx) 2228c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ctx->dev = dev; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ret = exynos_dpi_parse_dt(ctx); 2278c2ecf20Sopenharmony_ci if (ret < 0) { 2288c2ecf20Sopenharmony_ci devm_kfree(dev, ctx); 2298c2ecf20Sopenharmony_ci return NULL; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (ctx->panel_node) { 2338c2ecf20Sopenharmony_ci ctx->panel = of_drm_find_panel(ctx->panel_node); 2348c2ecf20Sopenharmony_ci if (IS_ERR(ctx->panel)) 2358c2ecf20Sopenharmony_ci return ERR_CAST(ctx->panel); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return &ctx->encoder; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ciint exynos_dpi_remove(struct drm_encoder *encoder) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct exynos_dpi *ctx = encoder_to_dpi(encoder); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci exynos_dpi_disable(&ctx->encoder); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 249