250 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Exynos DRM Parallel output support.4 *5 * Copyright (c) 2014 Samsung Electronics Co., Ltd6 *7 * Contacts: Andrzej Hajda <a.hajda@samsung.com>8*/9 10#include <linux/of.h>11#include <linux/of_graph.h>12#include <linux/regulator/consumer.h>13 14#include <drm/drm_atomic_helper.h>15#include <drm/drm_panel.h>16#include <drm/drm_print.h>17#include <drm/drm_probe_helper.h>18#include <drm/drm_simple_kms_helper.h>19 20#include <video/of_videomode.h>21#include <video/videomode.h>22 23#include "exynos_drm_crtc.h"24 25struct exynos_dpi {26 struct drm_encoder encoder;27 struct device *dev;28 struct device_node *panel_node;29 30 struct drm_panel *panel;31 struct drm_connector connector;32 33 struct videomode *vm;34};35 36#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)37 38static inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e)39{40 return container_of(e, struct exynos_dpi, encoder);41}42 43static enum drm_connector_status44exynos_dpi_detect(struct drm_connector *connector, bool force)45{46 return connector_status_connected;47}48 49static void exynos_dpi_connector_destroy(struct drm_connector *connector)50{51 drm_connector_unregister(connector);52 drm_connector_cleanup(connector);53}54 55static const struct drm_connector_funcs exynos_dpi_connector_funcs = {56 .detect = exynos_dpi_detect,57 .fill_modes = drm_helper_probe_single_connector_modes,58 .destroy = exynos_dpi_connector_destroy,59 .reset = drm_atomic_helper_connector_reset,60 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,61 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,62};63 64static int exynos_dpi_get_modes(struct drm_connector *connector)65{66 struct exynos_dpi *ctx = connector_to_dpi(connector);67 68 /* fimd timings gets precedence over panel modes */69 if (ctx->vm) {70 struct drm_display_mode *mode;71 72 mode = drm_mode_create(connector->dev);73 if (!mode) {74 DRM_DEV_ERROR(ctx->dev,75 "failed to create a new display mode\n");76 return 0;77 }78 drm_display_mode_from_videomode(ctx->vm, mode);79 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;80 drm_mode_probed_add(connector, mode);81 return 1;82 }83 84 if (ctx->panel)85 return drm_panel_get_modes(ctx->panel, connector);86 87 return 0;88}89 90static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {91 .get_modes = exynos_dpi_get_modes,92};93 94static int exynos_dpi_create_connector(struct drm_encoder *encoder)95{96 struct exynos_dpi *ctx = encoder_to_dpi(encoder);97 struct drm_connector *connector = &ctx->connector;98 int ret;99 100 connector->polled = DRM_CONNECTOR_POLL_HPD;101 102 ret = drm_connector_init(encoder->dev, connector,103 &exynos_dpi_connector_funcs,104 DRM_MODE_CONNECTOR_DPI);105 if (ret) {106 DRM_DEV_ERROR(ctx->dev,107 "failed to initialize connector with drm\n");108 return ret;109 }110 111 drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);112 drm_connector_attach_encoder(connector, encoder);113 114 return 0;115}116 117static void exynos_dpi_mode_set(struct drm_encoder *encoder,118 struct drm_display_mode *mode,119 struct drm_display_mode *adjusted_mode)120{121}122 123static void exynos_dpi_enable(struct drm_encoder *encoder)124{125 struct exynos_dpi *ctx = encoder_to_dpi(encoder);126 127 if (ctx->panel) {128 drm_panel_prepare(ctx->panel);129 drm_panel_enable(ctx->panel);130 }131}132 133static void exynos_dpi_disable(struct drm_encoder *encoder)134{135 struct exynos_dpi *ctx = encoder_to_dpi(encoder);136 137 if (ctx->panel) {138 drm_panel_disable(ctx->panel);139 drm_panel_unprepare(ctx->panel);140 }141}142 143static const struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = {144 .mode_set = exynos_dpi_mode_set,145 .enable = exynos_dpi_enable,146 .disable = exynos_dpi_disable,147};148 149enum {150 FIMD_PORT_IN0,151 FIMD_PORT_IN1,152 FIMD_PORT_IN2,153 FIMD_PORT_RGB,154 FIMD_PORT_WRB,155};156 157static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)158{159 struct device *dev = ctx->dev;160 struct device_node *dn = dev->of_node;161 struct device_node *np;162 163 ctx->panel_node = of_graph_get_remote_node(dn, FIMD_PORT_RGB, 0);164 165 np = of_get_child_by_name(dn, "display-timings");166 if (np) {167 struct videomode *vm;168 int ret;169 170 of_node_put(np);171 172 vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL);173 if (!vm)174 return -ENOMEM;175 176 ret = of_get_videomode(dn, vm, 0);177 if (ret < 0) {178 devm_kfree(dev, vm);179 return ret;180 }181 182 ctx->vm = vm;183 184 return 0;185 }186 187 if (!ctx->panel_node)188 return -EINVAL;189 190 return 0;191}192 193int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder)194{195 int ret;196 197 drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);198 199 drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs);200 201 ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);202 if (ret < 0)203 return ret;204 205 ret = exynos_dpi_create_connector(encoder);206 if (ret) {207 DRM_DEV_ERROR(encoder_to_dpi(encoder)->dev,208 "failed to create connector ret = %d\n", ret);209 drm_encoder_cleanup(encoder);210 return ret;211 }212 213 return 0;214}215 216struct drm_encoder *exynos_dpi_probe(struct device *dev)217{218 struct exynos_dpi *ctx;219 int ret;220 221 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);222 if (!ctx)223 return ERR_PTR(-ENOMEM);224 225 ctx->dev = dev;226 227 ret = exynos_dpi_parse_dt(ctx);228 if (ret < 0) {229 devm_kfree(dev, ctx);230 return NULL;231 }232 233 if (ctx->panel_node) {234 ctx->panel = of_drm_find_panel(ctx->panel_node);235 if (IS_ERR(ctx->panel))236 return ERR_CAST(ctx->panel);237 }238 239 return &ctx->encoder;240}241 242int exynos_dpi_remove(struct drm_encoder *encoder)243{244 struct exynos_dpi *ctx = encoder_to_dpi(encoder);245 246 exynos_dpi_disable(&ctx->encoder);247 248 return 0;249}250