diff options
Diffstat (limited to 'drivers/gpu/drm/gma500/oaktrail_crtc.c')
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_crtc.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c new file mode 100644 index 000000000..64761f46b --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright © 2009 Intel Corporation + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> + +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> + +#include "framebuffer.h" +#include "gem.h" +#include "gma_display.h" +#include "power.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" + +#define MRST_LIMIT_LVDS_100L 0 +#define MRST_LIMIT_LVDS_83 1 +#define MRST_LIMIT_LVDS_100 2 +#define MRST_LIMIT_SDVO 3 + +#define MRST_DOT_MIN 19750 +#define MRST_DOT_MAX 120000 +#define MRST_M_MIN_100L 20 +#define MRST_M_MIN_100 10 +#define MRST_M_MIN_83 12 +#define MRST_M_MAX_100L 34 +#define MRST_M_MAX_100 17 +#define MRST_M_MAX_83 20 +#define MRST_P1_MIN 2 +#define MRST_P1_MAX_0 7 +#define MRST_P1_MAX_1 8 + +static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock); + +static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock); + +static const struct gma_limit_t mrst_limits[] = { + { /* MRST_LIMIT_LVDS_100L */ + .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, + .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L}, + .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, + .find_pll = mrst_lvds_find_best_pll, + }, + { /* MRST_LIMIT_LVDS_83L */ + .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, + .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83}, + .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0}, + .find_pll = mrst_lvds_find_best_pll, + }, + { /* MRST_LIMIT_LVDS_100 */ + .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, + .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100}, + .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, + .find_pll = mrst_lvds_find_best_pll, + }, + { /* MRST_LIMIT_SDVO */ + .vco = {.min = 1400000, .max = 2800000}, + .n = {.min = 3, .max = 7}, + .m = {.min = 80, .max = 137}, + .p1 = {.min = 1, .max = 2}, + .p2 = {.dot_limit = 200000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = mrst_sdvo_find_best_pll, + }, +}; + +#define MRST_M_MIN 10 +static const u32 oaktrail_m_converts[] = { + 0x2B, 0x15, 0x2A, 0x35, 0x1A, 0x0D, 0x26, 0x33, 0x19, 0x2C, + 0x36, 0x3B, 0x1D, 0x2E, 0x37, 0x1B, 0x2D, 0x16, 0x0B, 0x25, + 0x12, 0x09, 0x24, 0x32, 0x39, 0x1c, +}; + +static const struct gma_limit_t *mrst_limit(struct drm_crtc *crtc, + int refclk) +{ + const struct gma_limit_t *limit = NULL; + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) + || gma_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) { + switch (dev_priv->core_freq) { + case 100: + limit = &mrst_limits[MRST_LIMIT_LVDS_100L]; + break; + case 166: + limit = &mrst_limits[MRST_LIMIT_LVDS_83]; + break; + case 200: + limit = &mrst_limits[MRST_LIMIT_LVDS_100]; + break; + } + } else if (gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { + limit = &mrst_limits[MRST_LIMIT_SDVO]; + } else { + limit = NULL; + dev_err(dev->dev, "mrst_limit Wrong display type.\n"); + } + + return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ +static void mrst_lvds_clock(int refclk, struct gma_clock_t *clock) +{ + clock->dot = (refclk * clock->m) / (14 * clock->p1); +} + +static void mrst_print_pll(struct gma_clock_t *clock) +{ + DRM_DEBUG_DRIVER("dotclock=%d, m=%d, m1=%d, m2=%d, n=%d, p1=%d, p2=%d\n", + clock->dot, clock->m, clock->m1, clock->m2, clock->n, + clock->p1, clock->p2); +} + +static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock) +{ + struct gma_clock_t clock; + u32 target_vco, actual_freq; + s32 freq_error, min_error = 100000; + + memset(best_clock, 0, sizeof(*best_clock)); + memset(&clock, 0, sizeof(clock)); + + for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { + 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++) { + /* p2 value always stored in p2_slow on SDVO */ + clock.p = clock.p1 * limit->p2.p2_slow; + target_vco = target * clock.p; + + /* VCO will increase at this point so break */ + if (target_vco > limit->vco.max) + break; + + if (target_vco < limit->vco.min) + continue; + + actual_freq = (refclk * clock.m) / + (clock.n * clock.p); + freq_error = 10000 - + ((target * 10000) / actual_freq); + + if (freq_error < -min_error) { + /* freq_error will start to decrease at + this point so break */ + break; + } + + if (freq_error < 0) + freq_error = -freq_error; + + if (freq_error < min_error) { + min_error = freq_error; + *best_clock = clock; + } + } + } + if (min_error == 0) + break; + } + + return min_error == 0; +} + +/* + * Returns a set of divisors for the desired target clock with the given refclk, + * or FALSE. Divisor values are the actual divisors for + */ +static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock) +{ + struct gma_clock_t clock; + int err = target; + + memset(best_clock, 0, sizeof(*best_clock)); + memset(&clock, 0, sizeof(clock)); + + for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { + for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; + clock.p1++) { + int this_err; + + mrst_lvds_clock(refclk, &clock); + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + return err != target; +} + +/* + * 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. + */ +static void oaktrail_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; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + u32 temp; + int i; + int need_aux = gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ? 1 : 0; + + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) { + oaktrail_crtc_hdmi_dpms(crtc, mode); + return; + } + + if (!gma_power_begin(dev, true)) + return; + + /* 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. + */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + for (i = 0; i <= need_aux; i++) { + /* Enable the DPLL */ + temp = REG_READ_WITH_AUX(map->dpll, i); + if ((temp & DPLL_VCO_ENABLE) == 0) { + REG_WRITE_WITH_AUX(map->dpll, temp, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE_WITH_AUX(map->dpll, + temp | DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE_WITH_AUX(map->dpll, + temp | DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + } + + /* Enable the pipe */ + temp = REG_READ_WITH_AUX(map->conf, i); + if ((temp & PIPEACONF_ENABLE) == 0) { + REG_WRITE_WITH_AUX(map->conf, + temp | PIPEACONF_ENABLE, i); + } + + /* Enable the plane */ + temp = REG_READ_WITH_AUX(map->cntr, i); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE_WITH_AUX(map->cntr, + temp | DISPLAY_PLANE_ENABLE, + i); + /* Flush the plane changes */ + REG_WRITE_WITH_AUX(map->base, + REG_READ_WITH_AUX(map->base, i), i); + } + + } + gma_crtc_load_lut(crtc); + + /* Give the overlay scaler a chance to enable + if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, true); TODO */ + break; + case DRM_MODE_DPMS_OFF: + /* Give the overlay scaler a chance to disable + * if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ + + for (i = 0; i <= need_aux; i++) { + /* Disable the VGA plane that we never use */ + REG_WRITE_WITH_AUX(VGACNTRL, VGA_DISP_DISABLE, i); + /* Disable display plane */ + temp = REG_READ_WITH_AUX(map->cntr, i); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE_WITH_AUX(map->cntr, + temp & ~DISPLAY_PLANE_ENABLE, i); + /* Flush the plane changes */ + REG_WRITE_WITH_AUX(map->base, + REG_READ(map->base), i); + REG_READ_WITH_AUX(map->base, i); + } + + /* Next, disable display pipes */ + temp = REG_READ_WITH_AUX(map->conf, i); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE_WITH_AUX(map->conf, + temp & ~PIPEACONF_ENABLE, i); + REG_READ_WITH_AUX(map->conf, i); + } + /* Wait for the pipe disable to take effect. */ + gma_wait_for_vblank(dev); + + temp = REG_READ_WITH_AUX(map->dpll, i); + if ((temp & DPLL_VCO_ENABLE) != 0) { + REG_WRITE_WITH_AUX(map->dpll, + temp & ~DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + } + break; + } + + /* Set FIFO Watermarks (values taken from EMGD) */ + REG_WRITE(DSPARB, 0x3f80); + REG_WRITE(DSPFW1, 0x3f8f0404); + REG_WRITE(DSPFW2, 0x04040f04); + REG_WRITE(DSPFW3, 0x0); + REG_WRITE(DSPFW4, 0x04040404); + REG_WRITE(DSPFW5, 0x04040404); + REG_WRITE(DSPFW6, 0x78); + REG_WRITE(DSPCHICKENBIT, REG_READ(DSPCHICKENBIT) | 0xc040); + + gma_power_end(dev); +} + +/* + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int oaktrail_panel_fitter_pipe(struct drm_device *dev) +{ + u32 pfit_control; + + pfit_control = REG_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + return (pfit_control >> 29) & 3; +} + +static int oaktrail_crtc_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 gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + int refclk = 0; + struct gma_clock_t clock; + const struct gma_limit_t *limit; + u32 dpll = 0, fp = 0, dspcntr, pipeconf; + bool ok, is_sdvo = false; + bool is_lvds = false; + bool is_mipi = false; + struct gma_encoder *gma_encoder = NULL; + uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; + int i; + int need_aux = gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ? 1 : 0; + + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) + return oaktrail_crtc_hdmi_mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + + if (!gma_power_begin(dev, true)) + return 0; + + drm_mode_copy(&gma_crtc->saved_mode, mode); + drm_mode_copy(&gma_crtc->saved_adjusted_mode, adjusted_mode); + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (!connector->encoder || connector->encoder->crtc != crtc) + continue; + + gma_encoder = gma_attached_encoder(connector); + + switch (gma_encoder->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_SDVO: + is_sdvo = true; + break; + case INTEL_OUTPUT_MIPI: + is_mipi = true; + break; + } + + break; + } + + if (gma_encoder) + drm_object_property_get_value(&connector->base, + dev->mode_config.scaling_mode_property, &scalingType); + + drm_connector_list_iter_end(&conn_iter); + + /* Disable the VGA plane that we never use */ + for (i = 0; i <= need_aux; i++) + REG_WRITE_WITH_AUX(VGACNTRL, VGA_DISP_DISABLE, i); + + /* Disable the panel fitter if it was on our pipe */ + if (oaktrail_panel_fitter_pipe(dev) == pipe) + REG_WRITE(PFIT_CONTROL, 0); + + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->src, ((mode->crtc_hdisplay - 1) << 16) | + (mode->crtc_vdisplay - 1), i); + } + + if (scalingType == DRM_MODE_SCALE_NO_SCALE) { + /* Moorestown doesn't have register support for centering so + * we need to mess with the h/vblank and h/vsync start and + * ends to get centering */ + int offsetX = 0, offsetY = 0; + + offsetX = (adjusted_mode->crtc_hdisplay - + mode->crtc_hdisplay) / 2; + offsetY = (adjusted_mode->crtc_vdisplay - + mode->crtc_vdisplay) / 2; + + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->htotal, (mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vtotal, (mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hblank, + (adjusted_mode->crtc_hblank_start - offsetX - 1) | + ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hsync, + (adjusted_mode->crtc_hsync_start - offsetX - 1) | + ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vblank, + (adjusted_mode->crtc_vblank_start - offsetY - 1) | + ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vsync, + (adjusted_mode->crtc_vsync_start - offsetY - 1) | + ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16), i); + } + } else { + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16), i); + } + } + + /* Flush the plane changes */ + { + const struct drm_crtc_helper_funcs *crtc_funcs = + crtc->helper_private; + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + } + + /* setup pipeconf */ + pipeconf = REG_READ(map->conf); + + /* Set up the display plane register */ + dspcntr = REG_READ(map->cntr); + dspcntr |= DISPPLANE_GAMMA_ENABLE; + + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + + if (is_mipi) + goto oaktrail_crtc_mode_set_exit; + + + dpll = 0; /*BIT16 = 0 for 100MHz reference */ + + refclk = is_sdvo ? 96000 : dev_priv->core_freq * 1000; + limit = mrst_limit(crtc, refclk); + ok = limit->find_pll(limit, crtc, adjusted_mode->clock, + refclk, &clock); + + if (is_sdvo) { + /* Convert calculated values to register values */ + clock.p1 = (1L << (clock.p1 - 1)); + clock.m -= 2; + clock.n = (1L << (clock.n - 1)); + } + + if (!ok) + DRM_ERROR("Failed to find proper PLL settings"); + + mrst_print_pll(&clock); + + if (is_sdvo) + fp = clock.n << 16 | clock.m; + else + fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8; + + dpll |= DPLL_VGA_MODE_DIS; + + + dpll |= DPLL_VCO_ENABLE; + + if (is_lvds) + dpll |= DPLLA_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + + if (is_sdvo) { + int sdvo_pixel_multiply = + adjusted_mode->clock / mode->clock; + + dpll |= DPLL_DVO_HIGH_SPEED; + dpll |= + (sdvo_pixel_multiply - + 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + } + + + /* compute bitmask from p1 value */ + if (is_sdvo) + dpll |= clock.p1 << 16; // dpll |= (1 << (clock.p1 - 1)) << 16; + else + dpll |= (1 << (clock.p1 - 2)) << 17; + + dpll |= DPLL_VCO_ENABLE; + + if (dpll & DPLL_VCO_ENABLE) { + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->fp0, fp, i); + REG_WRITE_WITH_AUX(map->dpll, dpll & ~DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Check the DPLLA lock bit PIPEACONF[29] */ + udelay(150); + } + } + + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->fp0, fp, i); + REG_WRITE_WITH_AUX(map->dpll, dpll, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + + /* write it again -- the BIOS does, after all */ + REG_WRITE_WITH_AUX(map->dpll, dpll, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + + REG_WRITE_WITH_AUX(map->conf, pipeconf, i); + REG_READ_WITH_AUX(map->conf, i); + gma_wait_for_vblank(dev); + + REG_WRITE_WITH_AUX(map->cntr, dspcntr, i); + gma_wait_for_vblank(dev); + } + +oaktrail_crtc_mode_set_exit: + gma_power_end(dev); + return 0; +} + +static int oaktrail_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; + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + unsigned long start, offset; + + u32 dspcntr; + int ret = 0; + + /* no fb bound */ + if (!fb) { + dev_dbg(dev->dev, "No FB bound\n"); + return 0; + } + + if (!gma_power_begin(dev, true)) + return 0; + + start = to_psb_gem_object(fb->obj[0])->offset; + offset = y * fb->pitches[0] + x * fb->format->cpp[0]; + + REG_WRITE(map->stride, fb->pitches[0]); + + dspcntr = REG_READ(map->cntr); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (fb->format->cpp[0] * 8) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (fb->format->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + dev_err(dev->dev, "Unknown color depth\n"); + ret = -EINVAL; + goto pipe_set_base_exit; + } + REG_WRITE(map->cntr, dspcntr); + + REG_WRITE(map->base, offset); + REG_READ(map->base); + REG_WRITE(map->surf, start); + REG_READ(map->surf); + +pipe_set_base_exit: + gma_power_end(dev); + return ret; +} + +const struct drm_crtc_helper_funcs oaktrail_helper_funcs = { + .dpms = oaktrail_crtc_dpms, + .mode_set = oaktrail_crtc_mode_set, + .mode_set_base = oaktrail_pipe_set_base, + .prepare = gma_crtc_prepare, + .commit = gma_crtc_commit, +}; + +/* Not used yet */ +const struct gma_clock_funcs mrst_clock_funcs = { + .clock = mrst_lvds_clock, + .limit = mrst_limit, + .pll_is_valid = gma_pll_is_valid, +}; |