You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -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);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user