aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/omapdrm/omap_drv.c
diff options
context:
space:
mode:
authorArchit Taneja <archit@ti.com>2013-03-26 09:45:19 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2013-04-11 06:26:01 -0400
commit0d8f371f5a0cfdad946b5dd0ba9c77d2fbd5b2d3 (patch)
treec041c317dc3989605cd72bf71d17182c70d68106 /drivers/gpu/drm/omapdrm/omap_drv.c
parentb03e14fd4b9f91d0c6be864bf62c75933fa26cac (diff)
drm/omap: Fix and improve crtc and overlay manager correlation
The omapdrm driver currently takes a config/module arg to figure out the number of crtcs it needs to create. We could create as many crtcs as there are overlay managers in the DSS hardware, but we don't do that because each crtc eats up one DSS overlay, and that reduces the number of planes we can attach to a single crtc. Since the number of crtcs may be lesser than the number of hardware overlay managers, we need to figure out which overlay managers to use for our crtcs. The current approach is to use pipe2chan(), which returns a higher numbered manager for the crtc. The problem with this approach is that it assumes that the overlay managers we choose will connect to the encoders the platform's panels are going to use, this isn't true, an overlay manager connects only to a few outputs/encoders, and choosing any overlay manager for our crtc might lead to a situation where the encoder cannot connect to any of the crtcs we have chosen. For example, an omap5-panda board has just one hdmi output. If num_crtc is set to 1, with the current approach, pipe2chan will pick up the LCD2 overlay manager, which cannot connect to the hdmi encoder at all. The only manager that could have connected to hdmi was the TV overlay manager. Therefore, there is a need to choose our overlay managers keeping in mind the panels we have on that platform. The new approach iterates through all the available panels, creates encoders and connectors for them, and then tries to get a suitable overlay manager to create a crtc which can connect to the encoders. We use the dispc_channel field in omap_dss_output to retrieve the desired overlay manager's channel number, we then check whether the manager had already been assigned to a crtc or not. If it was already assigned to a crtc, we assume that out of all the encoders which intend use this crtc, only one will run at a time. If the overlay manager wan't assigned to a crtc till then, we create a new crtc and link it with the overlay manager. This approach just looks for the best dispc_channel for each encoder. On DSS HW, some encoders can connect to multiple overlay managers. Since we don't try looking for alternate overlay managers, there is a greater possibility that 2 or more encoders end up asking for the same crtc, causing only one encoder to run at a time. Also, this approach isn't the most optimal one, it can do either good or bad depending on the sequence in which the panels/outputs are parsed. The optimal way would be some sort of back tracking approach, where we improve the set of managers we use as we iterate through the list of panels/encoders. That's something left for later. Signed-off-by: Archit Taneja <archit@ti.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_drv.c')
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c157
1 files changed, 129 insertions, 28 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 77b72259f15b..cbaa00338b62 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -74,49 +74,48 @@ static int get_connector_type(struct omap_dss_device *dssdev)
74 } 74 }
75} 75}
76 76
77static bool channel_used(struct drm_device *dev, enum omap_channel channel)
78{
79 struct omap_drm_private *priv = dev->dev_private;
80 int i;
81
82 for (i = 0; i < priv->num_crtcs; i++) {
83 struct drm_crtc *crtc = priv->crtcs[i];
84
85 if (omap_crtc_channel(crtc) == channel)
86 return true;
87 }
88
89 return false;
90}
91
77static int omap_modeset_init(struct drm_device *dev) 92static int omap_modeset_init(struct drm_device *dev)
78{ 93{
79 struct omap_drm_private *priv = dev->dev_private; 94 struct omap_drm_private *priv = dev->dev_private;
80 struct omap_dss_device *dssdev = NULL; 95 struct omap_dss_device *dssdev = NULL;
81 int num_ovls = dss_feat_get_num_ovls(); 96 int num_ovls = dss_feat_get_num_ovls();
82 int id; 97 int num_mgrs = dss_feat_get_num_mgrs();
98 int num_crtcs;
99 int i, id = 0;
83 100
84 drm_mode_config_init(dev); 101 drm_mode_config_init(dev);
85 102
86 omap_drm_irq_install(dev); 103 omap_drm_irq_install(dev);
87 104
88 /* 105 /*
89 * Create private planes and CRTCs for the last NUM_CRTCs overlay 106 * We usually don't want to create a CRTC for each manager, at least
90 * plus manager: 107 * not until we have a way to expose private planes to userspace.
91 */ 108 * Otherwise there would not be enough video pipes left for drm planes.
92 for (id = 0; id < min(num_crtc, num_ovls); id++) { 109 * We use the num_crtc argument to limit the number of crtcs we create.
93 struct drm_plane *plane;
94 struct drm_crtc *crtc;
95
96 plane = omap_plane_init(dev, id, true);
97 crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
98
99 BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
100 priv->crtcs[id] = crtc;
101 priv->num_crtcs++;
102
103 priv->planes[id] = plane;
104 priv->num_planes++;
105 }
106
107 /*
108 * Create normal planes for the remaining overlays:
109 */ 110 */
110 for (; id < num_ovls; id++) { 111 num_crtcs = min3(num_crtc, num_mgrs, num_ovls);
111 struct drm_plane *plane = omap_plane_init(dev, id, false);
112 112
113 BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); 113 dssdev = NULL;
114 priv->planes[priv->num_planes++] = plane;
115 }
116 114
117 for_each_dss_dev(dssdev) { 115 for_each_dss_dev(dssdev) {
118 struct drm_connector *connector; 116 struct drm_connector *connector;
119 struct drm_encoder *encoder; 117 struct drm_encoder *encoder;
118 enum omap_channel channel;
120 119
121 if (!dssdev->driver) { 120 if (!dssdev->driver) {
122 dev_warn(dev->dev, "%s has no driver.. skipping it\n", 121 dev_warn(dev->dev, "%s has no driver.. skipping it\n",
@@ -157,16 +156,118 @@ static int omap_modeset_init(struct drm_device *dev)
157 156
158 drm_mode_connector_attach_encoder(connector, encoder); 157 drm_mode_connector_attach_encoder(connector, encoder);
159 158
159 /*
160 * if we have reached the limit of the crtcs we are allowed to
161 * create, let's not try to look for a crtc for this
162 * panel/encoder and onwards, we will, of course, populate the
163 * the possible_crtcs field for all the encoders with the final
164 * set of crtcs we create
165 */
166 if (id == num_crtcs)
167 continue;
168
169 /*
170 * get the recommended DISPC channel for this encoder. For now,
171 * we only try to get create a crtc out of the recommended, the
172 * other possible channels to which the encoder can connect are
173 * not considered.
174 */
175 channel = dssdev->output->dispc_channel;
176
177 /*
178 * if this channel hasn't already been taken by a previously
179 * allocated crtc, we create a new crtc for it
180 */
181 if (!channel_used(dev, channel)) {
182 struct drm_plane *plane;
183 struct drm_crtc *crtc;
184
185 plane = omap_plane_init(dev, id, true);
186 crtc = omap_crtc_init(dev, plane, channel, id);
187
188 BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
189 priv->crtcs[id] = crtc;
190 priv->num_crtcs++;
191
192 priv->planes[id] = plane;
193 priv->num_planes++;
194
195 id++;
196 }
197 }
198
199 /*
200 * we have allocated crtcs according to the need of the panels/encoders,
201 * adding more crtcs here if needed
202 */
203 for (; id < num_crtcs; id++) {
204
205 /* find a free manager for this crtc */
206 for (i = 0; i < num_mgrs; i++) {
207 if (!channel_used(dev, i)) {
208 struct drm_plane *plane;
209 struct drm_crtc *crtc;
210
211 plane = omap_plane_init(dev, id, true);
212 crtc = omap_crtc_init(dev, plane, i, id);
213
214 BUG_ON(priv->num_crtcs >=
215 ARRAY_SIZE(priv->crtcs));
216
217 priv->crtcs[id] = crtc;
218 priv->num_crtcs++;
219
220 priv->planes[id] = plane;
221 priv->num_planes++;
222
223 break;
224 } else {
225 continue;
226 }
227 }
228
229 if (i == num_mgrs) {
230 /* this shouldn't really happen */
231 dev_err(dev->dev, "no managers left for crtc\n");
232 return -ENOMEM;
233 }
234 }
235
236 /*
237 * Create normal planes for the remaining overlays:
238 */
239 for (; id < num_ovls; id++) {
240 struct drm_plane *plane = omap_plane_init(dev, id, false);
241
242 BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
243 priv->planes[priv->num_planes++] = plane;
244 }
245
246 for (i = 0; i < priv->num_encoders; i++) {
247 struct drm_encoder *encoder = priv->encoders[i];
248 struct omap_dss_device *dssdev =
249 omap_encoder_get_dssdev(encoder);
250
160 /* figure out which crtc's we can connect the encoder to: */ 251 /* figure out which crtc's we can connect the encoder to: */
161 encoder->possible_crtcs = 0; 252 encoder->possible_crtcs = 0;
162 for (id = 0; id < priv->num_crtcs; id++) { 253 for (id = 0; id < priv->num_crtcs; id++) {
163 enum omap_dss_output_id supported_outputs = 254 struct drm_crtc *crtc = priv->crtcs[id];
164 dss_feat_get_supported_outputs(pipe2chan(id)); 255 enum omap_channel crtc_channel;
256 enum omap_dss_output_id supported_outputs;
257
258 crtc_channel = omap_crtc_channel(crtc);
259 supported_outputs =
260 dss_feat_get_supported_outputs(crtc_channel);
261
165 if (supported_outputs & dssdev->output->id) 262 if (supported_outputs & dssdev->output->id)
166 encoder->possible_crtcs |= (1 << id); 263 encoder->possible_crtcs |= (1 << id);
167 } 264 }
168 } 265 }
169 266
267 DBG("registered %d planes, %d crtcs, %d encoders and %d connectors\n",
268 priv->num_planes, priv->num_crtcs, priv->num_encoders,
269 priv->num_connectors);
270
170 dev->mode_config.min_width = 32; 271 dev->mode_config.min_width = 32;
171 dev->mode_config.min_height = 32; 272 dev->mode_config.min_height = 32;
172 273