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