struct work_struct page_flip_work;
};
+uint32_t pipe2vbl(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+ return dispc_mgr_get_vsync_irq(omap_crtc->channel);
+}
+
/*
* Manager-ops, callbacks from output when they need to configure
* the upstream part of the video pipe.
omap_crtc->apply.pre_apply = omap_crtc_pre_apply;
omap_crtc->apply.post_apply = omap_crtc_post_apply;
- omap_crtc->apply_irq.irqmask = pipe2vbl(id);
+ omap_crtc->channel = channel;
+ omap_crtc->plane = plane;
+ omap_crtc->plane->crtc = crtc;
+ omap_crtc->name = channel_names[channel];
+ omap_crtc->pipe = id;
+
+ omap_crtc->apply_irq.irqmask = pipe2vbl(crtc);
omap_crtc->apply_irq.irq = omap_crtc_apply_irq;
omap_crtc->error_irq.irqmask =
omap_crtc->error_irq.irq = omap_crtc_error_irq;
omap_irq_register(dev, &omap_crtc->error_irq);
- omap_crtc->channel = channel;
- omap_crtc->plane = plane;
- omap_crtc->plane->crtc = crtc;
- omap_crtc->name = channel_names[channel];
- omap_crtc->pipe = id;
-
/* temporary: */
omap_crtc->mgr.id = channel;
}
}
+static bool channel_used(struct drm_device *dev, enum omap_channel channel)
+{
+ struct omap_drm_private *priv = dev->dev_private;
+ int i;
+
+ for (i = 0; i < priv->num_crtcs; i++) {
+ struct drm_crtc *crtc = priv->crtcs[i];
+
+ if (omap_crtc_channel(crtc) == channel)
+ return true;
+ }
+
+ return false;
+}
+
static int omap_modeset_init(struct drm_device *dev)
{
struct omap_drm_private *priv = dev->dev_private;
struct omap_dss_device *dssdev = NULL;
int num_ovls = dss_feat_get_num_ovls();
- int id;
+ int num_mgrs = dss_feat_get_num_mgrs();
+ int num_crtcs;
+ int i, id = 0;
drm_mode_config_init(dev);
omap_drm_irq_install(dev);
/*
- * Create private planes and CRTCs for the last NUM_CRTCs overlay
- * plus manager:
- */
- for (id = 0; id < min(num_crtc, num_ovls); id++) {
- struct drm_plane *plane;
- struct drm_crtc *crtc;
-
- plane = omap_plane_init(dev, id, true);
- crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
-
- BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
- priv->crtcs[id] = crtc;
- priv->num_crtcs++;
-
- priv->planes[id] = plane;
- priv->num_planes++;
- }
-
- /*
- * Create normal planes for the remaining overlays:
+ * We usually don't want to create a CRTC for each manager, at least
+ * not until we have a way to expose private planes to userspace.
+ * Otherwise there would not be enough video pipes left for drm planes.
+ * We use the num_crtc argument to limit the number of crtcs we create.
*/
- for (; id < num_ovls; id++) {
- struct drm_plane *plane = omap_plane_init(dev, id, false);
+ num_crtcs = min3(num_crtc, num_mgrs, num_ovls);
- BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
- priv->planes[priv->num_planes++] = plane;
- }
+ dssdev = NULL;
for_each_dss_dev(dssdev) {
struct drm_connector *connector;
struct drm_encoder *encoder;
+ enum omap_channel channel;
if (!dssdev->driver) {
dev_warn(dev->dev, "%s has no driver.. skipping it\n",
drm_mode_connector_attach_encoder(connector, encoder);
+ /*
+ * if we have reached the limit of the crtcs we are allowed to
+ * create, let's not try to look for a crtc for this
+ * panel/encoder and onwards, we will, of course, populate the
+ * the possible_crtcs field for all the encoders with the final
+ * set of crtcs we create
+ */
+ if (id == num_crtcs)
+ continue;
+
+ /*
+ * get the recommended DISPC channel for this encoder. For now,
+ * we only try to get create a crtc out of the recommended, the
+ * other possible channels to which the encoder can connect are
+ * not considered.
+ */
+ channel = dssdev->output->dispc_channel;
+
+ /*
+ * if this channel hasn't already been taken by a previously
+ * allocated crtc, we create a new crtc for it
+ */
+ if (!channel_used(dev, channel)) {
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+
+ plane = omap_plane_init(dev, id, true);
+ crtc = omap_crtc_init(dev, plane, channel, id);
+
+ BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
+ priv->crtcs[id] = crtc;
+ priv->num_crtcs++;
+
+ priv->planes[id] = plane;
+ priv->num_planes++;
+
+ id++;
+ }
+ }
+
+ /*
+ * we have allocated crtcs according to the need of the panels/encoders,
+ * adding more crtcs here if needed
+ */
+ for (; id < num_crtcs; id++) {
+
+ /* find a free manager for this crtc */
+ for (i = 0; i < num_mgrs; i++) {
+ if (!channel_used(dev, i)) {
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+
+ plane = omap_plane_init(dev, id, true);
+ crtc = omap_crtc_init(dev, plane, i, id);
+
+ BUG_ON(priv->num_crtcs >=
+ ARRAY_SIZE(priv->crtcs));
+
+ priv->crtcs[id] = crtc;
+ priv->num_crtcs++;
+
+ priv->planes[id] = plane;
+ priv->num_planes++;
+
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (i == num_mgrs) {
+ /* this shouldn't really happen */
+ dev_err(dev->dev, "no managers left for crtc\n");
+ return -ENOMEM;
+ }
+ }
+
+ /*
+ * Create normal planes for the remaining overlays:
+ */
+ for (; id < num_ovls; id++) {
+ struct drm_plane *plane = omap_plane_init(dev, id, false);
+
+ BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
+ priv->planes[priv->num_planes++] = plane;
+ }
+
+ for (i = 0; i < priv->num_encoders; i++) {
+ struct drm_encoder *encoder = priv->encoders[i];
+ struct omap_dss_device *dssdev =
+ omap_encoder_get_dssdev(encoder);
+
/* figure out which crtc's we can connect the encoder to: */
encoder->possible_crtcs = 0;
for (id = 0; id < priv->num_crtcs; id++) {
- enum omap_dss_output_id supported_outputs =
- dss_feat_get_supported_outputs(pipe2chan(id));
+ struct drm_crtc *crtc = priv->crtcs[id];
+ enum omap_channel crtc_channel;
+ enum omap_dss_output_id supported_outputs;
+
+ crtc_channel = omap_crtc_channel(crtc);
+ supported_outputs =
+ dss_feat_get_supported_outputs(crtc_channel);
+
if (supported_outputs & dssdev->output->id)
encoder->possible_crtcs |= (1 << id);
}
}
+ DBG("registered %d planes, %d crtcs, %d encoders and %d connectors\n",
+ priv->num_planes, priv->num_crtcs, priv->num_encoders,
+ priv->num_connectors);
+
dev->mode_config.min_width = 32;
dev->mode_config.min_height = 32;
int omap_gem_resume(struct device *dev);
#endif
-int omap_irq_enable_vblank(struct drm_device *dev, int crtc);
-void omap_irq_disable_vblank(struct drm_device *dev, int crtc);
+int omap_irq_enable_vblank(struct drm_device *dev, int crtc_id);
+void omap_irq_disable_vblank(struct drm_device *dev, int crtc_id);
irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
void omap_irq_preinstall(struct drm_device *dev);
int omap_irq_postinstall(struct drm_device *dev);
return ALIGN(pitch, 8 * bytespp);
}
-static inline enum omap_channel pipe2chan(int pipe)
-{
- int num_mgrs = dss_feat_get_num_mgrs();
-
- /*
- * We usually don't want to create a CRTC for each manager,
- * at least not until we have a way to expose private planes
- * to userspace. Otherwise there would not be enough video
- * pipes left for drm planes. The higher #'d managers tend
- * to have more features so start in reverse order.
- */
- return num_mgrs - pipe - 1;
-}
-
/* map crtc to vblank mask */
-static inline uint32_t pipe2vbl(int crtc)
-{
- enum omap_channel channel = pipe2chan(crtc);
- return dispc_mgr_get_vsync_irq(channel);
-}
-
-static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc)
-{
- struct omap_drm_private *priv = dev->dev_private;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++)
- if (priv->crtcs[i] == crtc)
- return i;
-
- BUG(); /* bogus CRTC ptr */
- return -1;
-}
+uint32_t pipe2vbl(struct drm_crtc *crtc);
+struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder);
/* should these be made into common util helpers?
*/
* Zero on success, appropriate errno if the given @crtc's vblank
* interrupt cannot be enabled.
*/
-int omap_irq_enable_vblank(struct drm_device *dev, int crtc)
+int omap_irq_enable_vblank(struct drm_device *dev, int crtc_id)
{
struct omap_drm_private *priv = dev->dev_private;
+ struct drm_crtc *crtc = priv->crtcs[crtc_id];
unsigned long flags;
- DBG("dev=%p, crtc=%d", dev, crtc);
+ DBG("dev=%p, crtc=%d", dev, crtc_id);
dispc_runtime_get();
spin_lock_irqsave(&list_lock, flags);
* a hardware vblank counter, this routine should be a no-op, since
* interrupts will have to stay on to keep the count accurate.
*/
-void omap_irq_disable_vblank(struct drm_device *dev, int crtc)
+void omap_irq_disable_vblank(struct drm_device *dev, int crtc_id)
{
struct omap_drm_private *priv = dev->dev_private;
+ struct drm_crtc *crtc = priv->crtcs[crtc_id];
unsigned long flags;
- DBG("dev=%p, crtc=%d", dev, crtc);
+ DBG("dev=%p, crtc=%d", dev, crtc_id);
dispc_runtime_get();
spin_lock_irqsave(&list_lock, flags);
VERB("irqs: %08x", irqstatus);
- for (id = 0; id < priv->num_crtcs; id++)
- if (irqstatus & pipe2vbl(id))
+ for (id = 0; id < priv->num_crtcs; id++) {
+ struct drm_crtc *crtc = priv->crtcs[id];
+
+ if (irqstatus & pipe2vbl(crtc))
drm_handle_vblank(dev, id);
+ }
spin_lock_irqsave(&list_lock, flags);
list_for_each_entry_safe(handler, n, &priv->irq_list, node) {