mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
f4510a2752
Now that CRTC's have a primary plane, there's no need to track the framebuffer in the CRTC. Replace all references to the CRTC fb with the primary plane's fb. This patch was generated by the Coccinelle semantic patching tool using the following rules: @@ struct drm_crtc C; @@ - (C).fb + C.primary->fb @@ struct drm_crtc *C; @@ - (C)->fb + C->primary->fb v3: Generate patch via coccinelle. Actual removal of crtc->fb has been moved to a subsequent patch. v2: Fixup several lingering crtc->fb instances that were missed in the first patch iteration. [Rob Clark] Signed-off-by: Matt Roper <matthew.d.roper@intel.com> Reviewed-by: Rob Clark <robdclark@gmail.com>
673 lines
19 KiB
C
673 lines
19 KiB
C
/*
|
|
* Copyright © 2009 Intel Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#include <drm/drmP.h>
|
|
#include "framebuffer.h"
|
|
#include "psb_drv.h"
|
|
#include "psb_intel_drv.h"
|
|
#include "psb_intel_reg.h"
|
|
#include "gma_display.h"
|
|
#include "power.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 = dev->dev_private;
|
|
|
|
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));
|
|
|
|
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));
|
|
|
|
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 = dev->dev_private;
|
|
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 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 = dev->dev_private;
|
|
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 drm_mode_config *mode_config = &dev->mode_config;
|
|
struct gma_encoder *gma_encoder = NULL;
|
|
uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN;
|
|
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;
|
|
|
|
memcpy(&gma_crtc->saved_mode,
|
|
mode,
|
|
sizeof(struct drm_display_mode));
|
|
memcpy(&gma_crtc->saved_adjusted_mode,
|
|
adjusted_mode,
|
|
sizeof(struct drm_display_mode));
|
|
|
|
list_for_each_entry(connector, &mode_config->connector_list, head) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* 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 (gma_encoder)
|
|
drm_object_property_get_value(&connector->base,
|
|
dev->mode_config.scaling_mode_property, &scalingType);
|
|
|
|
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 */
|
|
{
|
|
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 = dev->dev_private;
|
|
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
|
|
struct psb_framebuffer *psbfb = to_psb_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 (!crtc->primary->fb) {
|
|
dev_dbg(dev->dev, "No FB bound\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!gma_power_begin(dev, true))
|
|
return 0;
|
|
|
|
start = psbfb->gtt->offset;
|
|
offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8);
|
|
|
|
REG_WRITE(map->stride, crtc->primary->fb->pitches[0]);
|
|
|
|
dspcntr = REG_READ(map->cntr);
|
|
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
|
|
|
|
switch (crtc->primary->fb->bits_per_pixel) {
|
|
case 8:
|
|
dspcntr |= DISPPLANE_8BPP;
|
|
break;
|
|
case 16:
|
|
if (crtc->primary->fb->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_fixup = gma_crtc_mode_fixup,
|
|
.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,
|
|
};
|