Commit | Line | Data |
---|---|---|
1c248b7d ID |
1 | /* exynos_drm_encoder.c |
2 | * | |
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | |
4 | * Authors: | |
5 | * Inki Dae <inki.dae@samsung.com> | |
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | |
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | |
8 | * | |
d81aecb5 ID |
9 | * This program is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. | |
1c248b7d ID |
13 | */ |
14 | ||
760285e7 DH |
15 | #include <drm/drmP.h> |
16 | #include <drm/drm_crtc_helper.h> | |
1c248b7d ID |
17 | |
18 | #include "exynos_drm_drv.h" | |
1c248b7d | 19 | #include "exynos_drm_encoder.h" |
50caf25c | 20 | #include "exynos_drm_connector.h" |
1c248b7d ID |
21 | |
22 | #define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ | |
23 | drm_encoder) | |
24 | ||
25 | /* | |
26 | * exynos specific encoder structure. | |
27 | * | |
28 | * @drm_encoder: encoder object. | |
29 | * @manager: specific encoder has its own manager to control a hardware | |
30 | * appropriately and we can access a hardware drawing on this manager. | |
ec05da95 | 31 | * @dpms: store the encoder dpms value. |
44c91697 | 32 | * @updated: indicate whether overlay data updating is needed or not. |
1c248b7d ID |
33 | */ |
34 | struct exynos_drm_encoder { | |
1b85a071 | 35 | struct drm_crtc *old_crtc; |
1c248b7d ID |
36 | struct drm_encoder drm_encoder; |
37 | struct exynos_drm_manager *manager; | |
44c91697 ID |
38 | int dpms; |
39 | bool updated; | |
1c248b7d ID |
40 | }; |
41 | ||
50caf25c | 42 | static void exynos_drm_connector_power(struct drm_encoder *encoder, int mode) |
1c248b7d ID |
43 | { |
44 | struct drm_device *dev = encoder->dev; | |
45 | struct drm_connector *connector; | |
ec05da95 ID |
46 | |
47 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
58f6aad7 | 48 | if (exynos_drm_best_encoder(connector) == encoder) { |
ec05da95 ID |
49 | DRM_DEBUG_KMS("connector[%d] dpms[%d]\n", |
50 | connector->base.id, mode); | |
50caf25c ID |
51 | |
52 | exynos_drm_display_power(connector, mode); | |
ec05da95 ID |
53 | } |
54 | } | |
55 | } | |
56 | ||
57 | static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) | |
58 | { | |
59 | struct drm_device *dev = encoder->dev; | |
60 | struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | |
396464df | 61 | struct exynos_drm_manager_ops *manager_ops = manager->ops; |
ec05da95 | 62 | struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); |
1c248b7d ID |
63 | |
64 | DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode); | |
65 | ||
ec05da95 ID |
66 | if (exynos_encoder->dpms == mode) { |
67 | DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); | |
68 | return; | |
69 | } | |
70 | ||
71 | mutex_lock(&dev->struct_mutex); | |
72 | ||
396464df JS |
73 | switch (mode) { |
74 | case DRM_MODE_DPMS_ON: | |
ec05da95 | 75 | if (manager_ops && manager_ops->apply) |
44c91697 ID |
76 | if (!exynos_encoder->updated) |
77 | manager_ops->apply(manager->dev); | |
78 | ||
50caf25c | 79 | exynos_drm_connector_power(encoder, mode); |
ec05da95 | 80 | exynos_encoder->dpms = mode; |
396464df JS |
81 | break; |
82 | case DRM_MODE_DPMS_STANDBY: | |
83 | case DRM_MODE_DPMS_SUSPEND: | |
84 | case DRM_MODE_DPMS_OFF: | |
50caf25c | 85 | exynos_drm_connector_power(encoder, mode); |
ec05da95 | 86 | exynos_encoder->dpms = mode; |
44c91697 | 87 | exynos_encoder->updated = false; |
396464df JS |
88 | break; |
89 | default: | |
90 | DRM_ERROR("unspecified mode %d\n", mode); | |
91 | break; | |
92 | } | |
93 | ||
ec05da95 | 94 | mutex_unlock(&dev->struct_mutex); |
1c248b7d ID |
95 | } |
96 | ||
97 | static bool | |
98 | exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, | |
e811f5ae | 99 | const struct drm_display_mode *mode, |
1c248b7d ID |
100 | struct drm_display_mode *adjusted_mode) |
101 | { | |
1de425b0 ID |
102 | struct drm_device *dev = encoder->dev; |
103 | struct drm_connector *connector; | |
104 | struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); | |
105 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | |
106 | ||
1c248b7d ID |
107 | DRM_DEBUG_KMS("%s\n", __FILE__); |
108 | ||
1de425b0 ID |
109 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
110 | if (connector->encoder == encoder) | |
111 | if (manager_ops && manager_ops->mode_fixup) | |
112 | manager_ops->mode_fixup(manager->dev, connector, | |
113 | mode, adjusted_mode); | |
114 | } | |
1c248b7d ID |
115 | |
116 | return true; | |
117 | } | |
118 | ||
1b85a071 ID |
119 | static void disable_plane_to_crtc(struct drm_device *dev, |
120 | struct drm_crtc *old_crtc, | |
121 | struct drm_crtc *new_crtc) | |
122 | { | |
123 | struct drm_plane *plane; | |
124 | ||
125 | /* | |
126 | * if old_crtc isn't same as encoder->crtc then it means that | |
127 | * user changed crtc id to another one so the plane to old_crtc | |
128 | * should be disabled and plane->crtc should be set to new_crtc | |
129 | * (encoder->crtc) | |
130 | */ | |
131 | list_for_each_entry(plane, &dev->mode_config.plane_list, head) { | |
132 | if (plane->crtc == old_crtc) { | |
133 | /* | |
134 | * do not change below call order. | |
135 | * | |
136 | * plane->funcs->disable_plane call checks | |
137 | * if encoder->crtc is same as plane->crtc and if same | |
138 | * then overlay_ops->disable callback will be called | |
139 | * to diasble current hw overlay so plane->crtc should | |
140 | * have new_crtc because new_crtc was set to | |
141 | * encoder->crtc in advance. | |
142 | */ | |
143 | plane->crtc = new_crtc; | |
144 | plane->funcs->disable_plane(plane); | |
145 | } | |
146 | } | |
147 | } | |
148 | ||
1c248b7d ID |
149 | static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, |
150 | struct drm_display_mode *mode, | |
151 | struct drm_display_mode *adjusted_mode) | |
152 | { | |
153 | struct drm_device *dev = encoder->dev; | |
154 | struct drm_connector *connector; | |
1b85a071 ID |
155 | struct exynos_drm_manager *manager; |
156 | struct exynos_drm_manager_ops *manager_ops; | |
1c248b7d ID |
157 | |
158 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
159 | ||
1c248b7d | 160 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
1b85a071 ID |
161 | if (connector->encoder == encoder) { |
162 | struct exynos_drm_encoder *exynos_encoder; | |
163 | ||
164 | exynos_encoder = to_exynos_encoder(encoder); | |
165 | ||
166 | if (exynos_encoder->old_crtc != encoder->crtc && | |
167 | exynos_encoder->old_crtc) { | |
168 | ||
169 | /* | |
170 | * disable a plane to old crtc and change | |
171 | * crtc of the plane to new one. | |
172 | */ | |
173 | disable_plane_to_crtc(dev, | |
174 | exynos_encoder->old_crtc, | |
175 | encoder->crtc); | |
176 | } | |
177 | ||
178 | manager = exynos_drm_get_manager(encoder); | |
179 | manager_ops = manager->ops; | |
180 | ||
1c248b7d | 181 | if (manager_ops && manager_ops->mode_set) |
1de425b0 ID |
182 | manager_ops->mode_set(manager->dev, |
183 | adjusted_mode); | |
1b85a071 ID |
184 | |
185 | exynos_encoder->old_crtc = encoder->crtc; | |
186 | } | |
1c248b7d ID |
187 | } |
188 | } | |
189 | ||
190 | static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) | |
191 | { | |
192 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
193 | ||
194 | /* drm framework doesn't check NULL. */ | |
195 | } | |
196 | ||
197 | static void exynos_drm_encoder_commit(struct drm_encoder *encoder) | |
198 | { | |
44c91697 ID |
199 | struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); |
200 | struct exynos_drm_manager *manager = exynos_encoder->manager; | |
1c248b7d | 201 | struct exynos_drm_manager_ops *manager_ops = manager->ops; |
1c248b7d ID |
202 | |
203 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
204 | ||
205 | if (manager_ops && manager_ops->commit) | |
206 | manager_ops->commit(manager->dev); | |
44c91697 ID |
207 | |
208 | /* | |
209 | * this will avoid one issue that overlay data is updated to | |
210 | * real hardware two times. | |
211 | * And this variable will be used to check if the data was | |
212 | * already updated or not by exynos_drm_encoder_dpms function. | |
213 | */ | |
214 | exynos_encoder->updated = true; | |
8e8755dd ID |
215 | |
216 | /* | |
217 | * In case of setcrtc, there is no way to update encoder's dpms | |
218 | * so update it here. | |
219 | */ | |
220 | exynos_encoder->dpms = DRM_MODE_DPMS_ON; | |
1c248b7d ID |
221 | } |
222 | ||
1daa892c ID |
223 | void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb) |
224 | { | |
225 | struct exynos_drm_encoder *exynos_encoder; | |
f74085a9 | 226 | struct exynos_drm_manager_ops *ops; |
1daa892c ID |
227 | struct drm_device *dev = fb->dev; |
228 | struct drm_encoder *encoder; | |
229 | ||
230 | /* | |
231 | * make sure that overlay data are updated to real hardware | |
232 | * for all encoders. | |
233 | */ | |
234 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
235 | exynos_encoder = to_exynos_encoder(encoder); | |
f74085a9 | 236 | ops = exynos_encoder->manager->ops; |
1daa892c ID |
237 | |
238 | /* | |
239 | * wait for vblank interrupt | |
240 | * - this makes sure that overlay data are updated to | |
241 | * real hardware. | |
242 | */ | |
f74085a9 P |
243 | if (ops->wait_for_vblank) |
244 | ops->wait_for_vblank(exynos_encoder->manager->dev); | |
1daa892c ID |
245 | } |
246 | } | |
247 | ||
248 | ||
bcf4cef9 ID |
249 | static void exynos_drm_encoder_disable(struct drm_encoder *encoder) |
250 | { | |
251 | struct drm_plane *plane; | |
252 | struct drm_device *dev = encoder->dev; | |
253 | ||
254 | exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); | |
255 | ||
256 | /* all planes connected to this encoder should be also disabled. */ | |
257 | list_for_each_entry(plane, &dev->mode_config.plane_list, head) { | |
258 | if (plane->crtc == encoder->crtc) | |
259 | plane->funcs->disable_plane(plane); | |
260 | } | |
261 | } | |
262 | ||
1c248b7d ID |
263 | static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { |
264 | .dpms = exynos_drm_encoder_dpms, | |
265 | .mode_fixup = exynos_drm_encoder_mode_fixup, | |
266 | .mode_set = exynos_drm_encoder_mode_set, | |
267 | .prepare = exynos_drm_encoder_prepare, | |
268 | .commit = exynos_drm_encoder_commit, | |
bcf4cef9 | 269 | .disable = exynos_drm_encoder_disable, |
1c248b7d ID |
270 | }; |
271 | ||
272 | static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) | |
273 | { | |
274 | struct exynos_drm_encoder *exynos_encoder = | |
275 | to_exynos_encoder(encoder); | |
276 | ||
277 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
278 | ||
279 | exynos_encoder->manager->pipe = -1; | |
280 | ||
281 | drm_encoder_cleanup(encoder); | |
1c248b7d ID |
282 | kfree(exynos_encoder); |
283 | } | |
284 | ||
285 | static struct drm_encoder_funcs exynos_encoder_funcs = { | |
286 | .destroy = exynos_drm_encoder_destroy, | |
287 | }; | |
288 | ||
d081f566 ID |
289 | static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) |
290 | { | |
291 | struct drm_encoder *clone; | |
292 | struct drm_device *dev = encoder->dev; | |
293 | struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); | |
294 | struct exynos_drm_display_ops *display_ops = | |
295 | exynos_encoder->manager->display_ops; | |
296 | unsigned int clone_mask = 0; | |
297 | int cnt = 0; | |
298 | ||
299 | list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { | |
300 | switch (display_ops->type) { | |
301 | case EXYNOS_DISPLAY_TYPE_LCD: | |
302 | case EXYNOS_DISPLAY_TYPE_HDMI: | |
b73d1230 | 303 | case EXYNOS_DISPLAY_TYPE_VIDI: |
d081f566 ID |
304 | clone_mask |= (1 << (cnt++)); |
305 | break; | |
306 | default: | |
307 | continue; | |
308 | } | |
309 | } | |
310 | ||
311 | return clone_mask; | |
312 | } | |
313 | ||
314 | void exynos_drm_encoder_setup(struct drm_device *dev) | |
315 | { | |
316 | struct drm_encoder *encoder; | |
317 | ||
318 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
319 | ||
320 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) | |
321 | encoder->possible_clones = exynos_drm_encoder_clones(encoder); | |
322 | } | |
323 | ||
1c248b7d ID |
324 | struct drm_encoder * |
325 | exynos_drm_encoder_create(struct drm_device *dev, | |
326 | struct exynos_drm_manager *manager, | |
327 | unsigned int possible_crtcs) | |
328 | { | |
329 | struct drm_encoder *encoder; | |
330 | struct exynos_drm_encoder *exynos_encoder; | |
331 | ||
332 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
333 | ||
334 | if (!manager || !possible_crtcs) | |
335 | return NULL; | |
336 | ||
337 | if (!manager->dev) | |
338 | return NULL; | |
339 | ||
340 | exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); | |
341 | if (!exynos_encoder) { | |
342 | DRM_ERROR("failed to allocate encoder\n"); | |
343 | return NULL; | |
344 | } | |
345 | ||
ec05da95 | 346 | exynos_encoder->dpms = DRM_MODE_DPMS_OFF; |
1c248b7d ID |
347 | exynos_encoder->manager = manager; |
348 | encoder = &exynos_encoder->drm_encoder; | |
349 | encoder->possible_crtcs = possible_crtcs; | |
350 | ||
351 | DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); | |
352 | ||
353 | drm_encoder_init(dev, encoder, &exynos_encoder_funcs, | |
354 | DRM_MODE_ENCODER_TMDS); | |
355 | ||
356 | drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); | |
357 | ||
358 | DRM_DEBUG_KMS("encoder has been created\n"); | |
359 | ||
360 | return encoder; | |
361 | } | |
362 | ||
363 | struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder) | |
364 | { | |
365 | return to_exynos_encoder(encoder)->manager; | |
366 | } | |
367 | ||
368 | void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, | |
369 | void (*fn)(struct drm_encoder *, void *)) | |
370 | { | |
371 | struct drm_device *dev = crtc->dev; | |
372 | struct drm_encoder *encoder; | |
d2716c89 JS |
373 | struct exynos_drm_private *private = dev->dev_private; |
374 | struct exynos_drm_manager *manager; | |
1c248b7d ID |
375 | |
376 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
d2716c89 JS |
377 | /* |
378 | * if crtc is detached from encoder, check pipe, | |
379 | * otherwise check crtc attached to encoder | |
380 | */ | |
381 | if (!encoder->crtc) { | |
382 | manager = to_exynos_encoder(encoder)->manager; | |
383 | if (manager->pipe < 0 || | |
384 | private->crtc[manager->pipe] != crtc) | |
385 | continue; | |
386 | } else { | |
387 | if (encoder->crtc != crtc) | |
388 | continue; | |
389 | } | |
1c248b7d ID |
390 | |
391 | fn(encoder, data); | |
392 | } | |
393 | } | |
394 | ||
395 | void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) | |
396 | { | |
397 | struct exynos_drm_manager *manager = | |
398 | to_exynos_encoder(encoder)->manager; | |
399 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | |
400 | int crtc = *(int *)data; | |
401 | ||
d249ce02 JS |
402 | if (manager->pipe != crtc) |
403 | return; | |
1c248b7d ID |
404 | |
405 | if (manager_ops->enable_vblank) | |
406 | manager_ops->enable_vblank(manager->dev); | |
407 | } | |
408 | ||
409 | void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) | |
410 | { | |
411 | struct exynos_drm_manager *manager = | |
412 | to_exynos_encoder(encoder)->manager; | |
413 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | |
414 | int crtc = *(int *)data; | |
415 | ||
d249ce02 JS |
416 | if (manager->pipe != crtc) |
417 | return; | |
1c248b7d ID |
418 | |
419 | if (manager_ops->disable_vblank) | |
420 | manager_ops->disable_vblank(manager->dev); | |
421 | } | |
422 | ||
ec05da95 ID |
423 | void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) |
424 | { | |
ec05da95 ID |
425 | struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); |
426 | struct exynos_drm_manager *manager = exynos_encoder->manager; | |
427 | struct exynos_drm_manager_ops *manager_ops = manager->ops; | |
ec05da95 ID |
428 | int mode = *(int *)data; |
429 | ||
430 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
431 | ||
432 | if (manager_ops && manager_ops->dpms) | |
433 | manager_ops->dpms(manager->dev, mode); | |
434 | ||
ec05da95 ID |
435 | /* |
436 | * if this condition is ok then it means that the crtc is already | |
437 | * detached from encoder and last function for detaching is properly | |
438 | * done, so clear pipe from manager to prevent repeated call. | |
439 | */ | |
440 | if (mode > DRM_MODE_DPMS_ON) { | |
441 | if (!encoder->crtc) | |
442 | manager->pipe = -1; | |
443 | } | |
444 | } | |
445 | ||
4070d212 JS |
446 | void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data) |
447 | { | |
448 | struct exynos_drm_manager *manager = | |
449 | to_exynos_encoder(encoder)->manager; | |
450 | int pipe = *(int *)data; | |
451 | ||
452 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
453 | ||
454 | /* | |
455 | * when crtc is detached from encoder, this pipe is used | |
456 | * to select manager operation | |
457 | */ | |
458 | manager->pipe = pipe; | |
459 | } | |
460 | ||
461 | void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data) | |
1c248b7d ID |
462 | { |
463 | struct exynos_drm_manager *manager = | |
464 | to_exynos_encoder(encoder)->manager; | |
465 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | |
466 | struct exynos_drm_overlay *overlay = data; | |
467 | ||
4070d212 JS |
468 | DRM_DEBUG_KMS("%s\n", __FILE__); |
469 | ||
b0e0f856 SWK |
470 | if (overlay_ops && overlay_ops->mode_set) |
471 | overlay_ops->mode_set(manager->dev, overlay); | |
1c248b7d ID |
472 | } |
473 | ||
4070d212 | 474 | void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data) |
d2716c89 JS |
475 | { |
476 | struct exynos_drm_manager *manager = | |
477 | to_exynos_encoder(encoder)->manager; | |
478 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | |
864ee9e6 | 479 | int zpos = DEFAULT_ZPOS; |
d2716c89 | 480 | |
4070d212 | 481 | DRM_DEBUG_KMS("%s\n", __FILE__); |
d2716c89 | 482 | |
864ee9e6 JS |
483 | if (data) |
484 | zpos = *(int *)data; | |
485 | ||
4070d212 JS |
486 | if (overlay_ops && overlay_ops->commit) |
487 | overlay_ops->commit(manager->dev, zpos); | |
d2716c89 | 488 | } |
d249ce02 | 489 | |
cf5188ac JS |
490 | void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data) |
491 | { | |
492 | struct exynos_drm_manager *manager = | |
493 | to_exynos_encoder(encoder)->manager; | |
494 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; | |
495 | int zpos = DEFAULT_ZPOS; | |
496 | ||
497 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
498 | ||
499 | if (data) | |
500 | zpos = *(int *)data; | |
501 | ||
502 | if (overlay_ops && overlay_ops->enable) | |
503 | overlay_ops->enable(manager->dev, zpos); | |
504 | } | |
505 | ||
4070d212 | 506 | void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data) |
d249ce02 JS |
507 | { |
508 | struct exynos_drm_manager *manager = | |
509 | to_exynos_encoder(encoder)->manager; | |
4070d212 JS |
510 | struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; |
511 | int zpos = DEFAULT_ZPOS; | |
d249ce02 JS |
512 | |
513 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
514 | ||
4070d212 JS |
515 | if (data) |
516 | zpos = *(int *)data; | |
517 | ||
518 | if (overlay_ops && overlay_ops->disable) | |
519 | overlay_ops->disable(manager->dev, zpos); | |
d249ce02 | 520 | } |