diff options
Diffstat (limited to 'drivers/gpu/drm/imx/parallel-display.c')
-rw-r--r-- | drivers/gpu/drm/imx/parallel-display.c | 149 |
1 files changed, 95 insertions, 54 deletions
diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 2d1fd02cd3d6..1dad297b01fd 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/component.h> | 16 | #include <linux/component.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <drm/drmP.h> | 18 | #include <drm/drmP.h> |
19 | #include <drm/drm_atomic_helper.h> | ||
19 | #include <drm/drm_fb_helper.h> | 20 | #include <drm/drm_fb_helper.h> |
20 | #include <drm/drm_crtc_helper.h> | 21 | #include <drm/drm_crtc_helper.h> |
21 | #include <drm/drm_panel.h> | 22 | #include <drm/drm_panel.h> |
@@ -25,9 +26,6 @@ | |||
25 | 26 | ||
26 | #include "imx-drm.h" | 27 | #include "imx-drm.h" |
27 | 28 | ||
28 | #define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector) | ||
29 | #define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder) | ||
30 | |||
31 | struct imx_parallel_display { | 29 | struct imx_parallel_display { |
32 | struct drm_connector connector; | 30 | struct drm_connector connector; |
33 | struct drm_encoder encoder; | 31 | struct drm_encoder encoder; |
@@ -37,8 +35,19 @@ struct imx_parallel_display { | |||
37 | u32 bus_format; | 35 | u32 bus_format; |
38 | struct drm_display_mode mode; | 36 | struct drm_display_mode mode; |
39 | struct drm_panel *panel; | 37 | struct drm_panel *panel; |
38 | struct drm_bridge *bridge; | ||
40 | }; | 39 | }; |
41 | 40 | ||
41 | static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c) | ||
42 | { | ||
43 | return container_of(c, struct imx_parallel_display, connector); | ||
44 | } | ||
45 | |||
46 | static inline struct imx_parallel_display *enc_to_imxpd(struct drm_encoder *e) | ||
47 | { | ||
48 | return container_of(e, struct imx_parallel_display, encoder); | ||
49 | } | ||
50 | |||
42 | static enum drm_connector_status imx_pd_connector_detect( | 51 | static enum drm_connector_status imx_pd_connector_detect( |
43 | struct drm_connector *connector, bool force) | 52 | struct drm_connector *connector, bool force) |
44 | { | 53 | { |
@@ -53,11 +62,7 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) | |||
53 | 62 | ||
54 | if (imxpd->panel && imxpd->panel->funcs && | 63 | if (imxpd->panel && imxpd->panel->funcs && |
55 | imxpd->panel->funcs->get_modes) { | 64 | imxpd->panel->funcs->get_modes) { |
56 | struct drm_display_info *di = &connector->display_info; | ||
57 | |||
58 | num_modes = imxpd->panel->funcs->get_modes(imxpd->panel); | 65 | num_modes = imxpd->panel->funcs->get_modes(imxpd->panel); |
59 | if (!imxpd->bus_format && di->num_bus_formats) | ||
60 | imxpd->bus_format = di->bus_formats[0]; | ||
61 | if (num_modes > 0) | 66 | if (num_modes > 0) |
62 | return num_modes; | 67 | return num_modes; |
63 | } | 68 | } |
@@ -69,10 +74,16 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) | |||
69 | 74 | ||
70 | if (np) { | 75 | if (np) { |
71 | struct drm_display_mode *mode = drm_mode_create(connector->dev); | 76 | struct drm_display_mode *mode = drm_mode_create(connector->dev); |
77 | int ret; | ||
72 | 78 | ||
73 | if (!mode) | 79 | if (!mode) |
74 | return -EINVAL; | 80 | return -EINVAL; |
75 | of_get_drm_display_mode(np, &imxpd->mode, OF_USE_NATIVE_MODE); | 81 | |
82 | ret = of_get_drm_display_mode(np, &imxpd->mode, | ||
83 | OF_USE_NATIVE_MODE); | ||
84 | if (ret) | ||
85 | return ret; | ||
86 | |||
76 | drm_mode_copy(mode, &imxpd->mode); | 87 | drm_mode_copy(mode, &imxpd->mode); |
77 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, | 88 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, |
78 | drm_mode_probed_add(connector, mode); | 89 | drm_mode_probed_add(connector, mode); |
@@ -90,24 +101,7 @@ static struct drm_encoder *imx_pd_connector_best_encoder( | |||
90 | return &imxpd->encoder; | 101 | return &imxpd->encoder; |
91 | } | 102 | } |
92 | 103 | ||
93 | static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode) | 104 | static void imx_pd_encoder_enable(struct drm_encoder *encoder) |
94 | { | ||
95 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
96 | |||
97 | if (mode != DRM_MODE_DPMS_ON) | ||
98 | drm_panel_disable(imxpd->panel); | ||
99 | else | ||
100 | drm_panel_enable(imxpd->panel); | ||
101 | } | ||
102 | |||
103 | static void imx_pd_encoder_prepare(struct drm_encoder *encoder) | ||
104 | { | ||
105 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
106 | imx_drm_set_bus_config(encoder, imxpd->bus_format, 2, 3, | ||
107 | imxpd->connector.display_info.bus_flags); | ||
108 | } | ||
109 | |||
110 | static void imx_pd_encoder_commit(struct drm_encoder *encoder) | ||
111 | { | 105 | { |
112 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | 106 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); |
113 | 107 | ||
@@ -115,12 +109,6 @@ static void imx_pd_encoder_commit(struct drm_encoder *encoder) | |||
115 | drm_panel_enable(imxpd->panel); | 109 | drm_panel_enable(imxpd->panel); |
116 | } | 110 | } |
117 | 111 | ||
118 | static void imx_pd_encoder_mode_set(struct drm_encoder *encoder, | ||
119 | struct drm_display_mode *orig_mode, | ||
120 | struct drm_display_mode *mode) | ||
121 | { | ||
122 | } | ||
123 | |||
124 | static void imx_pd_encoder_disable(struct drm_encoder *encoder) | 112 | static void imx_pd_encoder_disable(struct drm_encoder *encoder) |
125 | { | 113 | { |
126 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | 114 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); |
@@ -129,11 +117,33 @@ static void imx_pd_encoder_disable(struct drm_encoder *encoder) | |||
129 | drm_panel_unprepare(imxpd->panel); | 117 | drm_panel_unprepare(imxpd->panel); |
130 | } | 118 | } |
131 | 119 | ||
120 | static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder, | ||
121 | struct drm_crtc_state *crtc_state, | ||
122 | struct drm_connector_state *conn_state) | ||
123 | { | ||
124 | struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); | ||
125 | struct drm_display_info *di = &conn_state->connector->display_info; | ||
126 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
127 | |||
128 | imx_crtc_state->bus_flags = di->bus_flags; | ||
129 | if (!imxpd->bus_format && di->num_bus_formats) | ||
130 | imx_crtc_state->bus_format = di->bus_formats[0]; | ||
131 | else | ||
132 | imx_crtc_state->bus_format = imxpd->bus_format; | ||
133 | imx_crtc_state->di_hsync_pin = 2; | ||
134 | imx_crtc_state->di_vsync_pin = 3; | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
132 | static const struct drm_connector_funcs imx_pd_connector_funcs = { | 139 | static const struct drm_connector_funcs imx_pd_connector_funcs = { |
133 | .dpms = drm_helper_connector_dpms, | 140 | .dpms = drm_atomic_helper_connector_dpms, |
134 | .fill_modes = drm_helper_probe_single_connector_modes, | 141 | .fill_modes = drm_helper_probe_single_connector_modes, |
135 | .detect = imx_pd_connector_detect, | 142 | .detect = imx_pd_connector_detect, |
136 | .destroy = imx_drm_connector_destroy, | 143 | .destroy = imx_drm_connector_destroy, |
144 | .reset = drm_atomic_helper_connector_reset, | ||
145 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
146 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
137 | }; | 147 | }; |
138 | 148 | ||
139 | static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { | 149 | static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { |
@@ -146,20 +156,18 @@ static const struct drm_encoder_funcs imx_pd_encoder_funcs = { | |||
146 | }; | 156 | }; |
147 | 157 | ||
148 | static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { | 158 | static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { |
149 | .dpms = imx_pd_encoder_dpms, | 159 | .enable = imx_pd_encoder_enable, |
150 | .prepare = imx_pd_encoder_prepare, | ||
151 | .commit = imx_pd_encoder_commit, | ||
152 | .mode_set = imx_pd_encoder_mode_set, | ||
153 | .disable = imx_pd_encoder_disable, | 160 | .disable = imx_pd_encoder_disable, |
161 | .atomic_check = imx_pd_encoder_atomic_check, | ||
154 | }; | 162 | }; |
155 | 163 | ||
156 | static int imx_pd_register(struct drm_device *drm, | 164 | static int imx_pd_register(struct drm_device *drm, |
157 | struct imx_parallel_display *imxpd) | 165 | struct imx_parallel_display *imxpd) |
158 | { | 166 | { |
167 | struct drm_encoder *encoder = &imxpd->encoder; | ||
159 | int ret; | 168 | int ret; |
160 | 169 | ||
161 | ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder, | 170 | ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node); |
162 | imxpd->dev->of_node); | ||
163 | if (ret) | 171 | if (ret) |
164 | return ret; | 172 | return ret; |
165 | 173 | ||
@@ -170,19 +178,33 @@ static int imx_pd_register(struct drm_device *drm, | |||
170 | */ | 178 | */ |
171 | imxpd->connector.dpms = DRM_MODE_DPMS_OFF; | 179 | imxpd->connector.dpms = DRM_MODE_DPMS_OFF; |
172 | 180 | ||
173 | drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs); | 181 | drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs); |
174 | drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs, | 182 | drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs, |
175 | DRM_MODE_ENCODER_NONE, NULL); | 183 | DRM_MODE_ENCODER_NONE, NULL); |
176 | 184 | ||
177 | drm_connector_helper_add(&imxpd->connector, | 185 | if (!imxpd->bridge) { |
178 | &imx_pd_connector_helper_funcs); | 186 | drm_connector_helper_add(&imxpd->connector, |
179 | drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs, | 187 | &imx_pd_connector_helper_funcs); |
180 | DRM_MODE_CONNECTOR_VGA); | 188 | drm_connector_init(drm, &imxpd->connector, |
189 | &imx_pd_connector_funcs, | ||
190 | DRM_MODE_CONNECTOR_VGA); | ||
191 | } | ||
181 | 192 | ||
182 | if (imxpd->panel) | 193 | if (imxpd->panel) |
183 | drm_panel_attach(imxpd->panel, &imxpd->connector); | 194 | drm_panel_attach(imxpd->panel, &imxpd->connector); |
184 | 195 | ||
185 | drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); | 196 | if (imxpd->bridge) { |
197 | imxpd->bridge->encoder = encoder; | ||
198 | encoder->bridge = imxpd->bridge; | ||
199 | ret = drm_bridge_attach(drm, imxpd->bridge); | ||
200 | if (ret < 0) { | ||
201 | dev_err(imxpd->dev, "failed to attach bridge: %d\n", | ||
202 | ret); | ||
203 | return ret; | ||
204 | } | ||
205 | } else { | ||
206 | drm_mode_connector_attach_encoder(&imxpd->connector, encoder); | ||
207 | } | ||
186 | 208 | ||
187 | return 0; | 209 | return 0; |
188 | } | 210 | } |
@@ -195,6 +217,7 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) | |||
195 | const u8 *edidp; | 217 | const u8 *edidp; |
196 | struct imx_parallel_display *imxpd; | 218 | struct imx_parallel_display *imxpd; |
197 | int ret; | 219 | int ret; |
220 | u32 bus_format = 0; | ||
198 | const char *fmt; | 221 | const char *fmt; |
199 | 222 | ||
200 | imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); | 223 | imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); |
@@ -208,14 +231,15 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) | |||
208 | ret = of_property_read_string(np, "interface-pix-fmt", &fmt); | 231 | ret = of_property_read_string(np, "interface-pix-fmt", &fmt); |
209 | if (!ret) { | 232 | if (!ret) { |
210 | if (!strcmp(fmt, "rgb24")) | 233 | if (!strcmp(fmt, "rgb24")) |
211 | imxpd->bus_format = MEDIA_BUS_FMT_RGB888_1X24; | 234 | bus_format = MEDIA_BUS_FMT_RGB888_1X24; |
212 | else if (!strcmp(fmt, "rgb565")) | 235 | else if (!strcmp(fmt, "rgb565")) |
213 | imxpd->bus_format = MEDIA_BUS_FMT_RGB565_1X16; | 236 | bus_format = MEDIA_BUS_FMT_RGB565_1X16; |
214 | else if (!strcmp(fmt, "bgr666")) | 237 | else if (!strcmp(fmt, "bgr666")) |
215 | imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X18; | 238 | bus_format = MEDIA_BUS_FMT_RGB666_1X18; |
216 | else if (!strcmp(fmt, "lvds666")) | 239 | else if (!strcmp(fmt, "lvds666")) |
217 | imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI; | 240 | bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI; |
218 | } | 241 | } |
242 | imxpd->bus_format = bus_format; | ||
219 | 243 | ||
220 | /* port@1 is the output port */ | 244 | /* port@1 is the output port */ |
221 | ep = of_graph_get_endpoint_by_regs(np, 1, -1); | 245 | ep = of_graph_get_endpoint_by_regs(np, 1, -1); |
@@ -223,13 +247,30 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) | |||
223 | struct device_node *remote; | 247 | struct device_node *remote; |
224 | 248 | ||
225 | remote = of_graph_get_remote_port_parent(ep); | 249 | remote = of_graph_get_remote_port_parent(ep); |
250 | if (!remote) { | ||
251 | dev_warn(dev, "endpoint %s not connected\n", | ||
252 | ep->full_name); | ||
253 | of_node_put(ep); | ||
254 | return -ENODEV; | ||
255 | } | ||
226 | of_node_put(ep); | 256 | of_node_put(ep); |
227 | if (remote) { | 257 | |
228 | imxpd->panel = of_drm_find_panel(remote); | 258 | imxpd->panel = of_drm_find_panel(remote); |
229 | of_node_put(remote); | 259 | if (imxpd->panel) { |
260 | dev_dbg(dev, "found panel %s\n", remote->full_name); | ||
261 | } else { | ||
262 | imxpd->bridge = of_drm_find_bridge(remote); | ||
263 | if (imxpd->bridge) | ||
264 | dev_dbg(dev, "found bridge %s\n", | ||
265 | remote->full_name); | ||
230 | } | 266 | } |
231 | if (!imxpd->panel) | 267 | if (!imxpd->panel && !imxpd->bridge) { |
268 | dev_dbg(dev, "waiting for panel or bridge %s\n", | ||
269 | remote->full_name); | ||
270 | of_node_put(remote); | ||
232 | return -EPROBE_DEFER; | 271 | return -EPROBE_DEFER; |
272 | } | ||
273 | of_node_put(remote); | ||
233 | } | 274 | } |
234 | 275 | ||
235 | imxpd->dev = dev; | 276 | imxpd->dev = dev; |