GNU Linux-libre 5.10.153-gnu1
[releases.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 <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
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
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
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
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
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
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 */
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 }