> There are still some mysteries left, in particular how (and in > fact if) the EDID is supposed to work on the HDMI port. However > the basic stuff now works and I can plug my Q550 into an HDMI > display and get the expected results. Assumning this is for -next, and its got whitespace damage, (checkpatch and git complain :-) Dave. > > Signed-off-by: Alan Cox <alan@xxxxxxxxxxxxxxx> > --- > > drivers/gpu/drm/gma500/oaktrail.h | 6 > drivers/gpu/drm/gma500/oaktrail_crtc.c | 8 + > drivers/gpu/drm/gma500/oaktrail_device.c | 2 > drivers/gpu/drm/gma500/oaktrail_hdmi.c | 366 +++++++++++++++++++++++++++++- > 4 files changed, 366 insertions(+), 16 deletions(-) > > diff --git a/drivers/gpu/drm/gma500/oaktrail.h b/drivers/gpu/drm/gma500/oaktrail.h > index f2f9f38..30adbbe 100644 > --- a/drivers/gpu/drm/gma500/oaktrail.h > +++ b/drivers/gpu/drm/gma500/oaktrail.h > @@ -249,3 +249,9 @@ extern void oaktrail_hdmi_i2c_exit(struct pci_dev *dev); > extern void oaktrail_hdmi_save(struct drm_device *dev); > extern void oaktrail_hdmi_restore(struct drm_device *dev); > extern void oaktrail_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); > +extern int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode, int x, int y, > + struct drm_framebuffer *old_fb); > +extern void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode); > + > + > diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c > index cdafd2a..4ec2962 100644 > --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c > +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c > @@ -168,6 +168,11 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) > const struct psb_offset *map = &dev_priv->regmap[pipe]; > u32 temp; > > + if (pipe == 1) { > + oaktrail_crtc_hdmi_dpms(crtc, mode); > + return; > + } > + > if (!gma_power_begin(dev, true)) > return; > > @@ -302,6 +307,9 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, > uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; > struct drm_connector *connector; > > + if (pipe == 1) > + return oaktrail_crtc_hdmi_mode_set(crtc, mode, adjusted_mode, x, y, old_fb); > + > if (!gma_power_begin(dev, true)) > return 0; > > diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c > index cf49ba5..a177082 100644 > --- a/drivers/gpu/drm/gma500/oaktrail_device.c > +++ b/drivers/gpu/drm/gma500/oaktrail_device.c > @@ -544,7 +544,7 @@ const struct psb_ops oaktrail_chip_ops = { > .accel_2d = 1, > .pipes = 2, > .crtcs = 2, > - .hdmi_mask = (1 << 0), > + .hdmi_mask = (1 << 1), > .lvds_mask = (1 << 0), > .cursor_needs_phys = 0, > .sgx_offset = MRST_SGX_OFFSET, > diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c > index 2eb3dc4..b0c83fa 100644 > --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c > +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c > @@ -155,6 +155,346 @@ static void oaktrail_hdmi_audio_disable(struct drm_device *dev) > HDMI_READ(HDMI_HCR); > } > > +static void wait_for_vblank(struct drm_device *dev) > +{ > + /* Wait for 20ms, i.e. one cycle at 50hz. */ > + mdelay(20); > +} > + > +static unsigned int htotal_calculate(struct drm_display_mode *mode) > +{ > + u32 htotal, new_crtc_htotal; > + > + htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16); > + > + /* > + * 1024 x 768 new_crtc_htotal = 0x1024; > + * 1280 x 1024 new_crtc_htotal = 0x0c34; > + */ > + new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock; > + > + DRM_DEBUG_KMS("new crtc htotal 0x%4x\n", new_crtc_htotal); > + return ((mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16)); > +} > + > +static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target, > + int refclk, struct oaktrail_hdmi_clock *best_clock) > +{ > + int np_min, np_max, nr_min, nr_max; > + int np, nr, nf; > + > + np_min = DIV_ROUND_UP(oaktrail_hdmi_limit.vco.min, target * 10); > + np_max = oaktrail_hdmi_limit.vco.max / (target * 10); > + if (np_min < oaktrail_hdmi_limit.np.min) > + np_min = oaktrail_hdmi_limit.np.min; > + if (np_max > oaktrail_hdmi_limit.np.max) > + np_max = oaktrail_hdmi_limit.np.max; > + > + nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max)); > + nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min)); > + if (nr_min < oaktrail_hdmi_limit.nr.min) > + nr_min = oaktrail_hdmi_limit.nr.min; > + if (nr_max > oaktrail_hdmi_limit.nr.max) > + nr_max = oaktrail_hdmi_limit.nr.max; > + > + np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max)); > + nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np)); > + nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk); > + DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf); > + > + /* > + * 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000; > + * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000; > + */ > + best_clock->np = np; > + best_clock->nr = nr - 1; > + best_clock->nf = (nf << 14); > +} > + > +static void scu_busy_loop(void __iomem *scu_base) > +{ > + u32 status = 0; > + u32 loop_count = 0; > + > + status = readl(scu_base + 0x04); > + while (status & 1) { > + udelay(1); /* scu processing time is in few u secods */ > + status = readl(scu_base + 0x04); > + loop_count++; > + /* break if scu doesn't reset busy bit after huge retry */ > + if (loop_count > 1000) { > + DRM_DEBUG_KMS("SCU IPC timed out"); > + return; > + } > + } > +} > + > +/* > + * You don't want to know, you really really don't want to know.... > + * > + * This is magic. However it's safe magic because of the way the platform > + * works and it is necessary magic. > + */ > +static void oaktrail_hdmi_reset(struct drm_device *dev) > +{ > + void __iomem *base; > + unsigned long scu_ipc_mmio = 0xff11c000UL; > + int scu_len = 1024; > + > + base = ioremap((resource_size_t)scu_ipc_mmio, scu_len); > + if (base == NULL) { > + DRM_ERROR("failed to map scu mmio \n"); > + return; > + } > + > + /* scu ipc: assert hdmi controller reset */ > + writel(0xff11d118, base + 0x0c); > + writel(0x7fffffdf, base + 0x80); > + writel(0x42005, base + 0x0); > + scu_busy_loop(base); > + > + /* scu ipc: de-assert hdmi controller reset */ > + writel(0xff11d118, base + 0x0c); > + writel(0x7fffffff, base + 0x80); > + writel(0x42005, base + 0x0); > + scu_busy_loop(base); > + > + iounmap(base); > +} > + > +int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode, > + int x, int y, > + struct drm_framebuffer *old_fb) > +{ > + struct drm_device *dev = crtc->dev; > + struct drm_psb_private *dev_priv = dev->dev_private; > + struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; > + int pipe = 1; > + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; > + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; > + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; > + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; > + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; > + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; > + int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; > + int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; > + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; > + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; > + int refclk; > + struct oaktrail_hdmi_clock clock; > + u32 dspcntr, pipeconf, dpll, temp; > + int dspcntr_reg = DSPBCNTR; > + > + if (!gma_power_begin(dev, true)) > + return 0; > + > + /* Disable the VGA plane that we never use */ > + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); > + > + /* Disable dpll if necessary */ > + dpll = REG_READ(DPLL_CTRL); > + if ((dpll & DPLL_PWRDN) == 0) { > + REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET)); > + REG_WRITE(DPLL_DIV_CTRL, 0x00000000); > + REG_WRITE(DPLL_STATUS, 0x1); > + } > + udelay(150); > + > + /* Reset controller */ > + oaktrail_hdmi_reset(dev); > + > + /* program and enable dpll */ > + refclk = 25000; > + oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock); > + > + /* Set the DPLL */ > + dpll = REG_READ(DPLL_CTRL); > + dpll &= ~DPLL_PDIV_MASK; > + dpll &= ~(DPLL_PWRDN | DPLL_RESET); > + REG_WRITE(DPLL_CTRL, 0x00000008); > + REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr)); > + REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1)); > + REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN)); > + REG_WRITE(DPLL_UPDATE, 0x80000000); > + REG_WRITE(DPLL_CLK_ENABLE, 0x80050102); > + udelay(150); > + > + /* configure HDMI */ > + HDMI_WRITE(0x1004, 0x1fd); > + HDMI_WRITE(0x2000, 0x1); > + HDMI_WRITE(0x2008, 0x0); > + HDMI_WRITE(0x3130, 0x8); > + HDMI_WRITE(0x101c, 0x1800810); > + > + temp = htotal_calculate(adjusted_mode); > + REG_WRITE(htot_reg, temp); > + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); > + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); > + REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); > + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); > + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); > + REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); > + > + REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); > + REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); > + REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); > + REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); > + REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); > + REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); > + REG_WRITE(PCH_PIPEBSRC, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); > + > + temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start; > + HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp); > + > + REG_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); > + REG_WRITE(dsppos_reg, 0); > + > + /* Flush the plane changes */ > + { > + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; > + > + crtc_funcs->mode_set_base(crtc, x, y, old_fb); > + } > + > + /* Set up the display plane register */ > + dspcntr = REG_READ(dspcntr_reg); > + dspcntr |= DISPPLANE_GAMMA_ENABLE; > + dspcntr |= DISPPLANE_SEL_PIPE_B; > + dspcntr |= DISPLAY_PLANE_ENABLE; > + > + /* setup pipeconf */ > + pipeconf = REG_READ(pipeconf_reg); > + pipeconf |= PIPEACONF_ENABLE; > + > + REG_WRITE(pipeconf_reg, pipeconf); > + REG_READ(pipeconf_reg); > + > + REG_WRITE(PCH_PIPEBCONF, pipeconf); > + REG_READ(PCH_PIPEBCONF); > + wait_for_vblank(dev); > + > + REG_WRITE(dspcntr_reg, dspcntr); > + wait_for_vblank(dev); > + > + gma_power_end(dev); > + > + return 0; > +} > + > +void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode) > +{ > + struct drm_device *dev = crtc->dev; > + u32 temp; > + > + DRM_DEBUG_KMS("%s %d\n", __func__, mode); > + > + switch (mode) { > + case DRM_MODE_DPMS_OFF: > + REG_WRITE(VGACNTRL, 0x80000000); > + > + /* Disable plane */ > + temp = REG_READ(DSPBCNTR); > + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { > + REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE); > + REG_READ(DSPBCNTR); > + /* Flush the plane changes */ > + REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); > + REG_READ(DSPBSURF); > + } > + > + /* Disable pipe B */ > + temp = REG_READ(PIPEBCONF); > + if ((temp & PIPEACONF_ENABLE) != 0) { > + REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE); > + REG_READ(PIPEBCONF); > + } > + > + /* Disable LNW Pipes, etc */ > + temp = REG_READ(PCH_PIPEBCONF); > + if ((temp & PIPEACONF_ENABLE) != 0) { > + REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE); > + REG_READ(PCH_PIPEBCONF); > + } > + > + /* wait for pipe off */ > + udelay(150); > + > + /* Disable dpll */ > + temp = REG_READ(DPLL_CTRL); > + if ((temp & DPLL_PWRDN) == 0) { > + REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET)); > + REG_WRITE(DPLL_STATUS, 0x1); > + } > + > + /* wait for dpll off */ > + udelay(150); > + > + break; > + case DRM_MODE_DPMS_ON: > + case DRM_MODE_DPMS_STANDBY: > + case DRM_MODE_DPMS_SUSPEND: > + /* Enable dpll */ > + temp = REG_READ(DPLL_CTRL); > + if ((temp & DPLL_PWRDN) != 0) { > + REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET)); > + temp = REG_READ(DPLL_CLK_ENABLE); > + REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI); > + REG_READ(DPLL_CLK_ENABLE); > + } > + /* wait for dpll warm up */ > + udelay(150); > + > + /* Enable pipe B */ > + temp = REG_READ(PIPEBCONF); > + if ((temp & PIPEACONF_ENABLE) == 0) { > + REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE); > + REG_READ(PIPEBCONF); > + } > + > + /* Enable LNW Pipe B */ > + temp = REG_READ(PCH_PIPEBCONF); > + if ((temp & PIPEACONF_ENABLE) == 0) { > + REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE); > + REG_READ(PCH_PIPEBCONF); > + } > + > + wait_for_vblank(dev); > + > + /* Enable plane */ > + temp = REG_READ(DSPBCNTR); > + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { > + REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE); > + /* Flush the plane changes */ > + REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); > + REG_READ(DSPBSURF); > + } > + > + psb_intel_crtc_load_lut(crtc); > + } > + > + /* DSPARB */ > + REG_WRITE(DSPARB, 0x00003fbf); > + > + /* FW1 */ > + REG_WRITE(0x70034, 0x3f880a0a); > + > + /* FW2 */ > + REG_WRITE(0x70038, 0x0b060808); > + > + /* FW4 */ > + REG_WRITE(0x70050, 0x08030404); > + > + /* FW5 */ > + REG_WRITE(0x70054, 0x04040404); > + > + /* LNC Chicken Bits - Squawk! */ > + REG_WRITE(0x70400, 0x4000); > + > + return; > +} > + > static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode) > { > static int dpms_mode = -1; > @@ -233,13 +573,15 @@ static const unsigned char raw_edid[] = { > > static int oaktrail_hdmi_get_modes(struct drm_connector *connector) > { > - struct drm_device *dev = connector->dev; > - struct drm_psb_private *dev_priv = dev->dev_private; > struct i2c_adapter *i2c_adap; > struct edid *edid; > - struct drm_display_mode *mode, *t; > - int i = 0, ret = 0; > + int ret = 0; > > + /* > + * FIXME: We need to figure this lot out. In theory we can > + * read the EDID somehow but I've yet to find working reference > + * code. > + */ > i2c_adap = i2c_get_adapter(3); > if (i2c_adap == NULL) { > DRM_ERROR("No ddc adapter available!\n"); > @@ -254,17 +596,7 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector) > ret = drm_add_edid_modes(connector, edid); > connector->display_info.raw_edid = NULL; > } > - > - /* > - * prune modes that require frame buffer bigger than stolen mem > - */ > - list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { > - if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) { > - i++; > - drm_mode_remove(connector, mode); > - } > - } > - return ret - i; > + return ret; > } > > static void oaktrail_hdmi_mode_set(struct drm_encoder *encoder, > @@ -350,6 +682,7 @@ void oaktrail_hdmi_init(struct drm_device *dev, > connector->interlace_allowed = false; > connector->doublescan_allowed = false; > drm_sysfs_connector_add(connector); > + dev_info(dev->dev, "HDMI initialised.\n"); > > return; > > @@ -404,6 +737,9 @@ void oaktrail_hdmi_setup(struct drm_device *dev) > > dev_priv->hdmi_priv = hdmi_dev; > oaktrail_hdmi_audio_disable(dev); > + > + dev_info(dev->dev, "HDMI hardware present.\n"); > + > return; > > free: > > _______________________________________________ > dri-devel mailing list > dri-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/dri-devel _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel