1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
214b6873aSAndrzej Hajda /*
314b6873aSAndrzej Hajda * Exynos DRM Parallel output support.
414b6873aSAndrzej Hajda *
514b6873aSAndrzej Hajda * Copyright (c) 2014 Samsung Electronics Co., Ltd
614b6873aSAndrzej Hajda *
714b6873aSAndrzej Hajda * Contacts: Andrzej Hajda <a.hajda@samsung.com>
814b6873aSAndrzej Hajda */
914b6873aSAndrzej Hajda
10*73289afeSVille Syrjälä #include <linux/of.h>
11fb38b1f6SPhilipp Zabel #include <linux/of_graph.h>
1214b6873aSAndrzej Hajda #include <linux/regulator/consumer.h>
1314b6873aSAndrzej Hajda
142bda34d7SSam Ravnborg #include <drm/drm_atomic_helper.h>
152bda34d7SSam Ravnborg #include <drm/drm_panel.h>
162bda34d7SSam Ravnborg #include <drm/drm_print.h>
172bda34d7SSam Ravnborg #include <drm/drm_probe_helper.h>
183e1fe32dSThomas Zimmermann #include <drm/drm_simple_kms_helper.h>
192bda34d7SSam Ravnborg
2014b6873aSAndrzej Hajda #include <video/of_videomode.h>
2114b6873aSAndrzej Hajda #include <video/videomode.h>
2214b6873aSAndrzej Hajda
23a2986e80SGustavo Padovan #include "exynos_drm_crtc.h"
2414b6873aSAndrzej Hajda
2514b6873aSAndrzej Hajda struct exynos_dpi {
262b8376c8SGustavo Padovan struct drm_encoder encoder;
2714b6873aSAndrzej Hajda struct device *dev;
2814b6873aSAndrzej Hajda struct device_node *panel_node;
2914b6873aSAndrzej Hajda
3014b6873aSAndrzej Hajda struct drm_panel *panel;
3114b6873aSAndrzej Hajda struct drm_connector connector;
3214b6873aSAndrzej Hajda
3314b6873aSAndrzej Hajda struct videomode *vm;
3414b6873aSAndrzej Hajda };
3514b6873aSAndrzej Hajda
3614b6873aSAndrzej Hajda #define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
3714b6873aSAndrzej Hajda
encoder_to_dpi(struct drm_encoder * e)382b8376c8SGustavo Padovan static inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e)
394cfde1f2SAndrzej Hajda {
40cf67cc9aSGustavo Padovan return container_of(e, struct exynos_dpi, encoder);
414cfde1f2SAndrzej Hajda }
424cfde1f2SAndrzej Hajda
4314b6873aSAndrzej Hajda static enum drm_connector_status
exynos_dpi_detect(struct drm_connector * connector,bool force)4414b6873aSAndrzej Hajda exynos_dpi_detect(struct drm_connector *connector, bool force)
4514b6873aSAndrzej Hajda {
4614b6873aSAndrzej Hajda return connector_status_connected;
4714b6873aSAndrzej Hajda }
4814b6873aSAndrzej Hajda
exynos_dpi_connector_destroy(struct drm_connector * connector)4914b6873aSAndrzej Hajda static void exynos_dpi_connector_destroy(struct drm_connector *connector)
5014b6873aSAndrzej Hajda {
5134ea3d38SThomas Wood drm_connector_unregister(connector);
5214b6873aSAndrzej Hajda drm_connector_cleanup(connector);
5314b6873aSAndrzej Hajda }
5414b6873aSAndrzej Hajda
55800ba2b5SVille Syrjälä static const struct drm_connector_funcs exynos_dpi_connector_funcs = {
5614b6873aSAndrzej Hajda .detect = exynos_dpi_detect,
5714b6873aSAndrzej Hajda .fill_modes = drm_helper_probe_single_connector_modes,
5814b6873aSAndrzej Hajda .destroy = exynos_dpi_connector_destroy,
594ea9526bSGustavo Padovan .reset = drm_atomic_helper_connector_reset,
604ea9526bSGustavo Padovan .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
614ea9526bSGustavo Padovan .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
6214b6873aSAndrzej Hajda };
6314b6873aSAndrzej Hajda
exynos_dpi_get_modes(struct drm_connector * connector)6414b6873aSAndrzej Hajda static int exynos_dpi_get_modes(struct drm_connector *connector)
6514b6873aSAndrzej Hajda {
6614b6873aSAndrzej Hajda struct exynos_dpi *ctx = connector_to_dpi(connector);
6714b6873aSAndrzej Hajda
6814b6873aSAndrzej Hajda /* fimd timings gets precedence over panel modes */
6914b6873aSAndrzej Hajda if (ctx->vm) {
7014b6873aSAndrzej Hajda struct drm_display_mode *mode;
7114b6873aSAndrzej Hajda
7214b6873aSAndrzej Hajda mode = drm_mode_create(connector->dev);
7314b6873aSAndrzej Hajda if (!mode) {
746f83d208SInki Dae DRM_DEV_ERROR(ctx->dev,
756f83d208SInki Dae "failed to create a new display mode\n");
7614b6873aSAndrzej Hajda return 0;
7714b6873aSAndrzej Hajda }
7814b6873aSAndrzej Hajda drm_display_mode_from_videomode(ctx->vm, mode);
7914b6873aSAndrzej Hajda mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
8014b6873aSAndrzej Hajda drm_mode_probed_add(connector, mode);
8114b6873aSAndrzej Hajda return 1;
8214b6873aSAndrzej Hajda }
8314b6873aSAndrzej Hajda
8414b6873aSAndrzej Hajda if (ctx->panel)
8506c4a9c2SSam Ravnborg return drm_panel_get_modes(ctx->panel, connector);
8614b6873aSAndrzej Hajda
8714b6873aSAndrzej Hajda return 0;
8814b6873aSAndrzej Hajda }
8914b6873aSAndrzej Hajda
90800ba2b5SVille Syrjälä static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
9114b6873aSAndrzej Hajda .get_modes = exynos_dpi_get_modes,
9214b6873aSAndrzej Hajda };
9314b6873aSAndrzej Hajda
exynos_dpi_create_connector(struct drm_encoder * encoder)942b8376c8SGustavo Padovan static int exynos_dpi_create_connector(struct drm_encoder *encoder)
9514b6873aSAndrzej Hajda {
962b8376c8SGustavo Padovan struct exynos_dpi *ctx = encoder_to_dpi(encoder);
9714b6873aSAndrzej Hajda struct drm_connector *connector = &ctx->connector;
9814b6873aSAndrzej Hajda int ret;
9914b6873aSAndrzej Hajda
10014b6873aSAndrzej Hajda connector->polled = DRM_CONNECTOR_POLL_HPD;
10114b6873aSAndrzej Hajda
10214b6873aSAndrzej Hajda ret = drm_connector_init(encoder->dev, connector,
10314b6873aSAndrzej Hajda &exynos_dpi_connector_funcs,
10414b6873aSAndrzej Hajda DRM_MODE_CONNECTOR_VGA);
10514b6873aSAndrzej Hajda if (ret) {
1066f83d208SInki Dae DRM_DEV_ERROR(ctx->dev,
1076f83d208SInki Dae "failed to initialize connector with drm\n");
10814b6873aSAndrzej Hajda return ret;
10914b6873aSAndrzej Hajda }
11014b6873aSAndrzej Hajda
11114b6873aSAndrzej Hajda drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
112cde4c44dSDaniel Vetter drm_connector_attach_encoder(connector, encoder);
11314b6873aSAndrzej Hajda
11414b6873aSAndrzej Hajda return 0;
11514b6873aSAndrzej Hajda }
11614b6873aSAndrzej Hajda
exynos_dpi_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)1172b8376c8SGustavo Padovan static void exynos_dpi_mode_set(struct drm_encoder *encoder,
1182b8376c8SGustavo Padovan struct drm_display_mode *mode,
1192b8376c8SGustavo Padovan struct drm_display_mode *adjusted_mode)
1202b8376c8SGustavo Padovan {
1212b8376c8SGustavo Padovan }
1222b8376c8SGustavo Padovan
exynos_dpi_enable(struct drm_encoder * encoder)1232b8376c8SGustavo Padovan static void exynos_dpi_enable(struct drm_encoder *encoder)
12414b6873aSAndrzej Hajda {
125cf67cc9aSGustavo Padovan struct exynos_dpi *ctx = encoder_to_dpi(encoder);
126b6595dc7SGustavo Padovan
12739bbde9cSAjay Kumar if (ctx->panel) {
12839bbde9cSAjay Kumar drm_panel_prepare(ctx->panel);
12914b6873aSAndrzej Hajda drm_panel_enable(ctx->panel);
13014b6873aSAndrzej Hajda }
13139bbde9cSAjay Kumar }
13214b6873aSAndrzej Hajda
exynos_dpi_disable(struct drm_encoder * encoder)1332b8376c8SGustavo Padovan static void exynos_dpi_disable(struct drm_encoder *encoder)
13414b6873aSAndrzej Hajda {
135cf67cc9aSGustavo Padovan struct exynos_dpi *ctx = encoder_to_dpi(encoder);
136b6595dc7SGustavo Padovan
13739bbde9cSAjay Kumar if (ctx->panel) {
13814b6873aSAndrzej Hajda drm_panel_disable(ctx->panel);
13939bbde9cSAjay Kumar drm_panel_unprepare(ctx->panel);
14039bbde9cSAjay Kumar }
14114b6873aSAndrzej Hajda }
14214b6873aSAndrzej Hajda
143800ba2b5SVille Syrjälä static const struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = {
1442b8376c8SGustavo Padovan .mode_set = exynos_dpi_mode_set,
145b6595dc7SGustavo Padovan .enable = exynos_dpi_enable,
146b6595dc7SGustavo Padovan .disable = exynos_dpi_disable,
14714b6873aSAndrzej Hajda };
14814b6873aSAndrzej Hajda
14914b6873aSAndrzej Hajda enum {
15014b6873aSAndrzej Hajda FIMD_PORT_IN0,
15114b6873aSAndrzej Hajda FIMD_PORT_IN1,
15214b6873aSAndrzej Hajda FIMD_PORT_IN2,
15314b6873aSAndrzej Hajda FIMD_PORT_RGB,
15414b6873aSAndrzej Hajda FIMD_PORT_WRB,
15514b6873aSAndrzej Hajda };
15614b6873aSAndrzej Hajda
exynos_dpi_parse_dt(struct exynos_dpi * ctx)15714b6873aSAndrzej Hajda static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
15814b6873aSAndrzej Hajda {
15914b6873aSAndrzej Hajda struct device *dev = ctx->dev;
16014b6873aSAndrzej Hajda struct device_node *dn = dev->of_node;
16114b6873aSAndrzej Hajda struct device_node *np;
16214b6873aSAndrzej Hajda
16386418f90SRob Herring ctx->panel_node = of_graph_get_remote_node(dn, FIMD_PORT_RGB, 0);
16414b6873aSAndrzej Hajda
16514b6873aSAndrzej Hajda np = of_get_child_by_name(dn, "display-timings");
16614b6873aSAndrzej Hajda if (np) {
16714b6873aSAndrzej Hajda struct videomode *vm;
16814b6873aSAndrzej Hajda int ret;
16914b6873aSAndrzej Hajda
17014b6873aSAndrzej Hajda of_node_put(np);
17114b6873aSAndrzej Hajda
17214b6873aSAndrzej Hajda vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL);
17314b6873aSAndrzej Hajda if (!vm)
17414b6873aSAndrzej Hajda return -ENOMEM;
17514b6873aSAndrzej Hajda
17614b6873aSAndrzej Hajda ret = of_get_videomode(dn, vm, 0);
177000cc920SAndrzej Hajda if (ret < 0) {
178000cc920SAndrzej Hajda devm_kfree(dev, vm);
17914b6873aSAndrzej Hajda return ret;
180000cc920SAndrzej Hajda }
18114b6873aSAndrzej Hajda
18214b6873aSAndrzej Hajda ctx->vm = vm;
18314b6873aSAndrzej Hajda
18414b6873aSAndrzej Hajda return 0;
18514b6873aSAndrzej Hajda }
18614b6873aSAndrzej Hajda
18714b6873aSAndrzej Hajda if (!ctx->panel_node)
18814b6873aSAndrzej Hajda return -EINVAL;
18914b6873aSAndrzej Hajda
19014b6873aSAndrzej Hajda return 0;
19114b6873aSAndrzej Hajda }
19214b6873aSAndrzej Hajda
exynos_dpi_bind(struct drm_device * dev,struct drm_encoder * encoder)1932b8376c8SGustavo Padovan int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder)
194a2986e80SGustavo Padovan {
195a2986e80SGustavo Padovan int ret;
196a2986e80SGustavo Padovan
1973e1fe32dSThomas Zimmermann drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
1982b8376c8SGustavo Padovan
1992b8376c8SGustavo Padovan drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs);
2002b8376c8SGustavo Padovan
2011ca582f1SAndrzej Hajda ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
2021ca582f1SAndrzej Hajda if (ret < 0)
2031ca582f1SAndrzej Hajda return ret;
2041ca582f1SAndrzej Hajda
2052b8376c8SGustavo Padovan ret = exynos_dpi_create_connector(encoder);
206a2986e80SGustavo Padovan if (ret) {
2076f83d208SInki Dae DRM_DEV_ERROR(encoder_to_dpi(encoder)->dev,
2086f83d208SInki Dae "failed to create connector ret = %d\n", ret);
2092b8376c8SGustavo Padovan drm_encoder_cleanup(encoder);
210a2986e80SGustavo Padovan return ret;
211a2986e80SGustavo Padovan }
212a2986e80SGustavo Padovan
213a2986e80SGustavo Padovan return 0;
214a2986e80SGustavo Padovan }
215a2986e80SGustavo Padovan
exynos_dpi_probe(struct device * dev)2162b8376c8SGustavo Padovan struct drm_encoder *exynos_dpi_probe(struct device *dev)
21714b6873aSAndrzej Hajda {
21814b6873aSAndrzej Hajda struct exynos_dpi *ctx;
21914b6873aSAndrzej Hajda int ret;
22014b6873aSAndrzej Hajda
22114b6873aSAndrzej Hajda ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
22214b6873aSAndrzej Hajda if (!ctx)
2234cfde1f2SAndrzej Hajda return ERR_PTR(-ENOMEM);
22414b6873aSAndrzej Hajda
22514b6873aSAndrzej Hajda ctx->dev = dev;
22614b6873aSAndrzej Hajda
22714b6873aSAndrzej Hajda ret = exynos_dpi_parse_dt(ctx);
228000cc920SAndrzej Hajda if (ret < 0) {
229000cc920SAndrzej Hajda devm_kfree(dev, ctx);
23086650408SAndrzej Hajda return NULL;
23114b6873aSAndrzej Hajda }
23214b6873aSAndrzej Hajda
233000cc920SAndrzej Hajda if (ctx->panel_node) {
234000cc920SAndrzej Hajda ctx->panel = of_drm_find_panel(ctx->panel_node);
2355fa8e4a2SBoris Brezillon if (IS_ERR(ctx->panel))
2365fa8e4a2SBoris Brezillon return ERR_CAST(ctx->panel);
237000cc920SAndrzej Hajda }
238000cc920SAndrzej Hajda
239cf67cc9aSGustavo Padovan return &ctx->encoder;
240000cc920SAndrzej Hajda }
241000cc920SAndrzej Hajda
exynos_dpi_remove(struct drm_encoder * encoder)2422b8376c8SGustavo Padovan int exynos_dpi_remove(struct drm_encoder *encoder)
24314b6873aSAndrzej Hajda {
244cf67cc9aSGustavo Padovan struct exynos_dpi *ctx = encoder_to_dpi(encoder);
245f37cd5e8SInki Dae
246cf67cc9aSGustavo Padovan exynos_dpi_disable(&ctx->encoder);
24790eac897SAndrzej Hajda
24814b6873aSAndrzej Hajda return 0;
24914b6873aSAndrzej Hajda }
250