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