]> Git Repo - linux.git/blob - drivers/gpu/drm/omapdrm/omap_encoder.c
Merge tag 'apparmor-pr-2019-12-03' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / gpu / drm / omapdrm / omap_encoder.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4  * Author: Rob Clark <[email protected]>
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 #include <drm/drm_panel.h>
14
15 #include "omap_drv.h"
16
17 /*
18  * encoder funcs
19  */
20
21 #define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
22
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.
27  */
28 struct omap_encoder {
29         struct drm_encoder base;
30         struct omap_dss_device *output;
31 };
32
33 static void omap_encoder_destroy(struct drm_encoder *encoder)
34 {
35         struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
36
37         drm_encoder_cleanup(encoder);
38         kfree(omap_encoder);
39 }
40
41 static const struct drm_encoder_funcs omap_encoder_funcs = {
42         .destroy = omap_encoder_destroy,
43 };
44
45 static void omap_encoder_update_videomode_flags(struct videomode *vm,
46                                                 u32 bus_flags)
47 {
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;
54         }
55
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;
62         }
63
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;
70         }
71 }
72
73 static void omap_encoder_hdmi_mode_set(struct drm_connector *connector,
74                                        struct drm_encoder *encoder,
75                                        struct drm_display_mode *adjusted_mode)
76 {
77         struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
78         struct omap_dss_device *dssdev = omap_encoder->output;
79         bool hdmi_mode;
80
81         hdmi_mode = omap_connector_get_hdmi_mode(connector);
82
83         if (dssdev->ops->hdmi.set_hdmi_mode)
84                 dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
85
86         if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) {
87                 struct hdmi_avi_infoframe avi;
88                 int r;
89
90                 r = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
91                                                              adjusted_mode);
92                 if (r == 0)
93                         dssdev->ops->hdmi.set_infoframe(dssdev, &avi);
94         }
95 }
96
97 static void omap_encoder_mode_set(struct drm_encoder *encoder,
98                                   struct drm_display_mode *mode,
99                                   struct drm_display_mode *adjusted_mode)
100 {
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 };
108         u32 bus_flags;
109
110         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
111                 if (connector->encoder == encoder)
112                         break;
113         }
114
115         drm_display_mode_to_videomode(adjusted_mode, &vm);
116
117         /*
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.
123          *
124          * A better solution is to use DRM's bus-flags through the whole driver.
125          */
126         for (dssdev = output; dssdev; dssdev = dssdev->next)
127                 omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
128
129         for (bridge = output->bridge; bridge; bridge = bridge->next) {
130                 if (!bridge->timings)
131                         continue;
132
133                 bus_flags = bridge->timings->input_bus_flags;
134                 omap_encoder_update_videomode_flags(&vm, bus_flags);
135         }
136
137         bus_flags = connector->display_info.bus_flags;
138         omap_encoder_update_videomode_flags(&vm, bus_flags);
139
140         /* Set timings for all devices in the display pipeline. */
141         dss_mgr_set_timings(output, &vm);
142
143         for (dssdev = output; dssdev; dssdev = dssdev->next) {
144                 if (dssdev->ops->set_timings)
145                         dssdev->ops->set_timings(dssdev, adjusted_mode);
146         }
147
148         /* Set the HDMI mode and HDMI infoframe if applicable. */
149         if (output->type == OMAP_DISPLAY_TYPE_HDMI)
150                 omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode);
151 }
152
153 static void omap_encoder_disable(struct drm_encoder *encoder)
154 {
155         struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
156         struct omap_dss_device *dssdev = omap_encoder->output;
157         struct drm_device *dev = encoder->dev;
158
159         dev_dbg(dev->dev, "disable(%s)\n", dssdev->name);
160
161         /* Disable the panel if present. */
162         if (dssdev->panel) {
163                 drm_panel_disable(dssdev->panel);
164                 drm_panel_unprepare(dssdev->panel);
165         }
166
167         /*
168          * Disable the chain of external devices, starting at the one at the
169          * internal encoder's output.
170          */
171         omapdss_device_disable(dssdev->next);
172
173         /*
174          * Disable the internal encoder. This will disable the DSS output. The
175          * DSI is treated as an exception as DSI pipelines still use the legacy
176          * flow where the pipeline output controls the encoder.
177          */
178         if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
179                 dssdev->ops->disable(dssdev);
180                 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
181         }
182
183         /*
184          * Perform the post-disable operations on the chain of external devices
185          * to complete the display pipeline disable.
186          */
187         omapdss_device_post_disable(dssdev->next);
188 }
189
190 static void omap_encoder_enable(struct drm_encoder *encoder)
191 {
192         struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
193         struct omap_dss_device *dssdev = omap_encoder->output;
194         struct drm_device *dev = encoder->dev;
195
196         dev_dbg(dev->dev, "enable(%s)\n", dssdev->name);
197
198         /* Prepare the chain of external devices for pipeline enable. */
199         omapdss_device_pre_enable(dssdev->next);
200
201         /*
202          * Enable the internal encoder. This will enable the DSS output. The
203          * DSI is treated as an exception as DSI pipelines still use the legacy
204          * flow where the pipeline output controls the encoder.
205          */
206         if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
207                 dssdev->ops->enable(dssdev);
208                 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
209         }
210
211         /*
212          * Enable the chain of external devices, starting at the one at the
213          * internal encoder's output.
214          */
215         omapdss_device_enable(dssdev->next);
216
217         /* Enable the panel if present. */
218         if (dssdev->panel) {
219                 drm_panel_prepare(dssdev->panel);
220                 drm_panel_enable(dssdev->panel);
221         }
222 }
223
224 static int omap_encoder_atomic_check(struct drm_encoder *encoder,
225                                      struct drm_crtc_state *crtc_state,
226                                      struct drm_connector_state *conn_state)
227 {
228         struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
229         enum drm_mode_status status;
230
231         status = omap_connector_mode_fixup(omap_encoder->output,
232                                            &crtc_state->mode,
233                                            &crtc_state->adjusted_mode);
234         if (status != MODE_OK) {
235                 dev_err(encoder->dev->dev, "invalid timings: %d\n", status);
236                 return -EINVAL;
237         }
238
239         return 0;
240 }
241
242 static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
243         .mode_set = omap_encoder_mode_set,
244         .disable = omap_encoder_disable,
245         .enable = omap_encoder_enable,
246         .atomic_check = omap_encoder_atomic_check,
247 };
248
249 /* initialize encoder */
250 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
251                                       struct omap_dss_device *output)
252 {
253         struct drm_encoder *encoder = NULL;
254         struct omap_encoder *omap_encoder;
255
256         omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
257         if (!omap_encoder)
258                 goto fail;
259
260         omap_encoder->output = output;
261
262         encoder = &omap_encoder->base;
263
264         drm_encoder_init(dev, encoder, &omap_encoder_funcs,
265                          DRM_MODE_ENCODER_TMDS, NULL);
266         drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
267
268         return encoder;
269
270 fail:
271         if (encoder)
272                 omap_encoder_destroy(encoder);
273
274         return NULL;
275 }
This page took 0.050656 seconds and 4 git commands to generate.