1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4 * Author: Rob Clark <rob@ti.com>
7 #include <linux/list.h>
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 #include <drm/drm_panel.h>
21 #define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
23 /* The encoder and connector both map to same dssdev.. the encoder
24 * handles the 'active' parts, ie. anything the modifies the state
25 * of the hw, and the connector handles the 'read-only' parts, like
26 * detecting connection and reading edid.
29 struct drm_encoder base
;
30 struct omap_dss_device
*output
;
33 static void omap_encoder_destroy(struct drm_encoder
*encoder
)
35 struct omap_encoder
*omap_encoder
= to_omap_encoder(encoder
);
37 drm_encoder_cleanup(encoder
);
41 static const struct drm_encoder_funcs omap_encoder_funcs
= {
42 .destroy
= omap_encoder_destroy
,
45 static void omap_encoder_update_videomode_flags(struct videomode
*vm
,
48 if (!(vm
->flags
& (DISPLAY_FLAGS_DE_LOW
|
49 DISPLAY_FLAGS_DE_HIGH
))) {
50 if (bus_flags
& DRM_BUS_FLAG_DE_LOW
)
51 vm
->flags
|= DISPLAY_FLAGS_DE_LOW
;
52 else if (bus_flags
& DRM_BUS_FLAG_DE_HIGH
)
53 vm
->flags
|= DISPLAY_FLAGS_DE_HIGH
;
56 if (!(vm
->flags
& (DISPLAY_FLAGS_PIXDATA_POSEDGE
|
57 DISPLAY_FLAGS_PIXDATA_NEGEDGE
))) {
58 if (bus_flags
& DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE
)
59 vm
->flags
|= DISPLAY_FLAGS_PIXDATA_POSEDGE
;
60 else if (bus_flags
& DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE
)
61 vm
->flags
|= DISPLAY_FLAGS_PIXDATA_NEGEDGE
;
64 if (!(vm
->flags
& (DISPLAY_FLAGS_SYNC_POSEDGE
|
65 DISPLAY_FLAGS_SYNC_NEGEDGE
))) {
66 if (bus_flags
& DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE
)
67 vm
->flags
|= DISPLAY_FLAGS_SYNC_POSEDGE
;
68 else if (bus_flags
& DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE
)
69 vm
->flags
|= DISPLAY_FLAGS_SYNC_NEGEDGE
;
73 static void omap_encoder_hdmi_mode_set(struct drm_connector
*connector
,
74 struct drm_encoder
*encoder
,
75 struct drm_display_mode
*adjusted_mode
)
77 struct omap_encoder
*omap_encoder
= to_omap_encoder(encoder
);
78 struct omap_dss_device
*dssdev
= omap_encoder
->output
;
81 hdmi_mode
= omap_connector_get_hdmi_mode(connector
);
83 if (dssdev
->ops
->hdmi
.set_hdmi_mode
)
84 dssdev
->ops
->hdmi
.set_hdmi_mode(dssdev
, hdmi_mode
);
86 if (hdmi_mode
&& dssdev
->ops
->hdmi
.set_infoframe
) {
87 struct hdmi_avi_infoframe avi
;
90 r
= drm_hdmi_avi_infoframe_from_display_mode(&avi
, connector
,
93 dssdev
->ops
->hdmi
.set_infoframe(dssdev
, &avi
);
97 static void omap_encoder_mode_set(struct drm_encoder
*encoder
,
98 struct drm_display_mode
*mode
,
99 struct drm_display_mode
*adjusted_mode
)
101 struct omap_encoder
*omap_encoder
= to_omap_encoder(encoder
);
102 struct omap_dss_device
*output
= omap_encoder
->output
;
103 struct omap_dss_device
*dssdev
;
104 struct drm_device
*dev
= encoder
->dev
;
105 struct drm_connector
*connector
;
106 struct drm_bridge
*bridge
;
107 struct videomode vm
= { 0 };
110 list_for_each_entry(connector
, &dev
->mode_config
.connector_list
, head
) {
111 if (connector
->encoder
== encoder
)
115 drm_display_mode_to_videomode(adjusted_mode
, &vm
);
118 * HACK: This fixes the vm flags.
119 * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags and
120 * they get lost when converting back and forth between struct
121 * drm_display_mode and struct videomode. The hack below goes and
122 * fetches the missing flags.
124 * A better solution is to use DRM's bus-flags through the whole driver.
126 for (dssdev
= output
; dssdev
; dssdev
= dssdev
->next
)
127 omap_encoder_update_videomode_flags(&vm
, dssdev
->bus_flags
);
129 for (bridge
= output
->bridge
; bridge
;
130 bridge
= drm_bridge_get_next_bridge(bridge
)) {
131 if (!bridge
->timings
)
134 bus_flags
= bridge
->timings
->input_bus_flags
;
135 omap_encoder_update_videomode_flags(&vm
, bus_flags
);
138 bus_flags
= connector
->display_info
.bus_flags
;
139 omap_encoder_update_videomode_flags(&vm
, bus_flags
);
141 /* Set timings for all devices in the display pipeline. */
142 dss_mgr_set_timings(output
, &vm
);
144 for (dssdev
= output
; dssdev
; dssdev
= dssdev
->next
) {
145 if (dssdev
->ops
->set_timings
)
146 dssdev
->ops
->set_timings(dssdev
, adjusted_mode
);
149 /* Set the HDMI mode and HDMI infoframe if applicable. */
150 if (output
->type
== OMAP_DISPLAY_TYPE_HDMI
)
151 omap_encoder_hdmi_mode_set(connector
, encoder
, adjusted_mode
);
154 static void omap_encoder_disable(struct drm_encoder
*encoder
)
156 struct omap_encoder
*omap_encoder
= to_omap_encoder(encoder
);
157 struct omap_dss_device
*dssdev
= omap_encoder
->output
;
158 struct drm_device
*dev
= encoder
->dev
;
160 dev_dbg(dev
->dev
, "disable(%s)\n", dssdev
->name
);
162 /* Disable the panel if present. */
164 drm_panel_disable(dssdev
->panel
);
165 drm_panel_unprepare(dssdev
->panel
);
169 * Disable the chain of external devices, starting at the one at the
170 * internal encoder's output.
172 omapdss_device_disable(dssdev
->next
);
175 * Disable the internal encoder. This will disable the DSS output. The
176 * DSI is treated as an exception as DSI pipelines still use the legacy
177 * flow where the pipeline output controls the encoder.
179 if (dssdev
->type
!= OMAP_DISPLAY_TYPE_DSI
) {
180 dssdev
->ops
->disable(dssdev
);
181 dssdev
->state
= OMAP_DSS_DISPLAY_DISABLED
;
185 * Perform the post-disable operations on the chain of external devices
186 * to complete the display pipeline disable.
188 omapdss_device_post_disable(dssdev
->next
);
191 static void omap_encoder_enable(struct drm_encoder
*encoder
)
193 struct omap_encoder
*omap_encoder
= to_omap_encoder(encoder
);
194 struct omap_dss_device
*dssdev
= omap_encoder
->output
;
195 struct drm_device
*dev
= encoder
->dev
;
197 dev_dbg(dev
->dev
, "enable(%s)\n", dssdev
->name
);
199 /* Prepare the chain of external devices for pipeline enable. */
200 omapdss_device_pre_enable(dssdev
->next
);
203 * Enable the internal encoder. This will enable the DSS output. The
204 * DSI is treated as an exception as DSI pipelines still use the legacy
205 * flow where the pipeline output controls the encoder.
207 if (dssdev
->type
!= OMAP_DISPLAY_TYPE_DSI
) {
208 dssdev
->ops
->enable(dssdev
);
209 dssdev
->state
= OMAP_DSS_DISPLAY_ACTIVE
;
213 * Enable the chain of external devices, starting at the one at the
214 * internal encoder's output.
216 omapdss_device_enable(dssdev
->next
);
218 /* Enable the panel if present. */
220 drm_panel_prepare(dssdev
->panel
);
221 drm_panel_enable(dssdev
->panel
);
225 static int omap_encoder_atomic_check(struct drm_encoder
*encoder
,
226 struct drm_crtc_state
*crtc_state
,
227 struct drm_connector_state
*conn_state
)
229 struct omap_encoder
*omap_encoder
= to_omap_encoder(encoder
);
230 enum drm_mode_status status
;
232 status
= omap_connector_mode_fixup(omap_encoder
->output
,
234 &crtc_state
->adjusted_mode
);
235 if (status
!= MODE_OK
) {
236 dev_err(encoder
->dev
->dev
, "invalid timings: %d\n", status
);
243 static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs
= {
244 .mode_set
= omap_encoder_mode_set
,
245 .disable
= omap_encoder_disable
,
246 .enable
= omap_encoder_enable
,
247 .atomic_check
= omap_encoder_atomic_check
,
250 /* initialize encoder */
251 struct drm_encoder
*omap_encoder_init(struct drm_device
*dev
,
252 struct omap_dss_device
*output
)
254 struct drm_encoder
*encoder
= NULL
;
255 struct omap_encoder
*omap_encoder
;
257 omap_encoder
= kzalloc(sizeof(*omap_encoder
), GFP_KERNEL
);
261 omap_encoder
->output
= output
;
263 encoder
= &omap_encoder
->base
;
265 drm_encoder_init(dev
, encoder
, &omap_encoder_funcs
,
266 DRM_MODE_ENCODER_TMDS
, NULL
);
267 drm_encoder_helper_add(encoder
, &omap_encoder_helper_funcs
);
273 omap_encoder_destroy(encoder
);