Message ID | 20120912202227.10823.43034.stgit@localhost.localdomain (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
> 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@linux.intel.com> > --- > > 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@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Thu, 13 Sep 2012 11:38:20 +1000 Dave Airlie <airlied@gmail.com> wrote: > > 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 :-) It is indeed for -next. Whitespace damage of what kind, messed up space/tabbing or 'doesn't apply eaten by mail system' ? If it's the former then I'll send you a couple of follow up patches to clean up the driver further fairly soon. Alan
On Thu, Sep 13, 2012 at 10:30 PM, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote: > On Thu, 13 Sep 2012 11:38:20 +1000 > Dave Airlie <airlied@gmail.com> wrote: > >> > 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 :-) > > It is indeed for -next. > > Whitespace damage of what kind, messed up space/tabbing or 'doesn't apply > eaten by mail system' ? just messed up spaces, trailing spaces/tabs, i.e. git am complains. Dave.
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: