[PATCH] gma600: Enable HDMI support
Dave Airlie
airlied at gmail.com
Wed Sep 12 18:38:20 PDT 2012
> 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 at 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 at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
More information about the dri-devel
mailing list