Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Exynos DRM Parallel output support.
0004  *
0005  * Copyright (c) 2014 Samsung Electronics Co., Ltd
0006  *
0007  * Contacts: Andrzej Hajda <a.hajda@samsung.com>
0008 */
0009 
0010 #include <linux/of.h>
0011 #include <linux/of_graph.h>
0012 #include <linux/regulator/consumer.h>
0013 
0014 #include <drm/drm_atomic_helper.h>
0015 #include <drm/drm_panel.h>
0016 #include <drm/drm_print.h>
0017 #include <drm/drm_probe_helper.h>
0018 #include <drm/drm_simple_kms_helper.h>
0019 
0020 #include <video/of_videomode.h>
0021 #include <video/videomode.h>
0022 
0023 #include "exynos_drm_crtc.h"
0024 
0025 struct exynos_dpi {
0026     struct drm_encoder encoder;
0027     struct device *dev;
0028     struct device_node *panel_node;
0029 
0030     struct drm_panel *panel;
0031     struct drm_connector connector;
0032 
0033     struct videomode *vm;
0034 };
0035 
0036 #define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
0037 
0038 static inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e)
0039 {
0040     return container_of(e, struct exynos_dpi, encoder);
0041 }
0042 
0043 static enum drm_connector_status
0044 exynos_dpi_detect(struct drm_connector *connector, bool force)
0045 {
0046     return connector_status_connected;
0047 }
0048 
0049 static void exynos_dpi_connector_destroy(struct drm_connector *connector)
0050 {
0051     drm_connector_unregister(connector);
0052     drm_connector_cleanup(connector);
0053 }
0054 
0055 static const struct drm_connector_funcs exynos_dpi_connector_funcs = {
0056     .detect = exynos_dpi_detect,
0057     .fill_modes = drm_helper_probe_single_connector_modes,
0058     .destroy = exynos_dpi_connector_destroy,
0059     .reset = drm_atomic_helper_connector_reset,
0060     .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
0061     .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
0062 };
0063 
0064 static int exynos_dpi_get_modes(struct drm_connector *connector)
0065 {
0066     struct exynos_dpi *ctx = connector_to_dpi(connector);
0067 
0068     /* fimd timings gets precedence over panel modes */
0069     if (ctx->vm) {
0070         struct drm_display_mode *mode;
0071 
0072         mode = drm_mode_create(connector->dev);
0073         if (!mode) {
0074             DRM_DEV_ERROR(ctx->dev,
0075                       "failed to create a new display mode\n");
0076             return 0;
0077         }
0078         drm_display_mode_from_videomode(ctx->vm, mode);
0079         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0080         drm_mode_probed_add(connector, mode);
0081         return 1;
0082     }
0083 
0084     if (ctx->panel)
0085         return drm_panel_get_modes(ctx->panel, connector);
0086 
0087     return 0;
0088 }
0089 
0090 static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
0091     .get_modes = exynos_dpi_get_modes,
0092 };
0093 
0094 static int exynos_dpi_create_connector(struct drm_encoder *encoder)
0095 {
0096     struct exynos_dpi *ctx = encoder_to_dpi(encoder);
0097     struct drm_connector *connector = &ctx->connector;
0098     int ret;
0099 
0100     connector->polled = DRM_CONNECTOR_POLL_HPD;
0101 
0102     ret = drm_connector_init(encoder->dev, connector,
0103                  &exynos_dpi_connector_funcs,
0104                  DRM_MODE_CONNECTOR_VGA);
0105     if (ret) {
0106         DRM_DEV_ERROR(ctx->dev,
0107                   "failed to initialize connector with drm\n");
0108         return ret;
0109     }
0110 
0111     drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
0112     drm_connector_attach_encoder(connector, encoder);
0113 
0114     return 0;
0115 }
0116 
0117 static void exynos_dpi_mode_set(struct drm_encoder *encoder,
0118                 struct drm_display_mode *mode,
0119                 struct drm_display_mode *adjusted_mode)
0120 {
0121 }
0122 
0123 static void exynos_dpi_enable(struct drm_encoder *encoder)
0124 {
0125     struct exynos_dpi *ctx = encoder_to_dpi(encoder);
0126 
0127     if (ctx->panel) {
0128         drm_panel_prepare(ctx->panel);
0129         drm_panel_enable(ctx->panel);
0130     }
0131 }
0132 
0133 static void exynos_dpi_disable(struct drm_encoder *encoder)
0134 {
0135     struct exynos_dpi *ctx = encoder_to_dpi(encoder);
0136 
0137     if (ctx->panel) {
0138         drm_panel_disable(ctx->panel);
0139         drm_panel_unprepare(ctx->panel);
0140     }
0141 }
0142 
0143 static const struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = {
0144     .mode_set = exynos_dpi_mode_set,
0145     .enable = exynos_dpi_enable,
0146     .disable = exynos_dpi_disable,
0147 };
0148 
0149 enum {
0150     FIMD_PORT_IN0,
0151     FIMD_PORT_IN1,
0152     FIMD_PORT_IN2,
0153     FIMD_PORT_RGB,
0154     FIMD_PORT_WRB,
0155 };
0156 
0157 static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
0158 {
0159     struct device *dev = ctx->dev;
0160     struct device_node *dn = dev->of_node;
0161     struct device_node *np;
0162 
0163     ctx->panel_node = of_graph_get_remote_node(dn, FIMD_PORT_RGB, 0);
0164 
0165     np = of_get_child_by_name(dn, "display-timings");
0166     if (np) {
0167         struct videomode *vm;
0168         int ret;
0169 
0170         of_node_put(np);
0171 
0172         vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL);
0173         if (!vm)
0174             return -ENOMEM;
0175 
0176         ret = of_get_videomode(dn, vm, 0);
0177         if (ret < 0) {
0178             devm_kfree(dev, vm);
0179             return ret;
0180         }
0181 
0182         ctx->vm = vm;
0183 
0184         return 0;
0185     }
0186 
0187     if (!ctx->panel_node)
0188         return -EINVAL;
0189 
0190     return 0;
0191 }
0192 
0193 int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder)
0194 {
0195     int ret;
0196 
0197     drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
0198 
0199     drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs);
0200 
0201     ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
0202     if (ret < 0)
0203         return ret;
0204 
0205     ret = exynos_dpi_create_connector(encoder);
0206     if (ret) {
0207         DRM_DEV_ERROR(encoder_to_dpi(encoder)->dev,
0208                   "failed to create connector ret = %d\n", ret);
0209         drm_encoder_cleanup(encoder);
0210         return ret;
0211     }
0212 
0213     return 0;
0214 }
0215 
0216 struct drm_encoder *exynos_dpi_probe(struct device *dev)
0217 {
0218     struct exynos_dpi *ctx;
0219     int ret;
0220 
0221     ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0222     if (!ctx)
0223         return ERR_PTR(-ENOMEM);
0224 
0225     ctx->dev = dev;
0226 
0227     ret = exynos_dpi_parse_dt(ctx);
0228     if (ret < 0) {
0229         devm_kfree(dev, ctx);
0230         return NULL;
0231     }
0232 
0233     if (ctx->panel_node) {
0234         ctx->panel = of_drm_find_panel(ctx->panel_node);
0235         if (IS_ERR(ctx->panel))
0236             return ERR_CAST(ctx->panel);
0237     }
0238 
0239     return &ctx->encoder;
0240 }
0241 
0242 int exynos_dpi_remove(struct drm_encoder *encoder)
0243 {
0244     struct exynos_dpi *ctx = encoder_to_dpi(encoder);
0245 
0246     exynos_dpi_disable(&ctx->encoder);
0247 
0248     return 0;
0249 }