Commit | Line | Data |
---|---|---|
cd5351f4 | 1 | /* |
8bb0daff | 2 | * drivers/gpu/drm/omapdrm/omap_encoder.c |
cd5351f4 RC |
3 | * |
4 | * Copyright (C) 2011 Texas Instruments | |
5 | * Author: Rob Clark <rob@ti.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published by | |
9 | * the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
4f930c0f TV |
20 | #include <drm/drm_edid.h> |
21 | ||
cd5351f4 RC |
22 | #include "omap_drv.h" |
23 | ||
24 | #include "drm_crtc.h" | |
25 | #include "drm_crtc_helper.h" | |
26 | ||
f5f9454c RC |
27 | #include <linux/list.h> |
28 | ||
29 | ||
cd5351f4 RC |
30 | /* |
31 | * encoder funcs | |
32 | */ | |
33 | ||
34 | #define to_omap_encoder(x) container_of(x, struct omap_encoder, base) | |
35 | ||
f5f9454c RC |
36 | /* The encoder and connector both map to same dssdev.. the encoder |
37 | * handles the 'active' parts, ie. anything the modifies the state | |
38 | * of the hw, and the connector handles the 'read-only' parts, like | |
39 | * detecting connection and reading edid. | |
40 | */ | |
cd5351f4 RC |
41 | struct omap_encoder { |
42 | struct drm_encoder base; | |
f5f9454c | 43 | struct omap_dss_device *dssdev; |
cd5351f4 RC |
44 | }; |
45 | ||
0d8f371f AT |
46 | struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder) |
47 | { | |
48 | struct omap_encoder *omap_encoder = to_omap_encoder(encoder); | |
49 | ||
50 | return omap_encoder->dssdev; | |
51 | } | |
52 | ||
cd5351f4 RC |
53 | static void omap_encoder_destroy(struct drm_encoder *encoder) |
54 | { | |
55 | struct omap_encoder *omap_encoder = to_omap_encoder(encoder); | |
ec72a81e AT |
56 | |
57 | omap_encoder_set_enabled(encoder, false); | |
58 | ||
cd5351f4 RC |
59 | drm_encoder_cleanup(encoder); |
60 | kfree(omap_encoder); | |
61 | } | |
62 | ||
f5f9454c RC |
63 | static const struct drm_encoder_funcs omap_encoder_funcs = { |
64 | .destroy = omap_encoder_destroy, | |
65 | }; | |
66 | ||
67 | /* | |
68 | * The CRTC drm_crtc_helper_set_mode() doesn't really give us the right | |
69 | * order.. the easiest way to work around this for now is to make all | |
70 | * the encoder-helper's no-op's and have the omap_crtc code take care | |
71 | * of the sequencing and call us in the right points. | |
72 | * | |
73 | * Eventually to handle connecting CRTCs to different encoders properly, | |
74 | * either the CRTC helpers need to change or we need to replace | |
75 | * drm_crtc_helper_set_mode(), but lets wait until atomic-modeset for | |
76 | * that. | |
77 | */ | |
78 | ||
cd5351f4 RC |
79 | static void omap_encoder_dpms(struct drm_encoder *encoder, int mode) |
80 | { | |
cd5351f4 RC |
81 | } |
82 | ||
83 | static bool omap_encoder_mode_fixup(struct drm_encoder *encoder, | |
e811f5ae | 84 | const struct drm_display_mode *mode, |
cd5351f4 RC |
85 | struct drm_display_mode *adjusted_mode) |
86 | { | |
cd5351f4 RC |
87 | return true; |
88 | } | |
89 | ||
90 | static void omap_encoder_mode_set(struct drm_encoder *encoder, | |
91 | struct drm_display_mode *mode, | |
92 | struct drm_display_mode *adjusted_mode) | |
93 | { | |
4f930c0f TV |
94 | struct drm_device *dev = encoder->dev; |
95 | struct omap_encoder *omap_encoder = to_omap_encoder(encoder); | |
96 | struct omap_dss_device *dssdev = omap_encoder->dssdev; | |
97 | struct drm_connector *connector; | |
98 | bool hdmi_mode; | |
99 | int r; | |
100 | ||
101 | hdmi_mode = false; | |
102 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
103 | if (connector->encoder == encoder) { | |
104 | hdmi_mode = omap_connector_get_hdmi_mode(connector); | |
105 | break; | |
106 | } | |
107 | } | |
108 | ||
109 | if (dssdev->driver->set_hdmi_mode) | |
110 | dssdev->driver->set_hdmi_mode(dssdev, hdmi_mode); | |
111 | ||
112 | if (hdmi_mode && dssdev->driver->set_hdmi_infoframe) { | |
113 | struct hdmi_avi_infoframe avi; | |
114 | ||
115 | r = drm_hdmi_avi_infoframe_from_display_mode(&avi, adjusted_mode); | |
116 | if (r == 0) | |
117 | dssdev->driver->set_hdmi_infoframe(dssdev, &avi); | |
118 | } | |
cd5351f4 RC |
119 | } |
120 | ||
121 | static void omap_encoder_prepare(struct drm_encoder *encoder) | |
122 | { | |
cd5351f4 RC |
123 | } |
124 | ||
125 | static void omap_encoder_commit(struct drm_encoder *encoder) | |
126 | { | |
cd5351f4 RC |
127 | } |
128 | ||
cd5351f4 RC |
129 | static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { |
130 | .dpms = omap_encoder_dpms, | |
131 | .mode_fixup = omap_encoder_mode_fixup, | |
132 | .mode_set = omap_encoder_mode_set, | |
133 | .prepare = omap_encoder_prepare, | |
134 | .commit = omap_encoder_commit, | |
135 | }; | |
136 | ||
f5f9454c RC |
137 | /* |
138 | * Instead of relying on the helpers for modeset, the omap_crtc code | |
139 | * calls these functions in the proper sequence. | |
140 | */ | |
141 | ||
142 | int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled) | |
cd5351f4 RC |
143 | { |
144 | struct omap_encoder *omap_encoder = to_omap_encoder(encoder); | |
f5f9454c RC |
145 | struct omap_dss_device *dssdev = omap_encoder->dssdev; |
146 | struct omap_dss_driver *dssdrv = dssdev->driver; | |
147 | ||
148 | if (enabled) { | |
149 | return dssdrv->enable(dssdev); | |
150 | } else { | |
151 | dssdrv->disable(dssdev); | |
152 | return 0; | |
153 | } | |
154 | } | |
155 | ||
156 | int omap_encoder_update(struct drm_encoder *encoder, | |
157 | struct omap_overlay_manager *mgr, | |
158 | struct omap_video_timings *timings) | |
159 | { | |
160 | struct drm_device *dev = encoder->dev; | |
161 | struct omap_encoder *omap_encoder = to_omap_encoder(encoder); | |
162 | struct omap_dss_device *dssdev = omap_encoder->dssdev; | |
163 | struct omap_dss_driver *dssdrv = dssdev->driver; | |
164 | int ret; | |
165 | ||
a73fdc64 | 166 | dssdev->src->manager = mgr; |
f5f9454c | 167 | |
bddabbe1 AT |
168 | if (dssdrv->check_timings) { |
169 | ret = dssdrv->check_timings(dssdev, timings); | |
170 | } else { | |
171 | struct omap_video_timings t = {0}; | |
172 | ||
173 | dssdrv->get_timings(dssdev, &t); | |
174 | ||
175 | if (memcmp(timings, &t, sizeof(struct omap_video_timings))) | |
176 | ret = -EINVAL; | |
177 | else | |
178 | ret = 0; | |
179 | } | |
180 | ||
f5f9454c RC |
181 | if (ret) { |
182 | dev_err(dev->dev, "could not set timings: %d\n", ret); | |
183 | return ret; | |
184 | } | |
185 | ||
bddabbe1 AT |
186 | if (dssdrv->set_timings) |
187 | dssdrv->set_timings(dssdev, timings); | |
f5f9454c RC |
188 | |
189 | return 0; | |
cd5351f4 RC |
190 | } |
191 | ||
192 | /* initialize encoder */ | |
193 | struct drm_encoder *omap_encoder_init(struct drm_device *dev, | |
f5f9454c | 194 | struct omap_dss_device *dssdev) |
cd5351f4 RC |
195 | { |
196 | struct drm_encoder *encoder = NULL; | |
197 | struct omap_encoder *omap_encoder; | |
cd5351f4 RC |
198 | |
199 | omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL); | |
78110bb8 | 200 | if (!omap_encoder) |
cd5351f4 | 201 | goto fail; |
cd5351f4 | 202 | |
f5f9454c RC |
203 | omap_encoder->dssdev = dssdev; |
204 | ||
cd5351f4 RC |
205 | encoder = &omap_encoder->base; |
206 | ||
207 | drm_encoder_init(dev, encoder, &omap_encoder_funcs, | |
208 | DRM_MODE_ENCODER_TMDS); | |
209 | drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs); | |
210 | ||
cd5351f4 RC |
211 | return encoder; |
212 | ||
213 | fail: | |
582bc28c | 214 | if (encoder) |
65b0bd06 | 215 | omap_encoder_destroy(encoder); |
cd5351f4 RC |
216 | |
217 | return NULL; | |
218 | } |