aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c21
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c157
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.h38
-rw-r--r--drivers/gpu/drm/omapdrm/omap_encoder.c7
-rw-r--r--drivers/gpu/drm/omapdrm/omap_irq.c17
5 files changed, 165 insertions, 75 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index bec66a490b8f..79b200aee18a 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -74,6 +74,13 @@ struct omap_crtc {
74 struct work_struct page_flip_work; 74 struct work_struct page_flip_work;
75}; 75};
76 76
77uint32_t pipe2vbl(struct drm_crtc *crtc)
78{
79 struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
80
81 return dispc_mgr_get_vsync_irq(omap_crtc->channel);
82}
83
77/* 84/*
78 * Manager-ops, callbacks from output when they need to configure 85 * Manager-ops, callbacks from output when they need to configure
79 * the upstream part of the video pipe. 86 * the upstream part of the video pipe.
@@ -613,7 +620,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
613 omap_crtc->apply.pre_apply = omap_crtc_pre_apply; 620 omap_crtc->apply.pre_apply = omap_crtc_pre_apply;
614 omap_crtc->apply.post_apply = omap_crtc_post_apply; 621 omap_crtc->apply.post_apply = omap_crtc_post_apply;
615 622
616 omap_crtc->apply_irq.irqmask = pipe2vbl(id); 623 omap_crtc->channel = channel;
624 omap_crtc->plane = plane;
625 omap_crtc->plane->crtc = crtc;
626 omap_crtc->name = channel_names[channel];
627 omap_crtc->pipe = id;
628
629 omap_crtc->apply_irq.irqmask = pipe2vbl(crtc);
617 omap_crtc->apply_irq.irq = omap_crtc_apply_irq; 630 omap_crtc->apply_irq.irq = omap_crtc_apply_irq;
618 631
619 omap_crtc->error_irq.irqmask = 632 omap_crtc->error_irq.irqmask =
@@ -621,12 +634,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
621 omap_crtc->error_irq.irq = omap_crtc_error_irq; 634 omap_crtc->error_irq.irq = omap_crtc_error_irq;
622 omap_irq_register(dev, &omap_crtc->error_irq); 635 omap_irq_register(dev, &omap_crtc->error_irq);
623 636
624 omap_crtc->channel = channel;
625 omap_crtc->plane = plane;
626 omap_crtc->plane->crtc = crtc;
627 omap_crtc->name = channel_names[channel];
628 omap_crtc->pipe = id;
629
630 /* temporary: */ 637 /* temporary: */
631 omap_crtc->mgr.id = channel; 638 omap_crtc->mgr.id = channel;
632 639
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
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
index d4f997bb4ac0..215a20dd340c 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.h
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
@@ -139,8 +139,8 @@ void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
139int omap_gem_resume(struct device *dev); 139int omap_gem_resume(struct device *dev);
140#endif 140#endif
141 141
142int omap_irq_enable_vblank(struct drm_device *dev, int crtc); 142int omap_irq_enable_vblank(struct drm_device *dev, int crtc_id);
143void omap_irq_disable_vblank(struct drm_device *dev, int crtc); 143void omap_irq_disable_vblank(struct drm_device *dev, int crtc_id);
144irqreturn_t omap_irq_handler(DRM_IRQ_ARGS); 144irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
145void omap_irq_preinstall(struct drm_device *dev); 145void omap_irq_preinstall(struct drm_device *dev);
146int omap_irq_postinstall(struct drm_device *dev); 146int omap_irq_postinstall(struct drm_device *dev);
@@ -271,39 +271,9 @@ static inline int align_pitch(int pitch, int width, int bpp)
271 return ALIGN(pitch, 8 * bytespp); 271 return ALIGN(pitch, 8 * bytespp);
272} 272}
273 273
274static inline enum omap_channel pipe2chan(int pipe)
275{
276 int num_mgrs = dss_feat_get_num_mgrs();
277
278 /*
279 * We usually don't want to create a CRTC for each manager,
280 * at least not until we have a way to expose private planes
281 * to userspace. Otherwise there would not be enough video
282 * pipes left for drm planes. The higher #'d managers tend
283 * to have more features so start in reverse order.
284 */
285 return num_mgrs - pipe - 1;
286}
287
288/* map crtc to vblank mask */ 274/* map crtc to vblank mask */
289static inline uint32_t pipe2vbl(int crtc) 275uint32_t pipe2vbl(struct drm_crtc *crtc);
290{ 276struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder);
291 enum omap_channel channel = pipe2chan(crtc);
292 return dispc_mgr_get_vsync_irq(channel);
293}
294
295static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc)
296{
297 struct omap_drm_private *priv = dev->dev_private;
298 int i;
299
300 for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++)
301 if (priv->crtcs[i] == crtc)
302 return i;
303
304 BUG(); /* bogus CRTC ptr */
305 return -1;
306}
307 277
308/* should these be made into common util helpers? 278/* should these be made into common util helpers?
309 */ 279 */
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c
index 18be00dd7b5e..c29451ba65da 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -41,6 +41,13 @@ struct omap_encoder {
41 struct omap_dss_device *dssdev; 41 struct omap_dss_device *dssdev;
42}; 42};
43 43
44struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder)
45{
46 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
47
48 return omap_encoder->dssdev;
49}
50
44static void omap_encoder_destroy(struct drm_encoder *encoder) 51static void omap_encoder_destroy(struct drm_encoder *encoder)
45{ 52{
46 struct omap_encoder *omap_encoder = to_omap_encoder(encoder); 53 struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
index e01303ee00c3..9263db117ff8 100644
--- a/drivers/gpu/drm/omapdrm/omap_irq.c
+++ b/drivers/gpu/drm/omapdrm/omap_irq.c
@@ -130,12 +130,13 @@ int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
130 * Zero on success, appropriate errno if the given @crtc's vblank 130 * Zero on success, appropriate errno if the given @crtc's vblank
131 * interrupt cannot be enabled. 131 * interrupt cannot be enabled.
132 */ 132 */
133int omap_irq_enable_vblank(struct drm_device *dev, int crtc) 133int omap_irq_enable_vblank(struct drm_device *dev, int crtc_id)
134{ 134{
135 struct omap_drm_private *priv = dev->dev_private; 135 struct omap_drm_private *priv = dev->dev_private;
136 struct drm_crtc *crtc = priv->crtcs[crtc_id];
136 unsigned long flags; 137 unsigned long flags;
137 138
138 DBG("dev=%p, crtc=%d", dev, crtc); 139 DBG("dev=%p, crtc=%d", dev, crtc_id);
139 140
140 dispc_runtime_get(); 141 dispc_runtime_get();
141 spin_lock_irqsave(&list_lock, flags); 142 spin_lock_irqsave(&list_lock, flags);
@@ -156,12 +157,13 @@ int omap_irq_enable_vblank(struct drm_device *dev, int crtc)
156 * a hardware vblank counter, this routine should be a no-op, since 157 * a hardware vblank counter, this routine should be a no-op, since
157 * interrupts will have to stay on to keep the count accurate. 158 * interrupts will have to stay on to keep the count accurate.
158 */ 159 */
159void omap_irq_disable_vblank(struct drm_device *dev, int crtc) 160void omap_irq_disable_vblank(struct drm_device *dev, int crtc_id)
160{ 161{
161 struct omap_drm_private *priv = dev->dev_private; 162 struct omap_drm_private *priv = dev->dev_private;
163 struct drm_crtc *crtc = priv->crtcs[crtc_id];
162 unsigned long flags; 164 unsigned long flags;
163 165
164 DBG("dev=%p, crtc=%d", dev, crtc); 166 DBG("dev=%p, crtc=%d", dev, crtc_id);
165 167
166 dispc_runtime_get(); 168 dispc_runtime_get();
167 spin_lock_irqsave(&list_lock, flags); 169 spin_lock_irqsave(&list_lock, flags);
@@ -186,9 +188,12 @@ irqreturn_t omap_irq_handler(DRM_IRQ_ARGS)
186 188
187 VERB("irqs: %08x", irqstatus); 189 VERB("irqs: %08x", irqstatus);
188 190
189 for (id = 0; id < priv->num_crtcs; id++) 191 for (id = 0; id < priv->num_crtcs; id++) {
190 if (irqstatus & pipe2vbl(id)) 192 struct drm_crtc *crtc = priv->crtcs[id];
193
194 if (irqstatus & pipe2vbl(crtc))
191 drm_handle_vblank(dev, id); 195 drm_handle_vblank(dev, id);
196 }
192 197
193 spin_lock_irqsave(&list_lock, flags); 198 spin_lock_irqsave(&list_lock, flags);
194 list_for_each_entry_safe(handler, n, &priv->irq_list, node) { 199 list_for_each_entry_safe(handler, n, &priv->irq_list, node) {