blob: d4ae664a0515653ba0b972d9ce78992c5df6164d [file] [log] [blame]
Inki Dae1c248b72011-10-04 19:19:01 +09001/* 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 *
Inki Daed81aecb2012-12-18 02:30:17 +09009 * 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.
Inki Dae1c248b72011-10-04 19:19:01 +090013 */
14
David Howells760285e2012-10-02 18:01:07 +010015#include <drm/drmP.h>
16#include <drm/drm_crtc_helper.h>
Inki Dae1c248b72011-10-04 19:19:01 +090017
18#include "exynos_drm_drv.h"
Inki Dae1c248b72011-10-04 19:19:01 +090019#include "exynos_drm_encoder.h"
Inki Dae50caf252012-08-20 21:29:25 +090020#include "exynos_drm_connector.h"
Inki Dae1c248b72011-10-04 19:19:01 +090021
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.
Sean Paul080be03d2014-02-19 21:02:55 +090029 * @display: the display structure that maps to this encoder
Inki Dae1c248b72011-10-04 19:19:01 +090030 */
31struct exynos_drm_encoder {
Inki Dae1b85a072012-08-17 17:58:38 +090032 struct drm_crtc *old_crtc;
Inki Dae1c248b72011-10-04 19:19:01 +090033 struct drm_encoder drm_encoder;
Sean Paul080be03d2014-02-19 21:02:55 +090034 struct exynos_drm_display *display;
Inki Dae1c248b72011-10-04 19:19:01 +090035};
36
Inki Daeec05da92011-12-06 11:06:54 +090037static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
38{
Sean Paul080be03d2014-02-19 21:02:55 +090039 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
40 struct exynos_drm_display *display = exynos_encoder->display;
Inki Daeec05da92011-12-06 11:06:54 +090041
YoungJun Chobca34c92013-06-12 10:40:52 +090042 DRM_DEBUG_KMS("encoder dpms: %d\n", mode);
Inki Daeec05da92011-12-06 11:06:54 +090043
Sean Paul080be03d2014-02-19 21:02:55 +090044 if (display->ops->dpms)
45 display->ops->dpms(display, mode);
Inki Daeec05da92011-12-06 11:06:54 +090046}
47
Inki Dae1c248b72011-10-04 19:19:01 +090048static bool
49exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
Laurent Pincharte811f5a2012-07-17 17:56:50 +020050 const struct drm_display_mode *mode,
Inki Dae1c248b72011-10-04 19:19:01 +090051 struct drm_display_mode *adjusted_mode)
52{
Inki Dae1de425b2012-03-16 18:47:04 +090053 struct drm_device *dev = encoder->dev;
Sean Paul080be03d2014-02-19 21:02:55 +090054 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
55 struct exynos_drm_display *display = exynos_encoder->display;
Inki Dae1de425b2012-03-16 18:47:04 +090056 struct drm_connector *connector;
Inki Dae1de425b2012-03-16 18:47:04 +090057
Inki Dae1de425b2012-03-16 18:47:04 +090058 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
Sean Paul080be03d2014-02-19 21:02:55 +090059 if (connector->encoder != encoder)
60 continue;
61
62 if (display->ops->mode_fixup)
63 display->ops->mode_fixup(display, connector, mode,
64 adjusted_mode);
Inki Dae1de425b2012-03-16 18:47:04 +090065 }
Inki Dae1c248b72011-10-04 19:19:01 +090066
67 return true;
68}
69
Inki Dae1b85a072012-08-17 17:58:38 +090070static void disable_plane_to_crtc(struct drm_device *dev,
71 struct drm_crtc *old_crtc,
72 struct drm_crtc *new_crtc)
73{
74 struct drm_plane *plane;
75
76 /*
77 * if old_crtc isn't same as encoder->crtc then it means that
78 * user changed crtc id to another one so the plane to old_crtc
79 * should be disabled and plane->crtc should be set to new_crtc
80 * (encoder->crtc)
81 */
82 list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
83 if (plane->crtc == old_crtc) {
84 /*
85 * do not change below call order.
86 *
87 * plane->funcs->disable_plane call checks
88 * if encoder->crtc is same as plane->crtc and if same
Sean Paul1c6244c2014-01-30 16:19:02 -050089 * then manager_ops->win_disable callback will be called
Inki Dae1b85a072012-08-17 17:58:38 +090090 * to diasble current hw overlay so plane->crtc should
91 * have new_crtc because new_crtc was set to
92 * encoder->crtc in advance.
93 */
94 plane->crtc = new_crtc;
95 plane->funcs->disable_plane(plane);
96 }
97 }
98}
99
Inki Dae1c248b72011-10-04 19:19:01 +0900100static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
101 struct drm_display_mode *mode,
102 struct drm_display_mode *adjusted_mode)
103{
104 struct drm_device *dev = encoder->dev;
105 struct drm_connector *connector;
Sean Paul080be03d2014-02-19 21:02:55 +0900106 struct exynos_drm_display *display;
Inki Dae1c248b72011-10-04 19:19:01 +0900107
Inki Dae1c248b72011-10-04 19:19:01 +0900108 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
Inki Dae1b85a072012-08-17 17:58:38 +0900109 if (connector->encoder == encoder) {
110 struct exynos_drm_encoder *exynos_encoder;
111
112 exynos_encoder = to_exynos_encoder(encoder);
113
114 if (exynos_encoder->old_crtc != encoder->crtc &&
115 exynos_encoder->old_crtc) {
116
117 /*
118 * disable a plane to old crtc and change
119 * crtc of the plane to new one.
120 */
121 disable_plane_to_crtc(dev,
122 exynos_encoder->old_crtc,
123 encoder->crtc);
124 }
125
Sean Paul080be03d2014-02-19 21:02:55 +0900126 display = exynos_encoder->display;
Inki Dae1b85a072012-08-17 17:58:38 +0900127
Sean Paul080be03d2014-02-19 21:02:55 +0900128 if (display->ops->mode_set)
129 display->ops->mode_set(display,
130 adjusted_mode);
Inki Dae1b85a072012-08-17 17:58:38 +0900131
132 exynos_encoder->old_crtc = encoder->crtc;
133 }
Inki Dae1c248b72011-10-04 19:19:01 +0900134 }
135}
136
137static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
138{
Inki Dae1c248b72011-10-04 19:19:01 +0900139 /* drm framework doesn't check NULL. */
140}
141
142static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
143{
Inki Dae44c91692012-10-18 18:59:55 +0900144 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
Sean Paul080be03d2014-02-19 21:02:55 +0900145 struct exynos_drm_display *display = exynos_encoder->display;
Inki Dae1c248b72011-10-04 19:19:01 +0900146
Sean Paul080be03d2014-02-19 21:02:55 +0900147 if (display->ops->dpms)
148 display->ops->dpms(display, DRM_MODE_DPMS_ON);
149
150 if (display->ops->commit)
151 display->ops->commit(display);
Inki Dae1c248b72011-10-04 19:19:01 +0900152}
153
Inki Daebcf4cef2012-08-24 10:54:12 -0700154static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
155{
156 struct drm_plane *plane;
157 struct drm_device *dev = encoder->dev;
158
159 exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
160
161 /* all planes connected to this encoder should be also disabled. */
162 list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
163 if (plane->crtc == encoder->crtc)
164 plane->funcs->disable_plane(plane);
165 }
166}
167
Inki Dae1c248b72011-10-04 19:19:01 +0900168static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
169 .dpms = exynos_drm_encoder_dpms,
170 .mode_fixup = exynos_drm_encoder_mode_fixup,
171 .mode_set = exynos_drm_encoder_mode_set,
172 .prepare = exynos_drm_encoder_prepare,
173 .commit = exynos_drm_encoder_commit,
Inki Daebcf4cef2012-08-24 10:54:12 -0700174 .disable = exynos_drm_encoder_disable,
Inki Dae1c248b72011-10-04 19:19:01 +0900175};
176
177static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
178{
Sean Paul080be03d2014-02-19 21:02:55 +0900179 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
Inki Dae1c248b72011-10-04 19:19:01 +0900180
181 drm_encoder_cleanup(encoder);
Inki Dae1c248b72011-10-04 19:19:01 +0900182 kfree(exynos_encoder);
183}
184
185static struct drm_encoder_funcs exynos_encoder_funcs = {
186 .destroy = exynos_drm_encoder_destroy,
187};
188
Inki Daed081f562012-02-15 11:25:19 +0900189static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
190{
191 struct drm_encoder *clone;
192 struct drm_device *dev = encoder->dev;
193 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
Sean Paul080be03d2014-02-19 21:02:55 +0900194 struct exynos_drm_display *display = exynos_encoder->display;
Inki Daed081f562012-02-15 11:25:19 +0900195 unsigned int clone_mask = 0;
196 int cnt = 0;
197
198 list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
Sean Paul080be03d2014-02-19 21:02:55 +0900199 switch (display->type) {
Inki Daed081f562012-02-15 11:25:19 +0900200 case EXYNOS_DISPLAY_TYPE_LCD:
201 case EXYNOS_DISPLAY_TYPE_HDMI:
Inki Daeb73d1232012-03-21 10:55:26 +0900202 case EXYNOS_DISPLAY_TYPE_VIDI:
Inki Daed081f562012-02-15 11:25:19 +0900203 clone_mask |= (1 << (cnt++));
204 break;
205 default:
206 continue;
207 }
208 }
209
210 return clone_mask;
211}
212
213void exynos_drm_encoder_setup(struct drm_device *dev)
214{
215 struct drm_encoder *encoder;
216
Inki Daed081f562012-02-15 11:25:19 +0900217 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
218 encoder->possible_clones = exynos_drm_encoder_clones(encoder);
219}
220
Inki Dae1c248b72011-10-04 19:19:01 +0900221struct drm_encoder *
222exynos_drm_encoder_create(struct drm_device *dev,
Sean Paul080be03d2014-02-19 21:02:55 +0900223 struct exynos_drm_display *display,
Sean Paul3f283d92014-01-30 16:19:11 -0500224 unsigned long possible_crtcs)
Inki Dae1c248b72011-10-04 19:19:01 +0900225{
226 struct drm_encoder *encoder;
227 struct exynos_drm_encoder *exynos_encoder;
228
Sean Paul080be03d2014-02-19 21:02:55 +0900229 if (!possible_crtcs)
Inki Dae1c248b72011-10-04 19:19:01 +0900230 return NULL;
231
232 exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
Sachin Kamat38bb5252013-08-19 19:04:55 +0900233 if (!exynos_encoder)
Inki Dae1c248b72011-10-04 19:19:01 +0900234 return NULL;
Inki Dae1c248b72011-10-04 19:19:01 +0900235
Sean Paul080be03d2014-02-19 21:02:55 +0900236 exynos_encoder->display = display;
Inki Dae1c248b72011-10-04 19:19:01 +0900237 encoder = &exynos_encoder->drm_encoder;
238 encoder->possible_crtcs = possible_crtcs;
239
240 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
241
242 drm_encoder_init(dev, encoder, &exynos_encoder_funcs,
243 DRM_MODE_ENCODER_TMDS);
244
245 drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs);
246
247 DRM_DEBUG_KMS("encoder has been created\n");
248
249 return encoder;
250}
251
Sean Paul080be03d2014-02-19 21:02:55 +0900252struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder)
Inki Dae1c248b72011-10-04 19:19:01 +0900253{
Sean Paul080be03d2014-02-19 21:02:55 +0900254 return to_exynos_encoder(encoder)->display;
Joonyoung Shim4070d212012-06-27 14:27:05 +0900255}