diff options
Diffstat (limited to 'drivers/gpu/drm/gma500/oaktrail_device.c')
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_device.c | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c new file mode 100644 index 000000000..2531959d3 --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0-only +/************************************************************************** + * Copyright (c) 2011, Intel Corporation. + * All Rights Reserved. + * + **************************************************************************/ + +#include <linux/delay.h> +#include <linux/dmi.h> +#include <linux/module.h> + +#include <drm/drm.h> + +#include "intel_bios.h" +#include "mid_bios.h" +#include "psb_drv.h" +#include "psb_intel_reg.h" +#include "psb_reg.h" + +static int oaktrail_output_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + if (dev_priv->iLVDS_enable) + oaktrail_lvds_init(dev, &dev_priv->mode_dev); + else + dev_err(dev->dev, "DSI is not supported\n"); + if (dev_priv->hdmi_priv) + oaktrail_hdmi_init(dev, &dev_priv->mode_dev); + + psb_intel_sdvo_init(dev, SDVOB); + + return 0; +} + +/* + * Provide the low level interfaces for the Moorestown backlight + */ + +#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF +#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ +#define BLC_PWM_FREQ_CALC_CONSTANT 32 +#define MHz 1000000 +#define BLC_ADJUSTMENT_MAX 100 + +static void oaktrail_set_brightness(struct drm_device *dev, int level) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + u32 blc_pwm_ctl; + u32 max_pwm_blc; + + if (gma_power_begin(dev, 0)) { + /* Calculate and set the brightness value */ + max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16; + blc_pwm_ctl = level * max_pwm_blc / 100; + + /* Adjust the backlight level with the percent in + * dev_priv->blc_adj1; + */ + blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1; + blc_pwm_ctl = blc_pwm_ctl / 100; + + /* Adjust the backlight level with the percent in + * dev_priv->blc_adj2; + */ + blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2; + blc_pwm_ctl = blc_pwm_ctl / 100; + + /* force PWM bit on */ + REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); + REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl); + gma_power_end(dev); + } +} + +static int oaktrail_backlight_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + unsigned long core_clock; + u16 bl_max_freq; + uint32_t value; + uint32_t blc_pwm_precision_factor; + + dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; + dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; + bl_max_freq = 256; + /* this needs to be set elsewhere */ + blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR; + + core_clock = dev_priv->core_freq; + + value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; + value *= blc_pwm_precision_factor; + value /= bl_max_freq; + value /= blc_pwm_precision_factor; + + if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ) + return -ERANGE; + + if (gma_power_begin(dev, false)) { + REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); + REG_WRITE(BLC_PWM_CTL, value | (value << 16)); + gma_power_end(dev); + } + + oaktrail_set_brightness(dev, PSB_MAX_BRIGHTNESS); + return 0; +} + +/* + * Provide the Moorestown specific chip logic and low level methods + * for power management + */ + +/** + * oaktrail_save_display_registers - save registers lost on suspend + * @dev: our DRM device + * + * Save the state we need in order to be able to restore the interface + * upon resume from suspend + */ +static int oaktrail_save_display_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct psb_save_area *regs = &dev_priv->regs; + struct psb_pipe *p = ®s->pipe[0]; + int i; + u32 pp_stat; + + /* Display arbitration control + watermarks */ + regs->psb.saveDSPARB = PSB_RVDC32(DSPARB); + regs->psb.saveDSPFW1 = PSB_RVDC32(DSPFW1); + regs->psb.saveDSPFW2 = PSB_RVDC32(DSPFW2); + regs->psb.saveDSPFW3 = PSB_RVDC32(DSPFW3); + regs->psb.saveDSPFW4 = PSB_RVDC32(DSPFW4); + regs->psb.saveDSPFW5 = PSB_RVDC32(DSPFW5); + regs->psb.saveDSPFW6 = PSB_RVDC32(DSPFW6); + regs->psb.saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); + + /* Pipe & plane A info */ + p->conf = PSB_RVDC32(PIPEACONF); + p->src = PSB_RVDC32(PIPEASRC); + p->fp0 = PSB_RVDC32(MRST_FPA0); + p->fp1 = PSB_RVDC32(MRST_FPA1); + p->dpll = PSB_RVDC32(MRST_DPLL_A); + p->htotal = PSB_RVDC32(HTOTAL_A); + p->hblank = PSB_RVDC32(HBLANK_A); + p->hsync = PSB_RVDC32(HSYNC_A); + p->vtotal = PSB_RVDC32(VTOTAL_A); + p->vblank = PSB_RVDC32(VBLANK_A); + p->vsync = PSB_RVDC32(VSYNC_A); + regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); + p->cntr = PSB_RVDC32(DSPACNTR); + p->stride = PSB_RVDC32(DSPASTRIDE); + p->addr = PSB_RVDC32(DSPABASE); + p->surf = PSB_RVDC32(DSPASURF); + p->linoff = PSB_RVDC32(DSPALINOFF); + p->tileoff = PSB_RVDC32(DSPATILEOFF); + + /* Save cursor regs */ + regs->psb.saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); + regs->psb.saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); + regs->psb.saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); + + /* Save palette (gamma) */ + for (i = 0; i < 256; i++) + p->palette[i] = PSB_RVDC32(PALETTE_A + (i << 2)); + + if (dev_priv->hdmi_priv) + oaktrail_hdmi_save(dev); + + /* Save performance state */ + regs->psb.savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); + + /* LVDS state */ + regs->psb.savePP_CONTROL = PSB_RVDC32(PP_CONTROL); + regs->psb.savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); + regs->psb.savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); + regs->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); + regs->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); + regs->psb.saveLVDS = PSB_RVDC32(LVDS); + regs->psb.savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); + regs->psb.savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); + regs->psb.savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); + regs->psb.savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); + + /* HW overlay */ + regs->psb.saveOV_OVADD = PSB_RVDC32(OV_OVADD); + regs->psb.saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); + regs->psb.saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); + regs->psb.saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); + regs->psb.saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); + regs->psb.saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); + regs->psb.saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); + + /* DPST registers */ + regs->psb.saveHISTOGRAM_INT_CONTROL_REG = + PSB_RVDC32(HISTOGRAM_INT_CONTROL); + regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG = + PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); + regs->psb.savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); + + if (dev_priv->iLVDS_enable) { + /* Shut down the panel */ + PSB_WVDC32(0, PP_CONTROL); + + do { + pp_stat = PSB_RVDC32(PP_STATUS); + } while (pp_stat & 0x80000000); + + /* Turn off the plane */ + PSB_WVDC32(0x58000000, DSPACNTR); + /* Trigger the plane disable */ + PSB_WVDC32(0, DSPASURF); + + /* Wait ~4 ticks */ + msleep(4); + + /* Turn off pipe */ + PSB_WVDC32(0x0, PIPEACONF); + /* Wait ~8 ticks */ + msleep(8); + + /* Turn off PLLs */ + PSB_WVDC32(0, MRST_DPLL_A); + } + return 0; +} + +/** + * oaktrail_restore_display_registers - restore lost register state + * @dev: our DRM device + * + * Restore register state that was lost during suspend and resume. + */ +static int oaktrail_restore_display_registers(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct psb_save_area *regs = &dev_priv->regs; + struct psb_pipe *p = ®s->pipe[0]; + u32 pp_stat; + int i; + + /* Display arbitration + watermarks */ + PSB_WVDC32(regs->psb.saveDSPARB, DSPARB); + PSB_WVDC32(regs->psb.saveDSPFW1, DSPFW1); + PSB_WVDC32(regs->psb.saveDSPFW2, DSPFW2); + PSB_WVDC32(regs->psb.saveDSPFW3, DSPFW3); + PSB_WVDC32(regs->psb.saveDSPFW4, DSPFW4); + PSB_WVDC32(regs->psb.saveDSPFW5, DSPFW5); + PSB_WVDC32(regs->psb.saveDSPFW6, DSPFW6); + PSB_WVDC32(regs->psb.saveCHICKENBIT, DSPCHICKENBIT); + + /* Make sure VGA plane is off. it initializes to on after reset!*/ + PSB_WVDC32(0x80000000, VGACNTRL); + + /* set the plls */ + PSB_WVDC32(p->fp0, MRST_FPA0); + PSB_WVDC32(p->fp1, MRST_FPA1); + + /* Actually enable it */ + PSB_WVDC32(p->dpll, MRST_DPLL_A); + udelay(150); + + /* Restore mode */ + PSB_WVDC32(p->htotal, HTOTAL_A); + PSB_WVDC32(p->hblank, HBLANK_A); + PSB_WVDC32(p->hsync, HSYNC_A); + PSB_WVDC32(p->vtotal, VTOTAL_A); + PSB_WVDC32(p->vblank, VBLANK_A); + PSB_WVDC32(p->vsync, VSYNC_A); + PSB_WVDC32(p->src, PIPEASRC); + PSB_WVDC32(regs->psb.saveBCLRPAT_A, BCLRPAT_A); + + /* Restore performance mode*/ + PSB_WVDC32(regs->psb.savePERF_MODE, MRST_PERF_MODE); + + /* Enable the pipe*/ + if (dev_priv->iLVDS_enable) + PSB_WVDC32(p->conf, PIPEACONF); + + /* Set up the plane*/ + PSB_WVDC32(p->linoff, DSPALINOFF); + PSB_WVDC32(p->stride, DSPASTRIDE); + PSB_WVDC32(p->tileoff, DSPATILEOFF); + + /* Enable the plane */ + PSB_WVDC32(p->cntr, DSPACNTR); + PSB_WVDC32(p->surf, DSPASURF); + + /* Enable Cursor A */ + PSB_WVDC32(regs->psb.saveDSPACURSOR_CTRL, CURACNTR); + PSB_WVDC32(regs->psb.saveDSPACURSOR_POS, CURAPOS); + PSB_WVDC32(regs->psb.saveDSPACURSOR_BASE, CURABASE); + + /* Restore palette (gamma) */ + for (i = 0; i < 256; i++) + PSB_WVDC32(p->palette[i], PALETTE_A + (i << 2)); + + if (dev_priv->hdmi_priv) + oaktrail_hdmi_restore(dev); + + if (dev_priv->iLVDS_enable) { + PSB_WVDC32(regs->saveBLC_PWM_CTL2, BLC_PWM_CTL2); + PSB_WVDC32(regs->psb.saveLVDS, LVDS); /*port 61180h*/ + PSB_WVDC32(regs->psb.savePFIT_CONTROL, PFIT_CONTROL); + PSB_WVDC32(regs->psb.savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); + PSB_WVDC32(regs->psb.savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); + PSB_WVDC32(regs->saveBLC_PWM_CTL, BLC_PWM_CTL); + PSB_WVDC32(regs->psb.savePP_ON_DELAYS, LVDSPP_ON); + PSB_WVDC32(regs->psb.savePP_OFF_DELAYS, LVDSPP_OFF); + PSB_WVDC32(regs->psb.savePP_DIVISOR, PP_CYCLE); + PSB_WVDC32(regs->psb.savePP_CONTROL, PP_CONTROL); + } + + /* Wait for cycle delay */ + do { + pp_stat = PSB_RVDC32(PP_STATUS); + } while (pp_stat & 0x08000000); + + /* Wait for panel power up */ + do { + pp_stat = PSB_RVDC32(PP_STATUS); + } while (pp_stat & 0x10000000); + + /* Restore HW overlay */ + PSB_WVDC32(regs->psb.saveOV_OVADD, OV_OVADD); + PSB_WVDC32(regs->psb.saveOV_OGAMC0, OV_OGAMC0); + PSB_WVDC32(regs->psb.saveOV_OGAMC1, OV_OGAMC1); + PSB_WVDC32(regs->psb.saveOV_OGAMC2, OV_OGAMC2); + PSB_WVDC32(regs->psb.saveOV_OGAMC3, OV_OGAMC3); + PSB_WVDC32(regs->psb.saveOV_OGAMC4, OV_OGAMC4); + PSB_WVDC32(regs->psb.saveOV_OGAMC5, OV_OGAMC5); + + /* DPST registers */ + PSB_WVDC32(regs->psb.saveHISTOGRAM_INT_CONTROL_REG, + HISTOGRAM_INT_CONTROL); + PSB_WVDC32(regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG, + HISTOGRAM_LOGIC_CONTROL); + PSB_WVDC32(regs->psb.savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); + + return 0; +} + +/** + * oaktrail_power_down - power down the display island + * @dev: our DRM device + * + * Power down the display interface of our device + */ +static int oaktrail_power_down(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + u32 pwr_mask ; + u32 pwr_sts; + + pwr_mask = PSB_PWRGT_DISPLAY_MASK; + outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC); + + while (true) { + pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); + if ((pwr_sts & pwr_mask) == pwr_mask) + break; + else + udelay(10); + } + return 0; +} + +/* + * oaktrail_power_up + * + * Restore power to the specified island(s) (powergating) + */ +static int oaktrail_power_up(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; + u32 pwr_sts, pwr_cnt; + + pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); + pwr_cnt &= ~pwr_mask; + outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC)); + + while (true) { + pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); + if ((pwr_sts & pwr_mask) == 0) + break; + else + udelay(10); + } + return 0; +} + +/* Oaktrail */ +static const struct psb_offset oaktrail_regmap[2] = { + { + .fp0 = MRST_FPA0, + .fp1 = MRST_FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = MRST_DPLL_A, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .surf = DSPASURF, + .addr = MRST_DSPABASE, + .base = MRST_DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = FPB0, + .fp1 = FPB1, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = DPLL_B, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .surf = DSPBSURF, + .addr = DSPBBASE, + .base = DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + }, +}; + +static int oaktrail_chip_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + int ret; + + dev_priv->use_msi = true; + dev_priv->regmap = oaktrail_regmap; + + ret = mid_chip_setup(dev); + if (ret < 0) + return ret; + if (!dev_priv->has_gct) { + /* Now pull the BIOS data */ + psb_intel_opregion_init(dev); + psb_intel_init_bios(dev); + } + gma_intel_setup_gmbus(dev); + oaktrail_hdmi_setup(dev); + return 0; +} + +static void oaktrail_teardown(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + + gma_intel_teardown_gmbus(dev); + oaktrail_hdmi_teardown(dev); + if (!dev_priv->has_gct) + psb_intel_destroy_bios(dev); +} + +const struct psb_ops oaktrail_chip_ops = { + .name = "Oaktrail", + .pipes = 2, + .crtcs = 2, + .hdmi_mask = (1 << 1), + .lvds_mask = (1 << 0), + .sdvo_mask = (1 << 1), + .cursor_needs_phys = 0, + .sgx_offset = MRST_SGX_OFFSET, + + .chip_setup = oaktrail_chip_setup, + .chip_teardown = oaktrail_teardown, + .crtc_helper = &oaktrail_helper_funcs, + + .output_init = oaktrail_output_init, + + .backlight_init = oaktrail_backlight_init, + .backlight_set = oaktrail_set_brightness, + .backlight_name = "oaktrail-bl", + + .save_regs = oaktrail_save_display_registers, + .restore_regs = oaktrail_restore_display_registers, + .save_crtc = gma_crtc_save, + .restore_crtc = gma_crtc_restore, + .power_down = oaktrail_power_down, + .power_up = oaktrail_power_up, + + .i2c_bus = 1, +}; |