/* * Returns whether any output on the specified pipe is of the specified type
*/ bool gma_pipe_has_type(struct drm_crtc *crtc, int type)
{ struct drm_device *dev = crtc->dev; struct drm_connector_list_iter conn_iter; struct drm_connector *connector;
void gma_wait_for_vblank(struct drm_device *dev)
{ /* Wait for 20ms, i.e. one cycle at 50hz. */
mdelay(20);
}
int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb)
{ struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct gma_crtc *gma_crtc = to_gma_crtc(crtc); struct drm_framebuffer *fb = crtc->primary->fb; struct psb_gem_object *pobj; int pipe = gma_crtc->pipe; conststruct psb_offset *map = &dev_priv->regmap[pipe]; unsignedlong start, offset;
u32 dspcntr; int ret = 0;
if (!gma_power_begin(dev, true)) return 0;
/* no fb bound */ if (!fb) {
dev_err(dev->dev, "No FB bound\n"); goto gma_pipe_cleaner;
}
pobj = to_psb_gem_object(fb->obj[0]);
/* We are displaying this buffer, make sure it is actually loaded
into the GTT */
ret = psb_gem_pin(pobj); if (ret < 0) goto gma_pipe_set_base_exit;
start = pobj->offset;
offset = y * fb->pitches[0] + x * fb->format->cpp[0];
/* FIXME: Investigate whether this really is the base for psb and why the linear offset is named base for the other chips. map->surf
should be the base and map->linoff the offset for all chips */ if (IS_PSB(dev)) {
REG_WRITE(map->base, offset + start);
REG_READ(map->base);
} else {
REG_WRITE(map->base, offset);
REG_READ(map->base);
REG_WRITE(map->surf, start);
REG_READ(map->surf);
}
gma_pipe_cleaner: /* If there was a previous display we can now unpin it */ if (old_fb)
psb_gem_unpin(to_psb_gem_object(old_fb->obj[0]));
/* * Sets the power management mode of the pipe and plane. * * This code should probably grow support for turning the cursor off and back * on appropriately at the same time as we're turning the pipe off/on.
*/ void gma_crtc_dpms(struct drm_crtc *crtc, int mode)
{ struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct gma_crtc *gma_crtc = to_gma_crtc(crtc); int pipe = gma_crtc->pipe; conststruct psb_offset *map = &dev_priv->regmap[pipe];
u32 temp;
/* XXX: When our outputs are all unaware of DPMS modes other than off * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
*/
if (IS_CDV(dev))
dev_priv->ops->disable_sr(dev);
switch (mode) { case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: if (gma_crtc->active) break;
gma_crtc->active = true;
/* Enable the DPLL */
temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) == 0) {
REG_WRITE(map->dpll, temp);
REG_READ(map->dpll); /* Wait for the clocks to stabilize. */
udelay(150);
REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE);
REG_READ(map->dpll); /* Wait for the clocks to stabilize. */
udelay(150);
REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE);
REG_READ(map->dpll); /* Wait for the clocks to stabilize. */
udelay(150);
}
/* Enable the plane */
temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
REG_WRITE(map->cntr,
temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */
REG_WRITE(map->base, REG_READ(map->base));
}
udelay(150);
/* Enable the pipe */
temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) == 0)
REG_WRITE(map->conf, temp | PIPEACONF_ENABLE);
/* If we didn't get a handle then turn the cursor off */ if (!handle) {
temp = CURSOR_MODE_DISABLE; if (gma_power_begin(dev, false)) {
REG_WRITE(control, temp);
REG_WRITE(base, 0);
gma_power_end(dev);
}
/* Unpin the old GEM object */ if (gma_crtc->cursor_obj) {
pobj = to_psb_gem_object(gma_crtc->cursor_obj);
psb_gem_unpin(pobj);
drm_gem_object_put(gma_crtc->cursor_obj);
gma_crtc->cursor_obj = NULL;
} return 0;
}
/* Currently we only support 64x64 cursors */ if (width != 64 || height != 64) {
dev_dbg(dev->dev, "We currently only support 64x64 cursors\n"); return -EINVAL;
}
obj = drm_gem_object_lookup(file_priv, handle); if (!obj) {
ret = -ENOENT; goto unlock;
}
if (obj->size < width * height * 4) {
dev_dbg(dev->dev, "Buffer is too small\n");
ret = -ENOMEM; goto unref_cursor;
}
pobj = to_psb_gem_object(obj);
/* Pin the memory into the GTT */
ret = psb_gem_pin(pobj); if (ret) {
dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); goto unref_cursor;
}
if (dev_priv->ops->cursor_needs_phys) { if (!cursor_pobj) {
dev_err(dev->dev, "No hardware cursor mem available");
ret = -ENOMEM; goto unref_cursor;
}
/* Copy the cursor to cursor mem */
tmp_dst = dev_priv->vram_addr + cursor_pobj->offset; for (i = 0; i < cursor_pages; i++) {
memcpy_from_page(tmp_dst, pobj->pages[i], 0, PAGE_SIZE);
tmp_dst += PAGE_SIZE;
}
temp = 0; /* set the pipe for the cursor */
temp |= (pipe << 28);
temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
if (gma_power_begin(dev, false)) {
REG_WRITE(control, temp);
REG_WRITE(base, addr);
gma_power_end(dev);
}
/* unpin the old bo */ if (gma_crtc->cursor_obj) {
pobj = to_psb_gem_object(gma_crtc->cursor_obj);
psb_gem_unpin(pobj);
drm_gem_object_put(gma_crtc->cursor_obj);
}
/* Call this locked if we want an event at vblank interrupt. */
ret = crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, old_fb); if (ret) {
spin_lock_irqsave(&dev->event_lock, flags); if (gma_crtc->page_flip_event) {
gma_crtc->page_flip_event = NULL;
drm_crtc_vblank_put(crtc);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
} else {
ret = crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, old_fb);
}
/* Restore previous fb in case of failure. */ if (ret)
crtc->primary->fb = current_fb;
palette_reg = map->palette; for (i = 0; i < 256; ++i)
REG_WRITE(palette_reg + (i << 2), crtc_state->savePalette[i]);
}
void gma_encoder_prepare(struct drm_encoder *encoder)
{ conststruct drm_encoder_helper_funcs *encoder_funcs =
encoder->helper_private; /* lvds has its own version of prepare see psb_intel_lvds_prepare */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
}
void gma_encoder_commit(struct drm_encoder *encoder)
{ conststruct drm_encoder_helper_funcs *encoder_funcs =
encoder->helper_private; /* lvds has its own version of commit see psb_intel_lvds_commit */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
}
/* Currently there is only a 1:1 mapping of encoders and connectors */ struct drm_encoder *gma_best_encoder(struct drm_connector *connector)
{ struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
bool gma_pll_is_valid(struct drm_crtc *crtc, conststruct gma_limit_t *limit, struct gma_clock_t *clock)
{ if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
GMA_PLL_INVALID("p1 out of range"); if (clock->p < limit->p.min || limit->p.max < clock->p)
GMA_PLL_INVALID("p out of range"); if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
GMA_PLL_INVALID("m2 out of range"); if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
GMA_PLL_INVALID("m1 out of range"); /* On CDV m1 is always 0 */ if (clock->m1 <= clock->m2 && clock->m1 != 0)
GMA_PLL_INVALID("m1 <= m2 && m1 != 0"); if (clock->m < limit->m.min || limit->m.max < clock->m)
GMA_PLL_INVALID("m out of range"); if (clock->n < limit->n.min || limit->n.max < clock->n)
GMA_PLL_INVALID("n out of range"); if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
GMA_PLL_INVALID("vco out of range"); /* XXX: We may need to be checking "Dot clock" * depending on the multiplier, connector, etc., * rather than just a single range.
*/ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
GMA_PLL_INVALID("dot out of range");
if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
(REG_READ(LVDS) & LVDS_PORT_EN) != 0) { /* * For LVDS, if the panel is on, just rely on its current * settings for dual-channel. We haven't figured out how to * reliably set up different single/dual channel state, if we * even can.
*/ if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
LVDS_CLKB_POWER_UP)
clock.p2 = limit->p2.p2_fast; else
clock.p2 = limit->p2.p2_slow;
} else { if (target < limit->p2.dot_limit)
clock.p2 = limit->p2.p2_slow; else
clock.p2 = limit->p2.p2_fast;
}
memset(best_clock, 0, sizeof(*best_clock));
/* m1 is always 0 on CDV so the outmost loop will run just once */ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { for (clock.m2 = limit->m2.min;
(clock.m2 < clock.m1 || clock.m1 == 0) &&
clock.m2 <= limit->m2.max; clock.m2++) { for (clock.n = limit->n.min;
clock.n <= limit->n.max; clock.n++) { for (clock.p1 = limit->p1.min;
clock.p1 <= limit->p1.max;
clock.p1++) { int this_err;
clock_funcs->clock(refclk, &clock);
if (!clock_funcs->pll_is_valid(crtc,
limit, &clock)) continue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.