gma500: Add Oaktrail support
Oaktrail (GMA600) is found on some tablet/slate PC type systems. It's a bit different to the GMA500 but similar enough it makes sense to plug it into the same driver. Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
parent
89c78134cc
commit
1b082ccf59
252
drivers/gpu/drm/gma500/oaktrail.h
Normal file
252
drivers/gpu/drm/gma500/oaktrail.h
Normal file
@ -0,0 +1,252 @@
|
||||
/**************************************************************************
|
||||
* Copyright (c) 2007-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/* MID device specific descriptors */
|
||||
|
||||
struct oaktrail_vbt {
|
||||
s8 signature[4]; /*4 bytes,"$GCT" */
|
||||
u8 revision;
|
||||
u8 size;
|
||||
u8 checksum;
|
||||
void *oaktrail_gct;
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_timing_info {
|
||||
u16 pixel_clock;
|
||||
u8 hactive_lo;
|
||||
u8 hblank_lo;
|
||||
u8 hblank_hi:4;
|
||||
u8 hactive_hi:4;
|
||||
u8 vactive_lo;
|
||||
u8 vblank_lo;
|
||||
u8 vblank_hi:4;
|
||||
u8 vactive_hi:4;
|
||||
u8 hsync_offset_lo;
|
||||
u8 hsync_pulse_width_lo;
|
||||
u8 vsync_pulse_width_lo:4;
|
||||
u8 vsync_offset_lo:4;
|
||||
u8 vsync_pulse_width_hi:2;
|
||||
u8 vsync_offset_hi:2;
|
||||
u8 hsync_pulse_width_hi:2;
|
||||
u8 hsync_offset_hi:2;
|
||||
u8 width_mm_lo;
|
||||
u8 height_mm_lo;
|
||||
u8 height_mm_hi:4;
|
||||
u8 width_mm_hi:4;
|
||||
u8 hborder;
|
||||
u8 vborder;
|
||||
u8 unknown0:1;
|
||||
u8 hsync_positive:1;
|
||||
u8 vsync_positive:1;
|
||||
u8 separate_sync:2;
|
||||
u8 stereo:1;
|
||||
u8 unknown6:1;
|
||||
u8 interlaced:1;
|
||||
} __packed;
|
||||
|
||||
struct gct_r10_timing_info {
|
||||
u16 pixel_clock;
|
||||
u32 hactive_lo:8;
|
||||
u32 hactive_hi:4;
|
||||
u32 hblank_lo:8;
|
||||
u32 hblank_hi:4;
|
||||
u32 hsync_offset_lo:8;
|
||||
u16 hsync_offset_hi:2;
|
||||
u16 hsync_pulse_width_lo:8;
|
||||
u16 hsync_pulse_width_hi:2;
|
||||
u16 hsync_positive:1;
|
||||
u16 rsvd_1:3;
|
||||
u8 vactive_lo:8;
|
||||
u16 vactive_hi:4;
|
||||
u16 vblank_lo:8;
|
||||
u16 vblank_hi:4;
|
||||
u16 vsync_offset_lo:4;
|
||||
u16 vsync_offset_hi:2;
|
||||
u16 vsync_pulse_width_lo:4;
|
||||
u16 vsync_pulse_width_hi:2;
|
||||
u16 vsync_positive:1;
|
||||
u16 rsvd_2:3;
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_panel_descriptor_v1 {
|
||||
u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */
|
||||
/* 0x61190 if MIPI */
|
||||
u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/
|
||||
u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/
|
||||
u32 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 dword */
|
||||
/* Register 0x61210 */
|
||||
struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */
|
||||
u16 Panel_Backlight_Inverter_Descriptor;/* 16 bits, as follows */
|
||||
/* Bit 0, Frequency, 15 bits,0 - 32767Hz */
|
||||
/* Bit 15, Polarity, 1 bit, 0: Normal, 1: Inverted */
|
||||
u16 Panel_MIPI_Display_Descriptor;
|
||||
/*16 bits, Defined as follows: */
|
||||
/* if MIPI, 0x0000 if LVDS */
|
||||
/* Bit 0, Type, 2 bits, */
|
||||
/* 0: Type-1, */
|
||||
/* 1: Type-2, */
|
||||
/* 2: Type-3, */
|
||||
/* 3: Type-4 */
|
||||
/* Bit 2, Pixel Format, 4 bits */
|
||||
/* Bit0: 16bpp (not supported in LNC), */
|
||||
/* Bit1: 18bpp loosely packed, */
|
||||
/* Bit2: 18bpp packed, */
|
||||
/* Bit3: 24bpp */
|
||||
/* Bit 6, Reserved, 2 bits, 00b */
|
||||
/* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */
|
||||
/* Bit 14, Reserved, 2 bits, 00b */
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_panel_descriptor_v2 {
|
||||
u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */
|
||||
/* 0x61190 if MIPI */
|
||||
u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/
|
||||
u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/
|
||||
u8 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 byte */
|
||||
/* Register 0x61210 */
|
||||
struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */
|
||||
u16 Panel_Backlight_Inverter_Descriptor;/*16 bits, as follows*/
|
||||
/*Bit 0, Frequency, 16 bits, 0 - 32767Hz*/
|
||||
u8 Panel_Initial_Brightness;/* [7:0] 0 - 100% */
|
||||
/*Bit 7, Polarity, 1 bit,0: Normal, 1: Inverted*/
|
||||
u16 Panel_MIPI_Display_Descriptor;
|
||||
/*16 bits, Defined as follows: */
|
||||
/* if MIPI, 0x0000 if LVDS */
|
||||
/* Bit 0, Type, 2 bits, */
|
||||
/* 0: Type-1, */
|
||||
/* 1: Type-2, */
|
||||
/* 2: Type-3, */
|
||||
/* 3: Type-4 */
|
||||
/* Bit 2, Pixel Format, 4 bits */
|
||||
/* Bit0: 16bpp (not supported in LNC), */
|
||||
/* Bit1: 18bpp loosely packed, */
|
||||
/* Bit2: 18bpp packed, */
|
||||
/* Bit3: 24bpp */
|
||||
/* Bit 6, Reserved, 2 bits, 00b */
|
||||
/* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */
|
||||
/* Bit 14, Reserved, 2 bits, 00b */
|
||||
} __packed;
|
||||
|
||||
union oaktrail_panel_rx {
|
||||
struct {
|
||||
u16 NumberOfLanes:2; /*Num of Lanes, 2 bits,0 = 1 lane,*/
|
||||
/* 1 = 2 lanes, 2 = 3 lanes, 3 = 4 lanes. */
|
||||
u16 MaxLaneFreq:3; /* 0: 100MHz, 1: 200MHz, 2: 300MHz, */
|
||||
/*3: 400MHz, 4: 500MHz, 5: 600MHz, 6: 700MHz, 7: 800MHz.*/
|
||||
u16 SupportedVideoTransferMode:2; /*0: Non-burst only */
|
||||
/* 1: Burst and non-burst */
|
||||
/* 2/3: Reserved */
|
||||
u16 HSClkBehavior:1; /*0: Continuous, 1: Non-continuous*/
|
||||
u16 DuoDisplaySupport:1; /*1 bit,0: No, 1: Yes*/
|
||||
u16 ECC_ChecksumCapabilities:1;/*1 bit,0: No, 1: Yes*/
|
||||
u16 BidirectionalCommunication:1;/*1 bit,0: No, 1: Yes */
|
||||
u16 Rsvd:5;/*5 bits,00000b */
|
||||
} panelrx;
|
||||
u16 panel_receiver;
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_gct_v1 {
|
||||
union { /*8 bits,Defined as follows: */
|
||||
struct {
|
||||
u8 PanelType:4; /*4 bits, Bit field for panels*/
|
||||
/* 0 - 3: 0 = LVDS, 1 = MIPI*/
|
||||
/*2 bits,Specifies which of the*/
|
||||
u8 BootPanelIndex:2;
|
||||
/* 4 panels to use by default*/
|
||||
u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/
|
||||
/* the 4 MIPI DSI receivers to use*/
|
||||
} PD;
|
||||
u8 PanelDescriptor;
|
||||
};
|
||||
struct oaktrail_panel_descriptor_v1 panel[4];/*panel descrs,38 bytes each*/
|
||||
union oaktrail_panel_rx panelrx[4]; /* panel receivers*/
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_gct_v2 {
|
||||
union { /*8 bits,Defined as follows: */
|
||||
struct {
|
||||
u8 PanelType:4; /*4 bits, Bit field for panels*/
|
||||
/* 0 - 3: 0 = LVDS, 1 = MIPI*/
|
||||
/*2 bits,Specifies which of the*/
|
||||
u8 BootPanelIndex:2;
|
||||
/* 4 panels to use by default*/
|
||||
u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/
|
||||
/* the 4 MIPI DSI receivers to use*/
|
||||
} PD;
|
||||
u8 PanelDescriptor;
|
||||
};
|
||||
struct oaktrail_panel_descriptor_v2 panel[4];/*panel descrs,38 bytes each*/
|
||||
union oaktrail_panel_rx panelrx[4]; /* panel receivers*/
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_gct_data {
|
||||
u8 bpi; /* boot panel index, number of panel used during boot */
|
||||
u8 pt; /* panel type, 4 bit field, 0=lvds, 1=mipi */
|
||||
struct oaktrail_timing_info DTD; /* timing info for the selected panel */
|
||||
u32 Panel_Port_Control;
|
||||
u32 PP_On_Sequencing;/*1 dword,Register 0x61208,*/
|
||||
u32 PP_Off_Sequencing;/*1 dword,Register 0x6120C,*/
|
||||
u32 PP_Cycle_Delay;
|
||||
u16 Panel_Backlight_Inverter_Descriptor;
|
||||
u16 Panel_MIPI_Display_Descriptor;
|
||||
} __packed;
|
||||
|
||||
#define MODE_SETTING_IN_CRTC 0x1
|
||||
#define MODE_SETTING_IN_ENCODER 0x2
|
||||
#define MODE_SETTING_ON_GOING 0x3
|
||||
#define MODE_SETTING_IN_DSR 0x4
|
||||
#define MODE_SETTING_ENCODER_DONE 0x8
|
||||
|
||||
#define GCT_R10_HEADER_SIZE 16
|
||||
#define GCT_R10_DISPLAY_DESC_SIZE 28
|
||||
|
||||
/*
|
||||
* Moorestown HDMI interfaces
|
||||
*/
|
||||
|
||||
struct oaktrail_hdmi_dev {
|
||||
struct pci_dev *dev;
|
||||
void __iomem *regs;
|
||||
unsigned int mmio, mmio_len;
|
||||
int dpms_mode;
|
||||
struct hdmi_i2c_dev *i2c_dev;
|
||||
|
||||
/* register state */
|
||||
u32 saveDPLL_CTRL;
|
||||
u32 saveDPLL_DIV_CTRL;
|
||||
u32 saveDPLL_ADJUST;
|
||||
u32 saveDPLL_UPDATE;
|
||||
u32 saveDPLL_CLK_ENABLE;
|
||||
u32 savePCH_HTOTAL_B;
|
||||
u32 savePCH_HBLANK_B;
|
||||
u32 savePCH_HSYNC_B;
|
||||
u32 savePCH_VTOTAL_B;
|
||||
u32 savePCH_VBLANK_B;
|
||||
u32 savePCH_VSYNC_B;
|
||||
u32 savePCH_PIPEBCONF;
|
||||
u32 savePCH_PIPEBSRC;
|
||||
};
|
||||
|
||||
extern void oaktrail_hdmi_setup(struct drm_device *dev);
|
||||
extern void oaktrail_hdmi_teardown(struct drm_device *dev);
|
||||
extern int oaktrail_hdmi_i2c_init(struct pci_dev *dev);
|
||||
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);
|
610
drivers/gpu/drm/gma500/oaktrail_crtc.c
Normal file
610
drivers/gpu/drm/gma500/oaktrail_crtc.c
Normal file
@ -0,0 +1,610 @@
|
||||
/*
|
||||
* 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 "psb_intel_display.h"
|
||||
#include "power.h"
|
||||
|
||||
struct psb_intel_range_t {
|
||||
int min, max;
|
||||
};
|
||||
|
||||
struct oaktrail_limit_t {
|
||||
struct psb_intel_range_t dot, m, p1;
|
||||
};
|
||||
|
||||
struct oaktrail_clock_t {
|
||||
/* derived values */
|
||||
int dot;
|
||||
int m;
|
||||
int p1;
|
||||
};
|
||||
|
||||
#define MRST_LIMIT_LVDS_100L 0
|
||||
#define MRST_LIMIT_LVDS_83 1
|
||||
#define MRST_LIMIT_LVDS_100 2
|
||||
|
||||
#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 const struct oaktrail_limit_t oaktrail_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},
|
||||
},
|
||||
{ /* 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},
|
||||
},
|
||||
{ /* 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},
|
||||
},
|
||||
};
|
||||
|
||||
#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 oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc)
|
||||
{
|
||||
const struct oaktrail_limit_t *limit = NULL;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)
|
||||
|| psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) {
|
||||
switch (dev_priv->core_freq) {
|
||||
case 100:
|
||||
limit = &oaktrail_limits[MRST_LIMIT_LVDS_100L];
|
||||
break;
|
||||
case 166:
|
||||
limit = &oaktrail_limits[MRST_LIMIT_LVDS_83];
|
||||
break;
|
||||
case 200:
|
||||
limit = &oaktrail_limits[MRST_LIMIT_LVDS_100];
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
limit = NULL;
|
||||
dev_err(dev->dev, "oaktrail_limit Wrong display type.\n");
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
|
||||
static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock)
|
||||
{
|
||||
clock->dot = (refclk * clock->m) / (14 * clock->p1);
|
||||
}
|
||||
|
||||
void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock)
|
||||
{
|
||||
pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n",
|
||||
prefix, clock->dot, clock->m, clock->p1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
|
||||
struct oaktrail_clock_t *best_clock)
|
||||
{
|
||||
struct oaktrail_clock_t clock;
|
||||
const struct oaktrail_limit_t *limit = oaktrail_limit(crtc);
|
||||
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;
|
||||
|
||||
oaktrail_clock(refclk, &clock);
|
||||
|
||||
this_err = abs(clock.dot - target);
|
||||
if (this_err < err) {
|
||||
*best_clock = clock;
|
||||
err = this_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", 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 psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
|
||||
int pipe = psb_intel_crtc->pipe;
|
||||
int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B;
|
||||
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
|
||||
int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE;
|
||||
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
|
||||
u32 temp;
|
||||
bool enabled;
|
||||
|
||||
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:
|
||||
/* Enable the DPLL */
|
||||
temp = REG_READ(dpll_reg);
|
||||
if ((temp & DPLL_VCO_ENABLE) == 0) {
|
||||
REG_WRITE(dpll_reg, temp);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
}
|
||||
/* Enable the pipe */
|
||||
temp = REG_READ(pipeconf_reg);
|
||||
if ((temp & PIPEACONF_ENABLE) == 0)
|
||||
REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
|
||||
/* Enable the plane */
|
||||
temp = REG_READ(dspcntr_reg);
|
||||
if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
|
||||
REG_WRITE(dspcntr_reg,
|
||||
temp | DISPLAY_PLANE_ENABLE);
|
||||
/* Flush the plane changes */
|
||||
REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
|
||||
}
|
||||
|
||||
psb_intel_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 */
|
||||
|
||||
/* Disable the VGA plane that we never use */
|
||||
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
|
||||
/* Disable display plane */
|
||||
temp = REG_READ(dspcntr_reg);
|
||||
if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
|
||||
REG_WRITE(dspcntr_reg,
|
||||
temp & ~DISPLAY_PLANE_ENABLE);
|
||||
/* Flush the plane changes */
|
||||
REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
|
||||
REG_READ(dspbase_reg);
|
||||
}
|
||||
|
||||
/* Next, disable display pipes */
|
||||
temp = REG_READ(pipeconf_reg);
|
||||
if ((temp & PIPEACONF_ENABLE) != 0) {
|
||||
REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
|
||||
REG_READ(pipeconf_reg);
|
||||
}
|
||||
/* Wait for for the pipe disable to take effect. */
|
||||
psb_intel_wait_for_vblank(dev);
|
||||
|
||||
temp = REG_READ(dpll_reg);
|
||||
if ((temp & DPLL_VCO_ENABLE) != 0) {
|
||||
REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
}
|
||||
|
||||
/* Wait for the clocks to turn off. */
|
||||
udelay(150);
|
||||
break;
|
||||
}
|
||||
|
||||
enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
|
||||
|
||||
/*Set FIFO Watermarks*/
|
||||
REG_WRITE(DSPARB, 0x3FFF);
|
||||
REG_WRITE(DSPFW1, 0x3F88080A);
|
||||
REG_WRITE(DSPFW2, 0x0b060808);
|
||||
REG_WRITE(DSPFW3, 0x0);
|
||||
REG_WRITE(DSPFW4, 0x08030404);
|
||||
REG_WRITE(DSPFW5, 0x04040404);
|
||||
REG_WRITE(DSPFW6, 0x78);
|
||||
REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000);
|
||||
/* Must write Bit 14 of the Chicken Bit Register */
|
||||
|
||||
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 psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int pipe = psb_intel_crtc->pipe;
|
||||
int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0;
|
||||
int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B;
|
||||
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
|
||||
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
|
||||
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 pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
|
||||
int refclk = 0;
|
||||
struct oaktrail_clock_t clock;
|
||||
u32 dpll = 0, fp = 0, dspcntr, pipeconf;
|
||||
bool ok, is_sdvo = false;
|
||||
bool is_crt = false, is_lvds = false, is_tv = false;
|
||||
bool is_mipi = false;
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
struct psb_intel_output *psb_intel_output = NULL;
|
||||
uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return 0;
|
||||
|
||||
memcpy(&psb_intel_crtc->saved_mode,
|
||||
mode,
|
||||
sizeof(struct drm_display_mode));
|
||||
memcpy(&psb_intel_crtc->saved_adjusted_mode,
|
||||
adjusted_mode,
|
||||
sizeof(struct drm_display_mode));
|
||||
|
||||
list_for_each_entry(encoder, &mode_config->encoder_list, head) {
|
||||
|
||||
if (encoder->crtc != crtc)
|
||||
continue;
|
||||
|
||||
psb_intel_output = enc_to_psb_intel_output(encoder);
|
||||
switch (psb_intel_output->type) {
|
||||
case INTEL_OUTPUT_LVDS:
|
||||
is_lvds = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_SDVO:
|
||||
is_sdvo = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_TVOUT:
|
||||
is_tv = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_ANALOG:
|
||||
is_crt = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_MIPI:
|
||||
is_mipi = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable the VGA plane that we never use */
|
||||
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
|
||||
|
||||
/* Disable the panel fitter if it was on our pipe */
|
||||
if (oaktrail_panel_fitter_pipe(dev) == pipe)
|
||||
REG_WRITE(PFIT_CONTROL, 0);
|
||||
|
||||
REG_WRITE(pipesrc_reg,
|
||||
((mode->crtc_hdisplay - 1) << 16) |
|
||||
(mode->crtc_vdisplay - 1));
|
||||
|
||||
if (psb_intel_output)
|
||||
drm_connector_property_get_value(&psb_intel_output->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;
|
||||
|
||||
REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) |
|
||||
((adjusted_mode->crtc_htotal - 1) << 16));
|
||||
REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) |
|
||||
((adjusted_mode->crtc_vtotal - 1) << 16));
|
||||
REG_WRITE(hblank_reg,
|
||||
(adjusted_mode->crtc_hblank_start - offsetX - 1) |
|
||||
((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16));
|
||||
REG_WRITE(hsync_reg,
|
||||
(adjusted_mode->crtc_hsync_start - offsetX - 1) |
|
||||
((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16));
|
||||
REG_WRITE(vblank_reg,
|
||||
(adjusted_mode->crtc_vblank_start - offsetY - 1) |
|
||||
((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16));
|
||||
REG_WRITE(vsync_reg,
|
||||
(adjusted_mode->crtc_vsync_start - offsetY - 1) |
|
||||
((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16));
|
||||
} else {
|
||||
REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
|
||||
((adjusted_mode->crtc_htotal - 1) << 16));
|
||||
REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
|
||||
((adjusted_mode->crtc_vtotal - 1) << 16));
|
||||
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(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));
|
||||
}
|
||||
|
||||
/* 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(pipeconf_reg);
|
||||
|
||||
/* Set up the display plane register */
|
||||
dspcntr = REG_READ(dspcntr_reg);
|
||||
dspcntr |= DISPPLANE_GAMMA_ENABLE;
|
||||
|
||||
if (pipe == 0)
|
||||
dspcntr |= DISPPLANE_SEL_PIPE_A;
|
||||
else
|
||||
dspcntr |= DISPPLANE_SEL_PIPE_B;
|
||||
|
||||
dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE;
|
||||
dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE;
|
||||
|
||||
if (is_mipi)
|
||||
goto oaktrail_crtc_mode_set_exit;
|
||||
|
||||
refclk = dev_priv->core_freq * 1000;
|
||||
|
||||
dpll = 0; /*BIT16 = 0 for 100MHz reference */
|
||||
|
||||
ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock);
|
||||
|
||||
if (!ok) {
|
||||
dev_dbg(dev->dev, "mrstFindBestPLL fail in oaktrail_crtc_mode_set.\n");
|
||||
} else {
|
||||
dev_dbg(dev->dev, "oaktrail_crtc_mode_set pixel clock = %d,"
|
||||
"m = %x, p1 = %x.\n", clock.dot, clock.m,
|
||||
clock.p1);
|
||||
}
|
||||
|
||||
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 */
|
||||
dpll |= (1 << (clock.p1 - 2)) << 17;
|
||||
|
||||
dpll |= DPLL_VCO_ENABLE;
|
||||
|
||||
mrstPrintPll("chosen", &clock);
|
||||
|
||||
if (dpll & DPLL_VCO_ENABLE) {
|
||||
REG_WRITE(fp_reg, fp);
|
||||
REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
/* Check the DPLLA lock bit PIPEACONF[29] */
|
||||
udelay(150);
|
||||
}
|
||||
|
||||
REG_WRITE(fp_reg, fp);
|
||||
REG_WRITE(dpll_reg, dpll);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
|
||||
/* write it again -- the BIOS does, after all */
|
||||
REG_WRITE(dpll_reg, dpll);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
|
||||
REG_WRITE(pipeconf_reg, pipeconf);
|
||||
REG_READ(pipeconf_reg);
|
||||
psb_intel_wait_for_vblank(dev);
|
||||
|
||||
REG_WRITE(dspcntr_reg, dspcntr);
|
||||
psb_intel_wait_for_vblank(dev);
|
||||
|
||||
oaktrail_crtc_mode_set_exit:
|
||||
gma_power_end(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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_i915_master_private *master_priv; */
|
||||
struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
|
||||
struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
|
||||
int pipe = psb_intel_crtc->pipe;
|
||||
unsigned long start, offset;
|
||||
/* FIXME: check if we need this surely MRST is pipe 0 only */
|
||||
int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE);
|
||||
int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
|
||||
int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
|
||||
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
|
||||
u32 dspcntr;
|
||||
int ret = 0;
|
||||
|
||||
/* no fb bound */
|
||||
if (!crtc->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->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
|
||||
|
||||
REG_WRITE(dspstride, crtc->fb->pitch);
|
||||
|
||||
dspcntr = REG_READ(dspcntr_reg);
|
||||
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
|
||||
|
||||
switch (crtc->fb->bits_per_pixel) {
|
||||
case 8:
|
||||
dspcntr |= DISPPLANE_8BPP;
|
||||
break;
|
||||
case 16:
|
||||
if (crtc->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(dspcntr_reg, dspcntr);
|
||||
|
||||
if (0 /* FIXMEAC - check what PSB needs */) {
|
||||
REG_WRITE(dspbase, offset);
|
||||
REG_READ(dspbase);
|
||||
REG_WRITE(dspsurf, start);
|
||||
REG_READ(dspsurf);
|
||||
} else {
|
||||
REG_WRITE(dspbase, start + offset);
|
||||
REG_READ(dspbase);
|
||||
}
|
||||
|
||||
pipe_set_base_exit:
|
||||
gma_power_end(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void oaktrail_crtc_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static void oaktrail_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
const struct drm_crtc_helper_funcs oaktrail_helper_funcs = {
|
||||
.dpms = oaktrail_crtc_dpms,
|
||||
.mode_fixup = oaktrail_crtc_mode_fixup,
|
||||
.mode_set = oaktrail_crtc_mode_set,
|
||||
.mode_set_base = oaktrail_pipe_set_base,
|
||||
.prepare = oaktrail_crtc_prepare,
|
||||
.commit = oaktrail_crtc_commit,
|
||||
};
|
||||
|
489
drivers/gpu/drm/gma500/oaktrail_device.c
Normal file
489
drivers/gpu/drm/gma500/oaktrail_device.c
Normal file
@ -0,0 +1,489 @@
|
||||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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/backlight.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "psb_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include <asm/mrst.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include "mid_bios.h"
|
||||
|
||||
static int oaktrail_output_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide the low level interfaces for the Moorestown backlight
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
|
||||
#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 struct backlight_device *oaktrail_backlight_device;
|
||||
static int oaktrail_brightness;
|
||||
|
||||
static int oaktrail_set_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(oaktrail_backlight_device);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int level = bd->props.brightness;
|
||||
u32 blc_pwm_ctl;
|
||||
u32 max_pwm_blc;
|
||||
|
||||
/* Percentage 1-100% being valid */
|
||||
if (level < 1)
|
||||
level = 1;
|
||||
|
||||
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);
|
||||
}
|
||||
oaktrail_brightness = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oaktrail_get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
/* return locally cached var instead of HW read (due to DPST etc.) */
|
||||
/* FIXME: ideally return actual value in case firmware fiddled with
|
||||
it */
|
||||
return oaktrail_brightness;
|
||||
}
|
||||
|
||||
static int device_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops oaktrail_ops = {
|
||||
.get_brightness = oaktrail_get_brightness,
|
||||
.update_status = oaktrail_set_brightness,
|
||||
};
|
||||
|
||||
int oaktrail_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
struct backlight_properties props;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 100;
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
|
||||
oaktrail_backlight_device = backlight_device_register("oaktrail-bl",
|
||||
NULL, (void *)dev, &oaktrail_ops, &props);
|
||||
|
||||
if (IS_ERR(oaktrail_backlight_device))
|
||||
return PTR_ERR(oaktrail_backlight_device);
|
||||
|
||||
ret = device_backlight_init(dev);
|
||||
if (ret < 0) {
|
||||
backlight_device_unregister(oaktrail_backlight_device);
|
||||
return ret;
|
||||
}
|
||||
oaktrail_backlight_device->props.brightness = 100;
|
||||
oaktrail_backlight_device->props.max_brightness = 100;
|
||||
backlight_update_status(oaktrail_backlight_device);
|
||||
dev_priv->backlight_device = oaktrail_backlight_device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Provide the Moorestown specific chip logic and low level methods
|
||||
* for power management
|
||||
*/
|
||||
|
||||
static void oaktrail_init_pm(struct drm_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = dev->dev_private;
|
||||
int i;
|
||||
u32 pp_stat;
|
||||
|
||||
/* Display arbitration control + watermarks */
|
||||
dev_priv->saveDSPARB = PSB_RVDC32(DSPARB);
|
||||
dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1);
|
||||
dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2);
|
||||
dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3);
|
||||
dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4);
|
||||
dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5);
|
||||
dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6);
|
||||
dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
|
||||
|
||||
/* Pipe & plane A info */
|
||||
dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF);
|
||||
dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC);
|
||||
dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0);
|
||||
dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1);
|
||||
dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A);
|
||||
dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A);
|
||||
dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A);
|
||||
dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A);
|
||||
dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A);
|
||||
dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A);
|
||||
dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A);
|
||||
dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A);
|
||||
dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR);
|
||||
dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE);
|
||||
dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE);
|
||||
dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF);
|
||||
dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF);
|
||||
dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF);
|
||||
|
||||
/* Save cursor regs */
|
||||
dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR);
|
||||
dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE);
|
||||
dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS);
|
||||
|
||||
/* Save palette (gamma) */
|
||||
for (i = 0; i < 256; i++)
|
||||
dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2));
|
||||
|
||||
if (dev_priv->hdmi_priv)
|
||||
oaktrail_hdmi_save(dev);
|
||||
|
||||
/* Save performance state */
|
||||
dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE);
|
||||
|
||||
/* LVDS state */
|
||||
dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL);
|
||||
dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
|
||||
dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS);
|
||||
dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL);
|
||||
dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2);
|
||||
dev_priv->saveLVDS = PSB_RVDC32(LVDS);
|
||||
dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
|
||||
dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON);
|
||||
dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF);
|
||||
dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE);
|
||||
|
||||
/* HW overlay */
|
||||
dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD);
|
||||
dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0);
|
||||
dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1);
|
||||
dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2);
|
||||
dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3);
|
||||
dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4);
|
||||
dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5);
|
||||
|
||||
/* DPST registers */
|
||||
dev_priv->saveHISTOGRAM_INT_CONTROL_REG =
|
||||
PSB_RVDC32(HISTOGRAM_INT_CONTROL);
|
||||
dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG =
|
||||
PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL);
|
||||
dev_priv->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 = dev->dev_private;
|
||||
u32 pp_stat;
|
||||
int i;
|
||||
|
||||
/* Display arbitration + watermarks */
|
||||
PSB_WVDC32(dev_priv->saveDSPARB, DSPARB);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6);
|
||||
PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT);
|
||||
|
||||
/* Make sure VGA plane is off. it initializes to on after reset!*/
|
||||
PSB_WVDC32(0x80000000, VGACNTRL);
|
||||
|
||||
/* set the plls */
|
||||
PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0);
|
||||
PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1);
|
||||
|
||||
/* Actually enable it */
|
||||
PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A);
|
||||
DRM_UDELAY(150);
|
||||
|
||||
/* Restore mode */
|
||||
PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A);
|
||||
PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A);
|
||||
PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A);
|
||||
PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A);
|
||||
PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A);
|
||||
PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A);
|
||||
PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC);
|
||||
PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A);
|
||||
|
||||
/* Restore performance mode*/
|
||||
PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE);
|
||||
|
||||
/* Enable the pipe*/
|
||||
if (dev_priv->iLVDS_enable)
|
||||
PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF);
|
||||
|
||||
/* Set up the plane*/
|
||||
PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF);
|
||||
PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE);
|
||||
PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF);
|
||||
|
||||
/* Enable the plane */
|
||||
PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF);
|
||||
|
||||
/* Enable Cursor A */
|
||||
PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS);
|
||||
PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE);
|
||||
|
||||
/* Restore palette (gamma) */
|
||||
for (i = 0; i < 256; i++)
|
||||
PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2));
|
||||
|
||||
if (dev_priv->hdmi_priv)
|
||||
oaktrail_hdmi_restore(dev);
|
||||
|
||||
if (dev_priv->iLVDS_enable) {
|
||||
PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2);
|
||||
PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/
|
||||
PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL);
|
||||
PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
|
||||
PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS);
|
||||
PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL);
|
||||
PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON);
|
||||
PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF);
|
||||
PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE);
|
||||
PSB_WVDC32(dev_priv->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(dev_priv->saveOV_OVADD, OV_OVADD);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5);
|
||||
|
||||
/* DPST registers */
|
||||
PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG,
|
||||
HISTOGRAM_INT_CONTROL);
|
||||
PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG,
|
||||
HISTOGRAM_LOGIC_CONTROL);
|
||||
PSB_WVDC32(dev_priv->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 = dev->dev_private;
|
||||
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 = dev->dev_private;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static void oaktrail_teardown(struct drm_device *dev)
|
||||
{
|
||||
oaktrail_hdmi_teardown(dev);
|
||||
}
|
||||
|
||||
const struct psb_ops oaktrail_chip_ops = {
|
||||
.name = "Oaktrail",
|
||||
.accel_2d = 1,
|
||||
.pipes = 2,
|
||||
.crtcs = 2,
|
||||
.sgx_offset = MRST_SGX_OFFSET,
|
||||
|
||||
.chip_setup = mid_chip_setup,
|
||||
.chip_teardown = oaktrail_teardown,
|
||||
.crtc_helper = &oaktrail_helper_funcs,
|
||||
.crtc_funcs = &psb_intel_crtc_funcs,
|
||||
|
||||
.output_init = oaktrail_output_init,
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
.backlight_init = oaktrail_backlight_init,
|
||||
#endif
|
||||
|
||||
.init_pm = oaktrail_init_pm,
|
||||
.save_regs = oaktrail_save_display_registers,
|
||||
.restore_regs = oaktrail_restore_display_registers,
|
||||
.power_down = oaktrail_power_down,
|
||||
.power_up = oaktrail_power_up,
|
||||
|
||||
.i2c_bus = 1,
|
||||
};
|
852
drivers/gpu/drm/gma500/oaktrail_hdmi.c
Normal file
852
drivers/gpu/drm/gma500/oaktrail_hdmi.c
Normal file
@ -0,0 +1,852 @@
|
||||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Li Peng <peng.li@intel.com>
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "psb_drv.h"
|
||||
|
||||
#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg))
|
||||
#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg))
|
||||
|
||||
#define HDMI_HCR 0x1000
|
||||
#define HCR_ENABLE_HDCP (1 << 5)
|
||||
#define HCR_ENABLE_AUDIO (1 << 2)
|
||||
#define HCR_ENABLE_PIXEL (1 << 1)
|
||||
#define HCR_ENABLE_TMDS (1 << 0)
|
||||
|
||||
#define HDMI_HICR 0x1004
|
||||
#define HDMI_HSR 0x1008
|
||||
#define HDMI_HISR 0x100C
|
||||
#define HDMI_DETECT_HDP (1 << 0)
|
||||
|
||||
#define HDMI_VIDEO_REG 0x3000
|
||||
#define HDMI_UNIT_EN (1 << 7)
|
||||
#define HDMI_MODE_OUTPUT (1 << 0)
|
||||
#define HDMI_HBLANK_A 0x3100
|
||||
|
||||
#define HDMI_AUDIO_CTRL 0x4000
|
||||
#define HDMI_ENABLE_AUDIO (1 << 0)
|
||||
|
||||
#define PCH_HTOTAL_B 0x3100
|
||||
#define PCH_HBLANK_B 0x3104
|
||||
#define PCH_HSYNC_B 0x3108
|
||||
#define PCH_VTOTAL_B 0x310C
|
||||
#define PCH_VBLANK_B 0x3110
|
||||
#define PCH_VSYNC_B 0x3114
|
||||
#define PCH_PIPEBSRC 0x311C
|
||||
|
||||
#define PCH_PIPEB_DSL 0x3800
|
||||
#define PCH_PIPEB_SLC 0x3804
|
||||
#define PCH_PIPEBCONF 0x3808
|
||||
#define PCH_PIPEBSTAT 0x3824
|
||||
|
||||
#define CDVO_DFT 0x5000
|
||||
#define CDVO_SLEWRATE 0x5004
|
||||
#define CDVO_STRENGTH 0x5008
|
||||
#define CDVO_RCOMP 0x500C
|
||||
|
||||
#define DPLL_CTRL 0x6000
|
||||
#define DPLL_PDIV_SHIFT 16
|
||||
#define DPLL_PDIV_MASK (0xf << 16)
|
||||
#define DPLL_PWRDN (1 << 4)
|
||||
#define DPLL_RESET (1 << 3)
|
||||
#define DPLL_FASTEN (1 << 2)
|
||||
#define DPLL_ENSTAT (1 << 1)
|
||||
#define DPLL_DITHEN (1 << 0)
|
||||
|
||||
#define DPLL_DIV_CTRL 0x6004
|
||||
#define DPLL_CLKF_MASK 0xffffffc0
|
||||
#define DPLL_CLKR_MASK (0x3f)
|
||||
|
||||
#define DPLL_CLK_ENABLE 0x6008
|
||||
#define DPLL_EN_DISP (1 << 31)
|
||||
#define DPLL_SEL_HDMI (1 << 8)
|
||||
#define DPLL_EN_HDMI (1 << 1)
|
||||
#define DPLL_EN_VGA (1 << 0)
|
||||
|
||||
#define DPLL_ADJUST 0x600C
|
||||
#define DPLL_STATUS 0x6010
|
||||
#define DPLL_UPDATE 0x6014
|
||||
#define DPLL_DFT 0x6020
|
||||
|
||||
struct intel_range {
|
||||
int min, max;
|
||||
};
|
||||
|
||||
struct oaktrail_hdmi_limit {
|
||||
struct intel_range vco, np, nr, nf;
|
||||
};
|
||||
|
||||
struct oaktrail_hdmi_clock {
|
||||
int np;
|
||||
int nr;
|
||||
int nf;
|
||||
int dot;
|
||||
};
|
||||
|
||||
#define VCO_MIN 320000
|
||||
#define VCO_MAX 1650000
|
||||
#define NP_MIN 1
|
||||
#define NP_MAX 15
|
||||
#define NR_MIN 1
|
||||
#define NR_MAX 64
|
||||
#define NF_MIN 2
|
||||
#define NF_MAX 4095
|
||||
|
||||
static const struct oaktrail_hdmi_limit oaktrail_hdmi_limit = {
|
||||
.vco = { .min = VCO_MIN, .max = VCO_MAX },
|
||||
.np = { .min = NP_MIN, .max = NP_MAX },
|
||||
.nr = { .min = NR_MIN, .max = NR_MAX },
|
||||
.nf = { .min = NF_MIN, .max = NF_MAX },
|
||||
};
|
||||
|
||||
static void wait_for_vblank(struct drm_device *dev)
|
||||
{
|
||||
/* FIXME: Can we do this as a sleep ? */
|
||||
/* Wait for 20ms, i.e. one cycle at 50hz. */
|
||||
mdelay(20);
|
||||
}
|
||||
|
||||
static void scu_busy_loop(void *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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_reset(struct drm_device *dev)
|
||||
{
|
||||
void *base;
|
||||
/* FIXME: at least make these defines */
|
||||
unsigned int scu_ipc_mmio = 0xff11c000;
|
||||
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);
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_audio_enable(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
|
||||
HDMI_WRITE(HDMI_HCR, 0x67);
|
||||
HDMI_READ(HDMI_HCR);
|
||||
|
||||
HDMI_WRITE(0x51a8, 0x10);
|
||||
HDMI_READ(0x51a8);
|
||||
|
||||
HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1);
|
||||
HDMI_READ(HDMI_AUDIO_CTRL);
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_audio_disable(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
|
||||
HDMI_WRITE(0x51a8, 0x0);
|
||||
HDMI_READ(0x51a8);
|
||||
|
||||
HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0);
|
||||
HDMI_READ(HDMI_AUDIO_CTRL);
|
||||
|
||||
HDMI_WRITE(HDMI_HCR, 0x47);
|
||||
HDMI_READ(HDMI_HCR);
|
||||
}
|
||||
|
||||
void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
u32 temp;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
/* Disable VGACNTRL */
|
||||
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 */
|
||||
REG_WRITE(0x70400, 0x4000);
|
||||
}
|
||||
|
||||
|
||||
static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
static int dpms_mode = -1;
|
||||
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
u32 temp;
|
||||
|
||||
if (dpms_mode == mode)
|
||||
return;
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
temp = 0x0;
|
||||
else
|
||||
temp = 0x99;
|
||||
|
||||
dpms_mode = mode;
|
||||
HDMI_WRITE(HDMI_VIDEO_REG, temp);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Disable the VGA plane that we never use */
|
||||
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
|
||||
|
||||
/* XXX: Disable the panel fitter if it was on our pipe */
|
||||
|
||||
/* 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: FIXME - can we sort out the ioremap mess ? */
|
||||
iounmap(hdmi_dev->regs);
|
||||
oaktrail_hdmi_reset(dev);
|
||||
|
||||
/* program and enable dpll */
|
||||
refclk = 25000;
|
||||
oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock);
|
||||
|
||||
/* Setting 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);
|
||||
|
||||
hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
|
||||
if (hdmi_dev->regs == NULL) {
|
||||
DRM_ERROR("failed to do hdmi mmio mapping\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oaktrail_hdmi_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > 165000)
|
||||
return MODE_CLOCK_HIGH;
|
||||
if (mode->clock < 20000)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static bool oaktrail_hdmi_mode_fixup(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
oaktrail_hdmi_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
enum drm_connector_status status;
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
u32 temp;
|
||||
|
||||
temp = HDMI_READ(HDMI_HSR);
|
||||
DRM_DEBUG_KMS("HDMI_HSR %x\n", temp);
|
||||
|
||||
if ((temp & HDMI_DETECT_HDP) != 0)
|
||||
status = connector_status_connected;
|
||||
else
|
||||
status = connector_status_disconnected;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const unsigned char raw_edid[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0,
|
||||
0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78,
|
||||
0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5,
|
||||
0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0,
|
||||
0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a,
|
||||
0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35,
|
||||
0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44,
|
||||
0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20,
|
||||
0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
i2c_adap = i2c_get_adapter(3);
|
||||
if (i2c_adap == NULL) {
|
||||
DRM_ERROR("No ddc adapter available!\n");
|
||||
edid = (struct edid *)raw_edid;
|
||||
} else {
|
||||
edid = (struct edid *)raw_edid;
|
||||
/* FIXME ? edid = drm_get_edid(connector, i2c_adap); */
|
||||
}
|
||||
|
||||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
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;
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
|
||||
oaktrail_hdmi_audio_enable(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_destroy(struct drm_connector *connector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = {
|
||||
.dpms = oaktrail_hdmi_dpms,
|
||||
.mode_fixup = oaktrail_hdmi_mode_fixup,
|
||||
.prepare = psb_intel_encoder_prepare,
|
||||
.mode_set = oaktrail_hdmi_mode_set,
|
||||
.commit = psb_intel_encoder_commit,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
oaktrail_hdmi_connector_helper_funcs = {
|
||||
.get_modes = oaktrail_hdmi_get_modes,
|
||||
.mode_valid = oaktrail_hdmi_mode_valid,
|
||||
.best_encoder = psb_intel_best_encoder,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs oaktrail_hdmi_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = oaktrail_hdmi_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = oaktrail_hdmi_destroy,
|
||||
};
|
||||
|
||||
static void oaktrail_hdmi_enc_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs oaktrail_hdmi_enc_funcs = {
|
||||
.destroy = oaktrail_hdmi_enc_destroy,
|
||||
};
|
||||
|
||||
void oaktrail_hdmi_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
struct psb_intel_output *psb_intel_output;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL);
|
||||
if (!psb_intel_output)
|
||||
return;
|
||||
|
||||
psb_intel_output->mode_dev = mode_dev;
|
||||
connector = &psb_intel_output->base;
|
||||
encoder = &psb_intel_output->enc;
|
||||
drm_connector_init(dev, &psb_intel_output->base,
|
||||
&oaktrail_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DVID);
|
||||
|
||||
drm_encoder_init(dev, &psb_intel_output->enc,
|
||||
&oaktrail_hdmi_enc_funcs,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
|
||||
drm_mode_connector_attach_encoder(&psb_intel_output->base,
|
||||
&psb_intel_output->enc);
|
||||
|
||||
psb_intel_output->type = INTEL_OUTPUT_HDMI;
|
||||
drm_encoder_helper_add(encoder, &oaktrail_hdmi_helper_funcs);
|
||||
drm_connector_helper_add(connector, &oaktrail_hdmi_connector_helper_funcs);
|
||||
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
drm_sysfs_connector_add(connector);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) },
|
||||
{}
|
||||
};
|
||||
|
||||
void oaktrail_hdmi_setup(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct pci_dev *pdev;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev;
|
||||
int ret;
|
||||
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL);
|
||||
if (!pdev)
|
||||
return;
|
||||
|
||||
hdmi_dev = kzalloc(sizeof(struct oaktrail_hdmi_dev), GFP_KERNEL);
|
||||
if (!hdmi_dev) {
|
||||
dev_err(dev->dev, "failed to allocate memory\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable hdmi controller\n");
|
||||
goto free;
|
||||
}
|
||||
|
||||
hdmi_dev->mmio = pci_resource_start(pdev, 0);
|
||||
hdmi_dev->mmio_len = pci_resource_len(pdev, 0);
|
||||
hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
|
||||
if (!hdmi_dev->regs) {
|
||||
dev_err(dev->dev, "failed to map hdmi mmio\n");
|
||||
goto free;
|
||||
}
|
||||
|
||||
hdmi_dev->dev = pdev;
|
||||
pci_set_drvdata(pdev, hdmi_dev);
|
||||
|
||||
/* Initialize i2c controller */
|
||||
ret = oaktrail_hdmi_i2c_init(hdmi_dev->dev);
|
||||
if (ret)
|
||||
dev_err(dev->dev, "HDMI I2C initialization failed\n");
|
||||
|
||||
dev_priv->hdmi_priv = hdmi_dev;
|
||||
oaktrail_hdmi_audio_disable(dev);
|
||||
return;
|
||||
|
||||
free:
|
||||
kfree(hdmi_dev);
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
void oaktrail_hdmi_teardown(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if (hdmi_dev) {
|
||||
pdev = hdmi_dev->dev;
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
oaktrail_hdmi_i2c_exit(pdev);
|
||||
iounmap(hdmi_dev->regs);
|
||||
kfree(hdmi_dev);
|
||||
pci_dev_put(pdev);
|
||||
}
|
||||
}
|
||||
|
||||
/* save HDMI register state */
|
||||
void oaktrail_hdmi_save(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
int i;
|
||||
|
||||
/* dpll */
|
||||
hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL);
|
||||
hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL);
|
||||
hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST);
|
||||
hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE);
|
||||
hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE);
|
||||
|
||||
/* pipe B */
|
||||
dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF);
|
||||
dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC);
|
||||
dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B);
|
||||
dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B);
|
||||
dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B);
|
||||
dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B);
|
||||
dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B);
|
||||
dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B);
|
||||
|
||||
hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF);
|
||||
hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC);
|
||||
hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B);
|
||||
hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B);
|
||||
hdmi_dev->savePCH_HSYNC_B = PSB_RVDC32(PCH_HSYNC_B);
|
||||
hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B);
|
||||
hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B);
|
||||
hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B);
|
||||
|
||||
/* plane */
|
||||
dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR);
|
||||
dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE);
|
||||
dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE);
|
||||
dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF);
|
||||
dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF);
|
||||
dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF);
|
||||
|
||||
/* cursor B */
|
||||
dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR);
|
||||
dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE);
|
||||
dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS);
|
||||
|
||||
/* save palette */
|
||||
for (i = 0; i < 256; i++)
|
||||
dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2));
|
||||
}
|
||||
|
||||
/* restore HDMI register state */
|
||||
void oaktrail_hdmi_restore(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
int i;
|
||||
|
||||
/* dpll */
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE);
|
||||
DRM_UDELAY(150);
|
||||
|
||||
/* pipe */
|
||||
PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC);
|
||||
PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B);
|
||||
PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B);
|
||||
PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B);
|
||||
PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B);
|
||||
PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B);
|
||||
PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B);
|
||||
|
||||
PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B, PCH_HSYNC_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B);
|
||||
|
||||
PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF);
|
||||
|
||||
/* plane */
|
||||
PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF);
|
||||
PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE);
|
||||
PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF);
|
||||
PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF);
|
||||
|
||||
/* cursor B */
|
||||
PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS);
|
||||
PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE);
|
||||
|
||||
/* restore palette */
|
||||
for (i = 0; i < 256; i++)
|
||||
PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2));
|
||||
}
|
327
drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
Normal file
327
drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Li Peng <peng.li@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include "psb_drv.h"
|
||||
|
||||
#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg))
|
||||
#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg))
|
||||
|
||||
#define HDMI_HCR 0x1000
|
||||
#define HCR_DETECT_HDP (1 << 6)
|
||||
#define HCR_ENABLE_HDCP (1 << 5)
|
||||
#define HCR_ENABLE_AUDIO (1 << 2)
|
||||
#define HCR_ENABLE_PIXEL (1 << 1)
|
||||
#define HCR_ENABLE_TMDS (1 << 0)
|
||||
#define HDMI_HICR 0x1004
|
||||
#define HDMI_INTR_I2C_ERROR (1 << 4)
|
||||
#define HDMI_INTR_I2C_FULL (1 << 3)
|
||||
#define HDMI_INTR_I2C_DONE (1 << 2)
|
||||
#define HDMI_INTR_HPD (1 << 0)
|
||||
#define HDMI_HSR 0x1008
|
||||
#define HDMI_HISR 0x100C
|
||||
#define HDMI_HI2CRDB0 0x1200
|
||||
#define HDMI_HI2CHCR 0x1240
|
||||
#define HI2C_HDCP_WRITE (0 << 2)
|
||||
#define HI2C_HDCP_RI_READ (1 << 2)
|
||||
#define HI2C_HDCP_READ (2 << 2)
|
||||
#define HI2C_EDID_READ (3 << 2)
|
||||
#define HI2C_READ_CONTINUE (1 << 1)
|
||||
#define HI2C_ENABLE_TRANSACTION (1 << 0)
|
||||
|
||||
#define HDMI_ICRH 0x1100
|
||||
#define HDMI_HI2CTDR0 0x1244
|
||||
#define HDMI_HI2CTDR1 0x1248
|
||||
|
||||
#define I2C_STAT_INIT 0
|
||||
#define I2C_READ_DONE 1
|
||||
#define I2C_TRANSACTION_DONE 2
|
||||
|
||||
struct hdmi_i2c_dev {
|
||||
struct i2c_adapter *adap;
|
||||
struct mutex i2c_lock;
|
||||
struct completion complete;
|
||||
int status;
|
||||
struct i2c_msg *msg;
|
||||
int buf_offset;
|
||||
};
|
||||
|
||||
static void hdmi_i2c_irq_enable(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = HDMI_READ(HDMI_HICR);
|
||||
temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE);
|
||||
HDMI_WRITE(HDMI_HICR, temp);
|
||||
HDMI_READ(HDMI_HICR);
|
||||
}
|
||||
|
||||
static void hdmi_i2c_irq_disable(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
HDMI_WRITE(HDMI_HICR, 0x0);
|
||||
HDMI_READ(HDMI_HICR);
|
||||
}
|
||||
|
||||
static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
u32 temp;
|
||||
|
||||
i2c_dev->status = I2C_STAT_INIT;
|
||||
i2c_dev->msg = pmsg;
|
||||
i2c_dev->buf_offset = 0;
|
||||
INIT_COMPLETION(i2c_dev->complete);
|
||||
|
||||
/* Enable I2C transaction */
|
||||
temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION;
|
||||
HDMI_WRITE(HDMI_HI2CHCR, temp);
|
||||
HDMI_READ(HDMI_HI2CHCR);
|
||||
|
||||
while (i2c_dev->status != I2C_TRANSACTION_DONE)
|
||||
wait_for_completion_interruptible_timeout(&i2c_dev->complete,
|
||||
10 * HZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg)
|
||||
{
|
||||
/*
|
||||
* XXX: i2c write seems isn't useful for EDID probe, don't do anything
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
|
||||
struct i2c_msg *pmsg,
|
||||
int num)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
int i, err = 0;
|
||||
|
||||
mutex_lock(&i2c_dev->i2c_lock);
|
||||
|
||||
/* Enable i2c unit */
|
||||
HDMI_WRITE(HDMI_ICRH, 0x00008760);
|
||||
|
||||
/* Enable irq */
|
||||
hdmi_i2c_irq_enable(hdmi_dev);
|
||||
for (i = 0; i < num; i++) {
|
||||
if (pmsg->len && pmsg->buf) {
|
||||
if (pmsg->flags & I2C_M_RD)
|
||||
err = xfer_read(adap, pmsg);
|
||||
else
|
||||
err = xfer_write(adap, pmsg);
|
||||
}
|
||||
pmsg++; /* next message */
|
||||
}
|
||||
|
||||
/* Disable irq */
|
||||
hdmi_i2c_irq_disable(hdmi_dev);
|
||||
|
||||
mutex_unlock(&i2c_dev->i2c_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static u32 oaktrail_hdmi_i2c_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm oaktrail_hdmi_i2c_algorithm = {
|
||||
.master_xfer = oaktrail_hdmi_i2c_access,
|
||||
.functionality = oaktrail_hdmi_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter oaktrail_hdmi_i2c_adapter = {
|
||||
.name = "oaktrail_hdmi_i2c",
|
||||
.nr = 3,
|
||||
.owner = THIS_MODULE,
|
||||
.class = I2C_CLASS_DDC,
|
||||
.algo = &oaktrail_hdmi_i2c_algorithm,
|
||||
};
|
||||
|
||||
static void hdmi_i2c_read(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
struct i2c_msg *msg = i2c_dev->msg;
|
||||
u8 *buf = msg->buf;
|
||||
u32 temp;
|
||||
int i, offset;
|
||||
|
||||
offset = i2c_dev->buf_offset;
|
||||
for (i = 0; i < 0x10; i++) {
|
||||
temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4));
|
||||
memcpy(buf + (offset + i * 4), &temp, 4);
|
||||
}
|
||||
i2c_dev->buf_offset += (0x10 * 4);
|
||||
|
||||
/* clearing read buffer full intr */
|
||||
temp = HDMI_READ(HDMI_HISR);
|
||||
HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL);
|
||||
HDMI_READ(HDMI_HISR);
|
||||
|
||||
/* continue read transaction */
|
||||
temp = HDMI_READ(HDMI_HI2CHCR);
|
||||
HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE);
|
||||
HDMI_READ(HDMI_HI2CHCR);
|
||||
|
||||
i2c_dev->status = I2C_READ_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
static void hdmi_i2c_transaction_done(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
u32 temp;
|
||||
|
||||
/* clear transaction done intr */
|
||||
temp = HDMI_READ(HDMI_HISR);
|
||||
HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE);
|
||||
HDMI_READ(HDMI_HISR);
|
||||
|
||||
|
||||
temp = HDMI_READ(HDMI_HI2CHCR);
|
||||
HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION);
|
||||
HDMI_READ(HDMI_HI2CHCR);
|
||||
|
||||
i2c_dev->status = I2C_TRANSACTION_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
static irqreturn_t oaktrail_hdmi_i2c_handler(int this_irq, void *dev)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev;
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
u32 stat;
|
||||
|
||||
stat = HDMI_READ(HDMI_HISR);
|
||||
|
||||
if (stat & HDMI_INTR_HPD) {
|
||||
HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD);
|
||||
HDMI_READ(HDMI_HISR);
|
||||
}
|
||||
|
||||
if (stat & HDMI_INTR_I2C_FULL)
|
||||
hdmi_i2c_read(hdmi_dev);
|
||||
|
||||
if (stat & HDMI_INTR_I2C_DONE)
|
||||
hdmi_i2c_transaction_done(hdmi_dev);
|
||||
|
||||
complete(&i2c_dev->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* choose alternate function 2 of GPIO pin 52, 53,
|
||||
* which is used by HDMI I2C logic
|
||||
*/
|
||||
static void oaktrail_hdmi_i2c_gpio_fix(void)
|
||||
{
|
||||
void *base;
|
||||
unsigned int gpio_base = 0xff12c000;
|
||||
int gpio_len = 0x1000;
|
||||
u32 temp;
|
||||
|
||||
base = ioremap((resource_size_t)gpio_base, gpio_len);
|
||||
if (base == NULL) {
|
||||
DRM_ERROR("gpio ioremap fail\n");
|
||||
return;
|
||||
}
|
||||
|
||||
temp = readl(base + 0x44);
|
||||
DRM_DEBUG_DRIVER("old gpio val %x\n", temp);
|
||||
writel((temp | 0x00000a00), (base + 0x44));
|
||||
temp = readl(base + 0x44);
|
||||
DRM_DEBUG_DRIVER("new gpio val %x\n", temp);
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
int oaktrail_hdmi_i2c_init(struct pci_dev *dev)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev;
|
||||
struct hdmi_i2c_dev *i2c_dev;
|
||||
int ret;
|
||||
|
||||
hdmi_dev = pci_get_drvdata(dev);
|
||||
|
||||
i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL);
|
||||
if (i2c_dev == NULL) {
|
||||
DRM_ERROR("Can't allocate interface\n");
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_dev->adap = &oaktrail_hdmi_i2c_adapter;
|
||||
i2c_dev->status = I2C_STAT_INIT;
|
||||
init_completion(&i2c_dev->complete);
|
||||
mutex_init(&i2c_dev->i2c_lock);
|
||||
i2c_set_adapdata(&oaktrail_hdmi_i2c_adapter, hdmi_dev);
|
||||
hdmi_dev->i2c_dev = i2c_dev;
|
||||
|
||||
/* Enable HDMI I2C function on gpio */
|
||||
oaktrail_hdmi_i2c_gpio_fix();
|
||||
|
||||
/* request irq */
|
||||
ret = request_irq(dev->irq, oaktrail_hdmi_i2c_handler, IRQF_SHARED,
|
||||
oaktrail_hdmi_i2c_adapter.name, hdmi_dev);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to request IRQ for I2C controller\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Adapter registration */
|
||||
ret = i2c_add_numbered_adapter(&oaktrail_hdmi_i2c_adapter);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
kfree(i2c_dev);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void oaktrail_hdmi_i2c_exit(struct pci_dev *dev)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev;
|
||||
struct hdmi_i2c_dev *i2c_dev;
|
||||
|
||||
hdmi_dev = pci_get_drvdata(dev);
|
||||
if (i2c_del_adapter(&oaktrail_hdmi_i2c_adapter))
|
||||
DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n");
|
||||
|
||||
i2c_dev = hdmi_dev->i2c_dev;
|
||||
kfree(i2c_dev);
|
||||
free_irq(dev->irq, hdmi_dev);
|
||||
}
|
406
drivers/gpu/drm/gma500/oaktrail_lvds.c
Normal file
406
drivers/gpu/drm/gma500/oaktrail_lvds.c
Normal file
@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright © 2006-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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Dave Airlie <airlied@linux.ie>
|
||||
* Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <asm/mrst.h>
|
||||
|
||||
#include "intel_bios.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "power.h"
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/* The max/min PWM frequency in BPCR[31:17] - */
|
||||
/* The smallest number is 1 (not 0) that can fit in the
|
||||
* 15-bit field of the and then*/
|
||||
/* shifts to the left by one bit to get the actual 16-bit
|
||||
* value that the 15-bits correspond to.*/
|
||||
#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF
|
||||
#define BRIGHTNESS_MAX_LEVEL 100
|
||||
|
||||
/**
|
||||
* Sets the power state for the panel.
|
||||
*/
|
||||
static void oaktrail_lvds_set_power(struct drm_device *dev,
|
||||
struct psb_intel_output *output, bool on)
|
||||
{
|
||||
u32 pp_status;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
|
||||
POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while ((pp_status & (PP_ON | PP_READY)) == PP_READY);
|
||||
dev_priv->is_lvds_on = true;
|
||||
if (dev_priv->ops->lvds_bl_power)
|
||||
dev_priv->ops->lvds_bl_power(dev, true);
|
||||
} else {
|
||||
if (dev_priv->ops->lvds_bl_power)
|
||||
dev_priv->ops->lvds_bl_power(dev, false);
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
|
||||
~POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while (pp_status & PP_ON);
|
||||
dev_priv->is_lvds_on = false;
|
||||
pm_request_idle(&dev->pdev->dev);
|
||||
}
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct psb_intel_output *output = enc_to_psb_intel_output(encoder);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
oaktrail_lvds_set_power(dev, output, true);
|
||||
else
|
||||
oaktrail_lvds_set_power(dev, output, false);
|
||||
|
||||
/* XXX: We never power down the LVDS pairs. */
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct psb_intel_mode_device *mode_dev =
|
||||
enc_to_psb_intel_output(encoder)->mode_dev;
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 lvds_port;
|
||||
uint64_t v = DRM_MODE_SCALE_FULLSCREEN;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
/*
|
||||
* The LVDS pin pair will already have been turned on in the
|
||||
* psb_intel_crtc_mode_set since it has a large impact on the DPLL
|
||||
* settings.
|
||||
*/
|
||||
lvds_port = (REG_READ(LVDS) &
|
||||
(~LVDS_PIPEB_SELECT)) |
|
||||
LVDS_PORT_EN |
|
||||
LVDS_BORDER_EN;
|
||||
|
||||
/* If the firmware says dither on Moorestown, or the BIOS does
|
||||
on Oaktrail then enable dithering */
|
||||
if (mode_dev->panel_wants_dither || dev_priv->lvds_dither)
|
||||
lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE;
|
||||
|
||||
REG_WRITE(LVDS, lvds_port);
|
||||
|
||||
drm_connector_property_get_value(
|
||||
&enc_to_psb_intel_output(encoder)->base,
|
||||
dev->mode_config.scaling_mode_property,
|
||||
&v);
|
||||
|
||||
if (v == DRM_MODE_SCALE_NO_SCALE)
|
||||
REG_WRITE(PFIT_CONTROL, 0);
|
||||
else if (v == DRM_MODE_SCALE_ASPECT) {
|
||||
if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) ||
|
||||
(mode->hdisplay != adjusted_mode->crtc_hdisplay)) {
|
||||
if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) ==
|
||||
(mode->hdisplay * adjusted_mode->crtc_vdisplay))
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
|
||||
else if ((adjusted_mode->crtc_hdisplay *
|
||||
mode->vdisplay) > (mode->hdisplay *
|
||||
adjusted_mode->crtc_vdisplay))
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
|
||||
PFIT_SCALING_MODE_PILLARBOX);
|
||||
else
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
|
||||
PFIT_SCALING_MODE_LETTERBOX);
|
||||
} else
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
|
||||
} else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct psb_intel_output *output = enc_to_psb_intel_output(encoder);
|
||||
struct psb_intel_mode_device *mode_dev = output->mode_dev;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
|
||||
mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_DUTY_CYCLE_MASK);
|
||||
oaktrail_lvds_set_power(dev, output, false);
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 ret;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
ret = ((REG_READ(BLC_PWM_CTL) &
|
||||
BACKLIGHT_MODULATION_FREQ_MASK) >>
|
||||
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
|
||||
|
||||
gma_power_end(dev);
|
||||
} else
|
||||
ret = ((dev_priv->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_MODULATION_FREQ_MASK) >>
|
||||
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct psb_intel_output *output = enc_to_psb_intel_output(encoder);
|
||||
struct psb_intel_mode_device *mode_dev = output->mode_dev;
|
||||
|
||||
if (mode_dev->backlight_duty_cycle == 0)
|
||||
mode_dev->backlight_duty_cycle =
|
||||
oaktrail_lvds_get_max_backlight(dev);
|
||||
oaktrail_lvds_set_power(dev, output, true);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = {
|
||||
.dpms = oaktrail_lvds_dpms,
|
||||
.mode_fixup = psb_intel_lvds_mode_fixup,
|
||||
.prepare = oaktrail_lvds_prepare,
|
||||
.mode_set = oaktrail_lvds_mode_set,
|
||||
.commit = oaktrail_lvds_commit,
|
||||
};
|
||||
|
||||
static struct drm_display_mode lvds_configuration_modes[] = {
|
||||
/* hard coded fixed mode for TPO LTPS LPJ040K001A */
|
||||
{ DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836,
|
||||
846, 1056, 0, 480, 489, 491, 525, 0, 0) },
|
||||
/* hard coded fixed mode for LVDS 800x480 */
|
||||
{ DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801,
|
||||
802, 1024, 0, 480, 481, 482, 525, 0, 0) },
|
||||
/* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
|
||||
{ DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072,
|
||||
1104, 1184, 0, 600, 603, 604, 608, 0, 0) },
|
||||
/* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
|
||||
{ DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104,
|
||||
1136, 1184, 0, 600, 603, 604, 608, 0, 0) },
|
||||
/* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */
|
||||
{ DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124,
|
||||
1204, 1312, 0, 600, 607, 610, 621, 0, 0) },
|
||||
/* hard coded fixed mode for LVDS 1024x768 */
|
||||
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
|
||||
1184, 1344, 0, 768, 771, 777, 806, 0, 0) },
|
||||
/* hard coded fixed mode for LVDS 1366x768 */
|
||||
{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430,
|
||||
1558, 1664, 0, 768, 769, 770, 776, 0, 0) },
|
||||
};
|
||||
|
||||
/* Returns the panel fixed mode from configuration. */
|
||||
|
||||
static struct drm_display_mode *
|
||||
oaktrail_lvds_get_configuration_mode(struct drm_device *dev)
|
||||
{
|
||||
struct drm_display_mode *mode = NULL;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
|
||||
|
||||
if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/
|
||||
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
|
||||
if (!mode)
|
||||
return NULL;
|
||||
|
||||
mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
|
||||
mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
|
||||
mode->hsync_start = mode->hdisplay + \
|
||||
((ti->hsync_offset_hi << 8) | \
|
||||
ti->hsync_offset_lo);
|
||||
mode->hsync_end = mode->hsync_start + \
|
||||
((ti->hsync_pulse_width_hi << 8) | \
|
||||
ti->hsync_pulse_width_lo);
|
||||
mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \
|
||||
ti->hblank_lo);
|
||||
mode->vsync_start = \
|
||||
mode->vdisplay + ((ti->vsync_offset_hi << 4) | \
|
||||
ti->vsync_offset_lo);
|
||||
mode->vsync_end = \
|
||||
mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \
|
||||
ti->vsync_pulse_width_lo);
|
||||
mode->vtotal = mode->vdisplay + \
|
||||
((ti->vblank_hi << 8) | ti->vblank_lo);
|
||||
mode->clock = ti->pixel_clock * 10;
|
||||
#if 0
|
||||
printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay);
|
||||
printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay);
|
||||
printk(KERN_INFO "HSS is %d\n", mode->hsync_start);
|
||||
printk(KERN_INFO "HSE is %d\n", mode->hsync_end);
|
||||
printk(KERN_INFO "htotal is %d\n", mode->htotal);
|
||||
printk(KERN_INFO "VSS is %d\n", mode->vsync_start);
|
||||
printk(KERN_INFO "VSE is %d\n", mode->vsync_end);
|
||||
printk(KERN_INFO "vtotal is %d\n", mode->vtotal);
|
||||
printk(KERN_INFO "clock is %d\n", mode->clock);
|
||||
#endif
|
||||
} else
|
||||
mode = drm_mode_duplicate(dev, &lvds_configuration_modes[2]);
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
drm_mode_set_crtcinfo(mode, 0);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* oaktrail_lvds_init - setup LVDS connectors on this device
|
||||
* @dev: drm device
|
||||
*
|
||||
* Create the connector, register the LVDS DDC bus, and try to figure out what
|
||||
* modes we can display on the LVDS panel (if present).
|
||||
*/
|
||||
void oaktrail_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
struct psb_intel_output *psb_intel_output;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
struct edid *edid;
|
||||
int ret = 0;
|
||||
struct i2c_adapter *i2c_adap;
|
||||
struct drm_display_mode *scan; /* *modes, *bios_mode; */
|
||||
|
||||
psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL);
|
||||
if (!psb_intel_output)
|
||||
return;
|
||||
|
||||
psb_intel_output->mode_dev = mode_dev;
|
||||
connector = &psb_intel_output->base;
|
||||
encoder = &psb_intel_output->enc;
|
||||
dev_priv->is_lvds_on = true;
|
||||
drm_connector_init(dev, &psb_intel_output->base,
|
||||
&psb_intel_lvds_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
|
||||
drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs,
|
||||
DRM_MODE_ENCODER_LVDS);
|
||||
|
||||
drm_mode_connector_attach_encoder(&psb_intel_output->base,
|
||||
&psb_intel_output->enc);
|
||||
psb_intel_output->type = INTEL_OUTPUT_LVDS;
|
||||
|
||||
drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs);
|
||||
drm_connector_helper_add(connector,
|
||||
&psb_intel_lvds_connector_helper_funcs);
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
|
||||
drm_connector_attach_property(connector,
|
||||
dev->mode_config.scaling_mode_property,
|
||||
DRM_MODE_SCALE_FULLSCREEN);
|
||||
drm_connector_attach_property(connector,
|
||||
dev_priv->backlight_property,
|
||||
BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
mode_dev->panel_wants_dither = false;
|
||||
if (dev_priv->vbt_data.size != 0x00)
|
||||
mode_dev->panel_wants_dither = (dev_priv->gct_data.
|
||||
Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE);
|
||||
|
||||
/*
|
||||
* LVDS discovery:
|
||||
* 1) check for EDID on DDC
|
||||
* 2) check for VBT data
|
||||
* 3) check to see if LVDS is already on
|
||||
* if none of the above, no panel
|
||||
* 4) make sure lid is open
|
||||
* if closed, act like it's not there for now
|
||||
*/
|
||||
|
||||
i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
|
||||
if (i2c_adap == NULL)
|
||||
dev_err(dev->dev, "No ddc adapter available!\n");
|
||||
/*
|
||||
* Attempt to get the fixed panel mode from DDC. Assume that the
|
||||
* preferred mode is the right one.
|
||||
*/
|
||||
if (i2c_adap) {
|
||||
edid = drm_get_edid(connector, i2c_adap);
|
||||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector,
|
||||
edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
list_for_each_entry(scan, &connector->probed_modes, head) {
|
||||
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, scan);
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we didn't get EDID, try geting panel timing
|
||||
* from configuration data
|
||||
*/
|
||||
mode_dev->panel_fixed_mode = oaktrail_lvds_get_configuration_mode(dev);
|
||||
|
||||
if (mode_dev->panel_fixed_mode) {
|
||||
mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
|
||||
/* If we still don't have a mode after all that, give up. */
|
||||
if (!mode_dev->panel_fixed_mode) {
|
||||
dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n");
|
||||
goto failed_find;
|
||||
}
|
||||
|
||||
out:
|
||||
drm_sysfs_connector_add(connector);
|
||||
return;
|
||||
|
||||
failed_find:
|
||||
dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
|
||||
if (psb_intel_output->ddc_bus)
|
||||
psb_intel_i2c_destroy(psb_intel_output->ddc_bus);
|
||||
|
||||
/* failed_ddc: */
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user