1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4 * Author: Rob Clark <rob@ti.com>
5 */
6
7 #include <linux/list.h>
8
9 #include <drm/drm_bridge.h>
10 #include <drm/drm_crtc.h>
11 #include <drm/drm_modeset_helper_vtables.h>
12 #include <drm/drm_edid.h>
13
14 #include "omap_drv.h"
15
16 /*
17 * encoder funcs
18 */
19
20 #define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
21
22 /* The encoder and connector both map to same dssdev.. the encoder
23 * handles the 'active' parts, ie. anything the modifies the state
24 * of the hw, and the connector handles the 'read-only' parts, like
25 * detecting connection and reading edid.
26 */
27 struct omap_encoder {
28 struct drm_encoder base;
29 struct omap_dss_device *output;
30 };
31
omap_encoder_destroy(struct drm_encoder * encoder)32 static void omap_encoder_destroy(struct drm_encoder *encoder)
33 {
34 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
35
36 drm_encoder_cleanup(encoder);
37 kfree(omap_encoder);
38 }
39
40 static const struct drm_encoder_funcs omap_encoder_funcs = {
41 .destroy = omap_encoder_destroy,
42 };
43
omap_encoder_update_videomode_flags(struct videomode * vm,u32 bus_flags)44 static void omap_encoder_update_videomode_flags(struct videomode *vm,
45 u32 bus_flags)
46 {
47 if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW |
48 DISPLAY_FLAGS_DE_HIGH))) {
49 if (bus_flags & DRM_BUS_FLAG_DE_LOW)
50 vm->flags |= DISPLAY_FLAGS_DE_LOW;
51 else if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
52 vm->flags |= DISPLAY_FLAGS_DE_HIGH;
53 }
54
55 if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
56 DISPLAY_FLAGS_PIXDATA_NEGEDGE))) {
57 if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
58 vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
59 else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
60 vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
61 }
62
63 if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE |
64 DISPLAY_FLAGS_SYNC_NEGEDGE))) {
65 if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE)
66 vm->flags |= DISPLAY_FLAGS_SYNC_POSEDGE;
67 else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE)
68 vm->flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
69 }
70 }
71
omap_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)72 static void omap_encoder_mode_set(struct drm_encoder *encoder,
73 struct drm_display_mode *mode,
74 struct drm_display_mode *adjusted_mode)
75 {
76 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
77 struct omap_dss_device *output = omap_encoder->output;
78 struct omap_dss_device *dssdev;
79 struct drm_device *dev = encoder->dev;
80 struct drm_connector *connector;
81 struct drm_bridge *bridge;
82 struct videomode vm = { 0 };
83 u32 bus_flags;
84
85 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
86 if (connector->encoder == encoder)
87 break;
88 }
89
90 drm_display_mode_to_videomode(adjusted_mode, &vm);
91
92 /*
93 * HACK: This fixes the vm flags.
94 * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags and
95 * they get lost when converting back and forth between struct
96 * drm_display_mode and struct videomode. The hack below goes and
97 * fetches the missing flags.
98 *
99 * A better solution is to use DRM's bus-flags through the whole driver.
100 */
101 for (dssdev = output; dssdev; dssdev = dssdev->next)
102 omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
103
104 for (bridge = output->bridge; bridge;
105 bridge = drm_bridge_get_next_bridge(bridge)) {
106 if (!bridge->timings)
107 continue;
108
109 bus_flags = bridge->timings->input_bus_flags;
110 omap_encoder_update_videomode_flags(&vm, bus_flags);
111 }
112
113 bus_flags = connector->display_info.bus_flags;
114 omap_encoder_update_videomode_flags(&vm, bus_flags);
115
116 /* Set timings for the dss manager. */
117 dss_mgr_set_timings(output, &vm);
118 }
119
omap_encoder_disable(struct drm_encoder * encoder)120 static void omap_encoder_disable(struct drm_encoder *encoder)
121 {
122 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
123 struct omap_dss_device *dssdev = omap_encoder->output;
124 struct drm_device *dev = encoder->dev;
125
126 dev_dbg(dev->dev, "disable(%s)\n", dssdev->name);
127
128 /*
129 * Disable the chain of external devices, starting at the one at the
130 * internal encoder's output. This is used for DSI outputs only, as
131 * dssdev->next is NULL for all other outputs.
132 */
133 omapdss_device_disable(dssdev->next);
134 }
135
omap_encoder_enable(struct drm_encoder * encoder)136 static void omap_encoder_enable(struct drm_encoder *encoder)
137 {
138 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
139 struct omap_dss_device *dssdev = omap_encoder->output;
140 struct drm_device *dev = encoder->dev;
141
142 dev_dbg(dev->dev, "enable(%s)\n", dssdev->name);
143
144 /*
145 * Enable the chain of external devices, starting at the one at the
146 * internal encoder's output. This is used for DSI outputs only, as
147 * dssdev->next is NULL for all other outputs.
148 */
149 omapdss_device_enable(dssdev->next);
150 }
151
omap_encoder_atomic_check(struct drm_encoder * encoder,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)152 static int omap_encoder_atomic_check(struct drm_encoder *encoder,
153 struct drm_crtc_state *crtc_state,
154 struct drm_connector_state *conn_state)
155 {
156 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
157 enum drm_mode_status status;
158
159 status = omap_connector_mode_fixup(omap_encoder->output,
160 &crtc_state->mode,
161 &crtc_state->adjusted_mode);
162 if (status != MODE_OK) {
163 dev_err(encoder->dev->dev, "invalid timings: %d\n", status);
164 return -EINVAL;
165 }
166
167 return 0;
168 }
169
170 static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
171 .mode_set = omap_encoder_mode_set,
172 .disable = omap_encoder_disable,
173 .enable = omap_encoder_enable,
174 .atomic_check = omap_encoder_atomic_check,
175 };
176
177 /* initialize encoder */
omap_encoder_init(struct drm_device * dev,struct omap_dss_device * output)178 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
179 struct omap_dss_device *output)
180 {
181 struct drm_encoder *encoder = NULL;
182 struct omap_encoder *omap_encoder;
183
184 omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
185 if (!omap_encoder)
186 goto fail;
187
188 omap_encoder->output = output;
189
190 encoder = &omap_encoder->base;
191
192 drm_encoder_init(dev, encoder, &omap_encoder_funcs,
193 DRM_MODE_ENCODER_TMDS, NULL);
194 drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
195
196 return encoder;
197
198 fail:
199 if (encoder)
200 omap_encoder_destroy(encoder);
201
202 return NULL;
203 }
204